Raising events and send emails to the user when new post

Dear all,

In my web application, users can choose to subscribe a group, then if there are new topics in this group posted, the user will be notified via email. (Just like watch topic in yii’s forum).

I did this by adding send mail actions in the actionPost, something like this:


if($model->save())

$assign=xxxxxxxxxx...........//Use mysql queries to find who has subscribed this group.

if($assign){

     foreach($assign as $ass){      //slow...??

               .....

               .....

                Yii::app()->mail->send($message);  //send emails

               }


         }

				$this->redirect(array('view','id'=>$model->id));

		}

However, I found this method MAY slow the post speed down…

For instance, if there are 1000 subscribers, the user need to wait relatively a long time until the server send all the emails before it redirects to the view page…(Is that true…? any ideas?).

So I change my idea and decide to use Event to send emails…

I know i need to something like this:


if($model->save())

$assign=xxxxxxxxxx...........//Use mysql queries to find who has subscribed this group.

 if($assign){

       ///////////////RAISE EVENT HERE ///////////////// 

				$this->redirect(array('view','id'=>$model->id));

		}

But what about then?? Where should I put the handler… and how should I write the handler…? Any Hints will be very very helpful…

Thanks so much!!!

I really appreciate this!!!!!

Any ideas…? :lol: :rolleyes:

It won’t make any difference in terms of speed whether you start the mail-sending process directly in the controller or decouple it and start it in an event handler. This is because events won’t be processed in parallel. All attached handlers will be called like functions one after another. After all handlers have finished, your application resumes with normal workflow (the redirect in your scenario).

To speed up the mail-sending process, you could try to prepare one single mail and add all recipients to the mails BCC field and then send only this one mail instead of 1000 identical mails.

Thanks ! That is very helpful…Are you familiar with the extension Yii Mail? I don’t know how to add all recipients in the mailing list at once… That’s why I use the stupid for each…

I need to find the user from the database first…then in my case, the array $assign would be my users…Is there a way to send emails to them at once? (add them to recipients at once…)

:D

thanks!

what i would do:

  1. sending the email offline: meaning the post action does not send email at all but save the email info into a queue;

  2. create a batch command to process the queue and send emails, then schedule this job for 1 minutes or so;

by doing this, posting will never slow down due to the action of sending emails; seconds or minutes delay in sending email does not make different to email recipients;

Yeah that is really a good idea…Do you have any examples or something like a tutorial…? I am not very familiar with this…

Also, do you have any clues about how to send emails to multiple recipients at once?

Thanks so much anyway!!! :D You guys are so nice!

i don’t have example code handy, but you can check here;

for sending multiple emails in one shot, you could follow @ben’s suggestion add all recipients in bcc;

Sorry, I have no experience with this extension. However, since it basically wraps swift mailer, you can read through swift mailer’s documentation. Here is a sample how to set BCC-recipients: http://swiftmailer.org/docs/messages.html#setting-bcc-recipients

About reading users from DB: There are basically two ways which I would think about.

  1. Directly reading required data:

Use Yii’s CDbConnection to create and configure a CDbCommand, which will return the recepients email addresses:




$addresses = Yii::app()->db->createCommand()

    ->selectDistinct( /* email field */ )

    ->from( /* users table */ )

    ->join( /* groups table, groups/ users link table */ )

    ->where( /* groupId matches */ )

    ->queryColumn();


$message = new YiiMailMessage;

$message->setBcc( $addresses );


Yii::app()->mail->send( $message );



This can be done quickly, is straight forward, everyone who reads your code can figure out what’s going on.

  1. Create a scope:

Use Yii’s named scopes feature to easily access users that subscribed to a given group:




// in your user model

public function subscribedTo( $groupId )

{

    $this->getDbCriteria()->mergeWith(array(

        ->join( /* groups table, groups/ users link table */ )

        ->where( /* groupId matches */ )

    ));

    return $this;

}


// in your controller

if ($model->save())

{

    $users = User::model()->subscribedTo($model->group_id)->findAll();

    $addresses = array();

    foreach ($users as $user)

        $addresses[] = $user->email;


    $message = new YiiMailMessage;

    $message->setBcc( $addresses );


    Yii::app()->mail->send( $message );

}



Since this approach will load User objects instead of only the required email addresses, it will consume more memory. Also, it requires the loop to transform the array of user objects into an array of email addresses. And finally, it requires a bit more knowledge about the yii framework.

On the other hand, it gives you more flexibility. Maybe one day you want to display a list of users who subscribed to a group. Then you can simply re-use the scope, access the users names and are done with that new task.

It looks like I have to setTo(’’) first, because it is required by swift mailer…

I can’t just use Bcc(not allowed) or just use setTo(everyone will see each others’ addresses)…

swift mailer batch sending

Though queue approach described by rootbear is a better one for high loads.

I also searched for ‘queue’ in extensions. Seems like this one may help or give some ideas to you.