<p>If you have questions or other issues feel free to ask.<br>
Please don't be confused, my Plugins handle/name is "Test" because I only use it for these purposes.. So whenever you see the name "Test" you know you have to rename it</p>

<p>This is your plugins init function</p>

<pre><code>/**
 * Called after the plugin class is instantiated; do any one-time initialization here such as hooks and events:
 * @return mixed
 */
public function init()
{
    parent::init();
    craft()-&gt;on('entries.onBeforeSaveEntry', function(Event $event)
    {
        // trigger only when entries are changed not when created
        if(!$event-&gt;params['isNewEntry']){
            /** @var EntryModel $entry */
            $entry = $event-&gt;params['entry'];
            // create your logic to check if it is the correct entry...
            // you should check for your section here otherwise you'll send a mail for all updates^^
            // I just used the handle pages in my test system
            if($entry-&gt;getSection()-&gt;handle === "pages"){
                // next: fetch all user related to the entry...
                // since I don't know your entire fields, you have to implement the logic
                // to fetch users from entries that are related to the entry yourself
                // if you need help just ask
                $criteria = craft()-&gt;elements-&gt;getCriteria(ElementType::User);
                $criteria-&gt;relatedTo($entry);

                /** @var UserModel $user */
                foreach( $criteria as $user){
                    // create a new entry in the database
                    $email = new Test_EmailRecord();
                    $email-&gt;entryId = $entry-&gt;id;
                    $email-&gt;email = $user-&gt;email;
                    $email-&gt;save();
                }
            }
        }

    });
}
</code></pre>

<p>And you have to create the following record</p>

<pre><code>namespace Craft;


class Test_EmailRecord extends BaseRecord
{
    public function getTableName()
    {
        return 'cron_mails';
    }

    protected function defineAttributes()
    {
        return array(
            // you could store the user Id here too, but this way
            // you can send the mail to non users too
            'email' =&gt; AttributeType::String,
            'entryId' =&gt; array(AttributeType::Number),
        );
    }

    // you could - if you want - create relations here..
    // https://craftcms.com/docs/plugins/records#defining-relations
    // but since it's only an example and not needed I skipped that part
}
</code></pre>

<p>And you need this in your controller</p>

<pre><code> /**
 * Cronjob to send emails... you can activate it every X hours
 * or once per day or whatever you want
 * when this function is trigger a specific number of mails will be send
 * their entries in the database will be deleted until there are no rows in the
 * table
 */
public function actionSendMails()
{
    // fetch mails with limit.. insert your limit here.. I would suggest to fetch 20-50
    // entries per trigger depending on how often it should run
    $emails = Test_EmailRecord::model()-&gt;findAllBySql('select * from craft_cron_mails LIMIT 1');

    if (is_array($emails) &amp;&amp; count($emails)) {
        foreach ($emails as $email) {
            /** @var UserModel $user */
            $user = craft()-&gt;users-&gt;getUserByEmail($email-&gt;email);

            // do your logic if no user is found..
            // of course you could store the user Id rather than the email in the database table
            // but this way you can send the mail to non users too if you like
            if (!$user) {
                // you can just remove that part
                // I just wanted to show you could create an array with defaults here
                // if the user is not registered
                $user = array(/* fill some placeholders for your twig template */);
            }

            // prepare templates, you can address each user personally with a hello Mr {{ user.name }}
            // the entry {{ entry.title }} was edited and such... 
            $message = craft()-&gt;templates-&gt;render('path/to/template.twig', array(
                'user' =&gt; $user,
                'entry' =&gt; craft()-&gt;elements-&gt;getElementById($email-&gt;entryId)
            ));
            if ($this-&gt;sendMail($message, "The post was edited", $user-&gt;email)) {
                //remove the column if the mail was send
                $email-&gt;delete();
            }

        }
    }
}

/**
 * This function sends the mail, it usually belongs to a service
 * but to make it easier I'll just leave it here
 *
 * @param $message
 * @param $subject
 * @param null $mail
 * @return bool
 */
public function sendMail($message, $subject, $mail = null)
{
    $errors = false;
    $adminEmail = craft()-&gt;systemSettings-&gt;getSetting('email', 'emailAddress');

    $email = new EmailModel();

    $email-&gt;fromEmail = $adminEmail;
    $email-&gt;replyTo = $adminEmail;
    $email-&gt;sender = $adminEmail;
    $email-&gt;fromName = craft()-&gt;getSiteName();
    $email-&gt;toEmail = $mail;
    $email-&gt;subject = $subject;
    $email-&gt;body = $message;

    if (!craft()-&gt;email-&gt;sendEmail($email)) {
        $errors = true;
    }

    return $errors;
}
</code></pre>

CC BY-SA 3.0
2017-10-22T15:16:28.293Z
Wow, this looks super! Going to take me a short while to digest this and test it. Your help is very much appreciated! I’ll respond once I’ve had a play! Thanks
shorn
CC BY-SA 3.0
2017-10-26T19:18:46.997Z
Just looking at tackling this now and I think there might be a better way to do this. As I mentioned, this is for a basic commenting system. One way I was thinking of doing it would be to get get a notification only if someone replies directly to you, rather than setting up a users field. So, the user woudl hit reply under another comment and I can pass that users id to the new comment that gets posted and send an email to that user. That way, only 1 additional email is getting generated. Would save setting up and record and cron job. Make sense?
shorn
CC BY-SA 3.0
2017-10-26T19:25:59.780Z
It's totally up to you. If you think it might be enough to only notify one user you can do that as well. Some systems do it this way, some system inform everyone. If you only want to inform one user you can just render the email message and send it after your `saveEntry` from before is successfull
Robin Schambach
CC BY-SA 3.0
2017-10-26T19:29:51.633Z
Yea, I think this might be best. I've been in some threads where I get notified of every message and soon wish I hadn't :)
shorn
CC BY-SA 3.0
2017-10-26T19:32:25.710Z
Okay, then you can at least use the Sendmail function. Glad I could help you anyway.
Robin Schambach
CC BY-SA 3.0
2017-10-26T19:37:48.453Z
Will do, I already have some of the email stuff setup and working so it will be a case of extending that. Going to mark the answer as solved anyway as it was a reasonable solution. You help has been much appreciated! Many thanks,
shorn
CC BY-SA 3.0
2017-10-22T15:10:57.100Z
2017-10-22T16:13:30.747Z
2017-10-22T16:13:30.747Z
Robin Schambach
<p>I have a channel set up with front end submitted entries. I want a way for users to be able to subscribe themselves to receive an update whenever the entry (or it's related entries) are updated. I was thinking one way to so this so that it can be managed on the front end would be to add a Users field to the entry and let logged in users add themselves to it.</p>

<p>I have a simple notification service that I am using in a custom plugin that is sending emails to entry authors, but I'm not sure how, or what would be the best way to send emails to multiple users as per the above requirement.</p>

<p>(p.s I'm new and limited in my plugin knowledge!)</p>

<p>Any thoughts or suggestions. </p>

<p>Thanks!</p>

CC BY-SA 3.0
2017-10-22T13:40:45.760Z
There is an easy and a "more complex" way to achieve this, it depends on how many people will subscribe in one entry. You could just hook yourself on the `entry.onUpdate` method and send a mail to all users. That would be only a few lines (I'll write them) but if you think there could be 20+ people in one entry with frequent updates you have to collect all mails in a table and create a cron job to send them. It's easy but requires much more code (when you use craft 2,with craft 3 it's less).
Robin Schambach
CC BY-SA 3.0
2017-10-22T13:56:07.493Z
Ah ok. That makes sense. I think the entries are quite likely to have 20+ users. That was my concern in whether that field starts getting large. It’s essentially a comments section that’s set up, so the notifications would be for when people have commented.
shorn
CC BY-SA 3.0
2017-10-22T14:06:18.340Z
It's not about a field getting large, you can relate as many people as you want with an entry. There is no restriction in the database since every user has its own row in the relations table. It's more about the performance. You can't send all mails when one user submits something because this one user has to wait for all mails to be send until he gets a response. So if there are 100+ people the request could be rejected due to server time restrictions. Do you use a custom controller/service to save entries? I'll provide a solution after your answer
Robin Schambach
CC BY-SA 3.0
2017-10-22T14:52:44.920Z
Indeed, that’s what I meant, not so much the amount of users, but the effect that has on running something like notification mails. No custom controller/service. Just standard entries.
shorn
<plugin-development><emails>
Plugin sending emails to all users in an entries Users field
# Plugin sending emails to all users in an entries Users field

(Asked by: username_0 on 2017-10-22)

I have a channel set up with front end submitted entries. I want a way for users to be able to subscribe themselves to receive an update whenever the entry (or it's related entries) are updated. I was thinking one way to so this so that it can be managed on the front end would be to add a Users field to the entry and let logged in users add themselves to it.

I have a simple notification service that I am using in a custom plugin that is sending emails to entry authors, but I'm not sure how, or what would be the best way to send emails to multiple users as per the above requirement.

(p.s I'm new and limited in my plugin knowledge!)

Any thoughts or suggestions.

Thanks!

**Tags:** plugin-development, emails
> **Comments:**
> **username_1**: There is an easy and a "more complex" way to achieve this, it depends on how many people will subscribe in one entry. You could just hook yourself on the `entry.onUpdate` method and send a mail to all users. That would be only a few lines (I'll write them) but if you think there could be 20+ people in one entry with frequent updates you have to collect all mails in a table and create a cron job to send them. It's easy but requires much more code (when you use craft 2,with craft 3 it's less).  
> **username_0**: Ah ok. That makes sense. I think the entries are quite likely to have 20+ users. That was my concern in whether that field starts getting large. It’s essentially a comments section that’s set up, so the notifications would be for when people have commented.  
> **username_1**: It's not about a field getting large, you can relate as many people as you want with an entry. There is no restriction in the database since every user has its own row in the relations table. It's more about the performance. You can't send all mails when one user submits something because this one user has to wait for all mails to be send until he gets a response. So if there are 100+ people the request could be rejected due to server time restrictions. Do you use a custom controller/service to save entries? I'll provide a solution after your answer  
> **username_0**: Indeed, that’s what I meant, not so much the amount of users, but the effect that has on running something like notification mails. No custom controller/service. Just standard entries.  

---

## 1 answers

### Answer by username_1 on 2017-10-22. Score: 3 | Accepted answer

If you have questions or other issues feel free to ask.
Please don't be confused, my Plugins handle/name is "Test" because I only use it for these purposes.. So whenever you see the name "Test" you know you have to rename it
This is your plugins init function
/**
 * Called after the plugin class is instantiated; do any one-time initialization here such as hooks and events:
 * @return mixed
 */
public function init()
{
    parent::init();
    craft()->on('entries.onBeforeSaveEntry', function(Event $event)
    {
        // trigger only when entries are changed not when created
        if(!$event->params['isNewEntry']){
            /** @var EntryModel $entry */
            $entry = $event->params['entry'];
            // create your logic to check if it is the correct entry...
            // you should check for your section here otherwise you'll send a mail for all updates^^
            // I just used the handle pages in my test system
            if($entry->getSection()->handle === "pages"){
                // next: fetch all user related to the entry...
                // since I don't know your entire fields, you have to implement the logic
                // to fetch users from entries that are related to the entry yourself
                // if you need help just ask
                $criteria = craft()->elements->getCriteria(ElementType::User);
                $criteria->relatedTo($entry);

                /** @var UserModel $user */
                foreach( $criteria as $user){
                    // create a new entry in the database
                    $email = new Test_EmailRecord();
                    $email->entryId = $entry->id;
                    $email->email = $user->email;
                    $email->save();
                }
            }
        }

    });
}

And you have to create the following record
namespace Craft;


class Test_EmailRecord extends BaseRecord
{
    public function getTableName()
    {
        return 'cron_mails';
    }

    protected function defineAttributes()
    {
        return array(
            // you could store the user Id here too, but this way
            // you can send the mail to non users too
            'email' => AttributeType::String,
            'entryId' => array(AttributeType::Number),
        );
    }

    // you could - if you want - create relations here..
    // https://craftcms.com/docs/plugins/records#defining-relations
    // but since it's only an example and not needed I skipped that part
}

And you need this in your controller
 /**
 * Cronjob to send emails... you can activate it every X hours
 * or once per day or whatever you want
 * when this function is trigger a specific number of mails will be send
 * their entries in the database will be deleted until there are no rows in the
 * table
 */
public function actionSendMails()
{
    // fetch mails with limit.. insert your limit here.. I would suggest to fetch 20-50
    // entries per trigger depending on how often it should run
    $emails = Test_EmailRecord::model()->findAllBySql('select * from craft_cron_mails LIMIT 1');

    if (is_array($emails) && count($emails)) {
        foreach ($emails as $email) {
            /** @var UserModel $user */
            $user = craft()->users->getUserByEmail($email->email);

            // do your logic if no user is found..
            // of course you could store the user Id rather than the email in the database table
            // but this way you can send the mail to non users too if you like
            if (!$user) {
                // you can just remove that part
                // I just wanted to show you could create an array with defaults here
                // if the user is not registered
                $user = array(/* fill some placeholders for your twig template */);
            }

            // prepare templates, you can address each user personally with a hello Mr {{ user.name }}
            // the entry {{ entry.title }} was edited and such... 
            $message = craft()->templates->render('path/to/template.twig', array(
                'user' => $user,
                'entry' => craft()->elements->getElementById($email->entryId)
            ));
            if ($this->sendMail($message, "The post was edited", $user->email)) {
                //remove the column if the mail was send
                $email->delete();
            }

        }
    }
}

/**
 * This function sends the mail, it usually belongs to a service
 * but to make it easier I'll just leave it here
 *
 * @param $message
 * @param $subject
 * @param null $mail
 * @return bool
 */
public function sendMail($message, $subject, $mail = null)
{
    $errors = false;
    $adminEmail = craft()->systemSettings->getSetting('email', 'emailAddress');

    $email = new EmailModel();

    $email->fromEmail = $adminEmail;
    $email->replyTo = $adminEmail;
    $email->sender = $adminEmail;
    $email->fromName = craft()->getSiteName();
    $email->toEmail = $mail;
    $email->subject = $subject;
    $email->body = $message;

    if (!craft()->email->sendEmail($email)) {
        $errors = true;
    }

    return $errors;
}

> **Comments:**
> **username_0**: Wow, this looks super! Going to take me a short while to digest this and test it. Your help is very much appreciated! I’ll respond once I’ve had a play! Thanks  
> **username_0**: Just looking at tackling this now and I think there might be a better way to do this. As I mentioned, this is for a basic commenting system. One way I was thinking of doing it would be to get get a notification only if someone replies directly to you, rather than setting up a users field. So, the user woudl hit reply under another comment and I can pass that users id to the new comment that gets posted and send an email to that user. That way, only 1 additional email is getting generated. Would save setting up and record and cron job. Make sense?  
> **username_1**: It's totally up to you. If you think it might be enough to only notify one user you can do that as well. Some systems do it this way, some system inform everyone. If you only want to inform one user you can just render the email message and send it after your `saveEntry` from before is successfull  
> **username_0**: Yea, I think this might be best. I've been in some threads where I get notified of every message and soon wish I hadn't :)  
> **username_1**: Okay, then you can at least use the Sendmail function. Glad I could help you anyway.  
> **username_0**: Will do, I already have some of the email stuff setup and working so it will be a case of extending that. Going to mark the answer as solved anyway as it was a reasonable solution. You help has been much appreciated! Many thanks,  

---

