Question
I want to know if it is possible to asynchronously invoke a Yii controller method from one of its actions while the action renders a view, leaving the method to complete a long running operation. I would love to do something like the code below and I don't need to return a result from my_long_running_func
.
public function actionCreate() {
$model = new Vacancies;
if (isset($_POST['Vacancies'])) {
$model->setAttributes($_POST['Vacancies']);
$model->save();
//I wish :)
call_user_func_async('my_long_running_func',$model);
}
$this->render('create', array( 'model' => $model));
}
Problem
I am trying to write a controller action in Yii that posts a vacancy and notifies interested subscribers of the post. The problem is that it takes a long time to execute the notification query.
Now I am searching for a way to asynchronously run the query so the poster sees his response in as little time as possible while the query runs in the background in a way similar to C# delegates or events.
The solutions I googled up performed asynchronous request(s) during the course of the controller action but all I want to do is to run a method of the controller asynchronously and the action had to wait till the request(s) were completed.
Attempted
I have tried the following methods but the query is still slow for my test data of about 1500 users.
Yii ActiveRecord
if ($vacancy->save()) { if($vacancy->is_active == 1) { $url = Yii::app()->createUrl('vacancies/view',array('id'=>$model->id)); $trainees = YumUser::getUsersByRole('Trainees'); if($trainees!=null) { foreach($trainees as $trainee){ $message = new YumMessage; $message->from_user_id = Yii::app()->user->id; $message->title = 'Vacancy Notification: '.date('M j, Y'); $message->message = "A new vacancy has been posted at <a href='{$url}'>{$url}</a>."; $message->to_user_id = $trainee->id; $message->save(); } } } }
Yii Data Access Objects
if ($vacancy->save()) { if($vacancy->is_active == 1) { $url = Yii::app()->createAbsoluteUrl('vacancies/view',array('id'=>$model->id)); $trainee_ids=Yii::app()->db->createCommand()->select('user_id')->from('trainee')->queryColumn(); $fid=Yii::app()->user->id; $msg="A new vacancy has been posted at <a href='{$url}'>{$url}</a>."; $ts = time(); $tt = 'Vacancy Notification: '.date('M j, Y'); if($trainee_ids!=null) { foreach($trainee_ids as $trainee_id){ Yii::app()->db->createCommand() ->insert('message',array('timestamp'=>$ts,'from_user_id'=>$fid,'to_user_id'=>$tid,'title'=>$tt,'message'=>$msg)); } } } }
Prepared Statements
if ($vacancy->save()) { if($vacancy->is_active == 1) { $url = Yii::app()->createUrl('vacancies/view',array('id'=>$model->id)); $trainee_ids=Yii::app()->db->createCommand()->select('user_id')->from('trainee')->queryColumn(); $fu=Yii::app()->user->id; $msg="A new vacancy has been posted at <a href='{$url}'>{$url}</a>."; $ts = time(); $tt = 'Vacancy Notification: '.date('M j, Y'); $sql="INSERT INTO message (timestamp,from_user_id,title,message,to_user_id) VALUES (:ts,:fu,:tt,:msg,:tu)"; if($trainee_ids!=null) { foreach($trainee_ids as $trainee_id){ $command=Yii::app()->db->createCommand($sql); $command->bindParam(":ts",$ts,PDO::PARAM_INT); $command->bindParam(":fu",$fu,PDO::PARAM_INT); $command->bindParam(":tt",$tt,PDO::PARAM_STR); $command->bindParam(":msg",$msg,PDO::PARAM_STR); $command->bindParam(":tu",$trainee_id,PDO::PARAM_INT); $command->execute(); } } } }
Research
I have also checked the following websites (I'm only allowed to post two links) but they either require the action to wait for the request to be completed or need curl (which I don't have access to on the deployment server) or need an external library. I was hoping for a native PHP implementation.
- PHP Simulated Multi-Threading
- Multithreading in php
- Asynchronous PHP calls?
- Asynchronous processing in PHP
Edit
I was able to decrease response time considerably by rewriting my query in this way (moving the user loop to the database layer):
public function actionCreate() {
$user=YumUser::model()->findByPk(Yii::app()->user->id);
$model = new Vacancies;
$model->corporate_id=$user->professional->institution->corporate->id;
$model->date_posted=date('Y-m-d');
$model->last_modified=date('Y-m-d H:i:s');
if (isset($_POST['Vacancies'])) {
$model->setAttributes($_POST['Vacancies']);
if ($model->save()) {
if($model->is_active == 1) {
$url = Yii::app()->createAbsoluteUrl('vacancies/view',array('id'=>$model->id));
$fu=Yii::app()->user->id;
$msg="A new vacancy has been posted at <a href='{$url}'>{$url}</a>.";
$ts = time();
$tt = 'New Vacancy: '.$model->title;
$sql='INSERT INTO message (timestamp,from_user_id,title,message,to_user_id) SELECT :ts,:fu,:tt,:msg,t.user_id FROM trainee t';
Yii::app()->db->createCommand($sql)->execute(array(':ts'=>$ts,':fu'=>$fu,':tt'=>$tt,':msg'=>$msg));
}
if (Yii::app()->getRequest()->getIsAjaxRequest())
Yii::app()->end();
else
$this->redirect(array('view', 'id' => $model->id));
}
}
$this->render('create', array( 'model' => $model));
}
Notwithstanding, it would be nice if someone could post a way to call functions asynchronously.