I started to use CakePHP (1.2) a few months ago to add small features to the company's application and I'm not too familiar with it.
We test locally then on a development server before merging to a production server.
I want a controller action to be called every hour with what I assumed to be the best way to do this through my researches, a cron job.
Attempt 1
After reading these,
http://bakery.cakephp.org/articles/mathew_attlee/2006/12/05/calling-controller-actions-from-cron-and-the-command-line
http://book.cakephp.org/1.2/en/view/110/Creating-Shells-Tasks
I could implement something without errors, but the action is not executed.
Based on these examples, I added a file named cron_dispatcher.php in my app directory (not app/webroot) and then did this command from the app dir
php cron_dispatcher.php /controller/action/param
Still nothing happened but it works perfect when I call it through the url.
Attempt 2
I tried creating a shell (email.php) that would call the action in /app/vendors/shells/.
<?php
class EmailShell extends Shell {
public function main() {
$this->out('Test');
}
}
?>
This successfully outputs Test in the console using
cake email main
but then I cannot find how to call the controller's action. I have tried
$this->requestAction('/controller/action');
I have also tried to make the call from a different function than the main in the shell.
I have tried to include the controller in the $uses variable as I would with a model but that didn't work (and it doesn't make sense I think)
I don't think creating a task is the solution either as I don't want to duplicate the sendEmails function hence why I'm looking for a way to just call the controller's action from a shell or whatever!
There is probably some theory I'm missing, thanks
Solution
I moved some methods from the controller to a model and I was able to call them from a shell.
App::import('Component', 'Email');
class SendMemosShell extends Shell {
var $uses = array(
'Memo',
);
public function main() {
}
public function sendEmails () {
$this->Email =& new EmailComponent(null);
$memoList = $this->Memo->getMemos();
//...
}
}
This link helped http://book.cakephp.org/2.0/en/console-and-shells/cron-jobs.html
edit : clarified some of the information and added the solution
It is a quite common issue actually, ran into it also.
A controller is deciding how to handle a request and starting that task. In this case there is no need for a controller since you have a shell task, the task is already clear.
Knowing that, it does not make sense to call a controller method.
So rethink your options, and yes this is a quite difficult one. For example you might decide that sending the e-mail is a business logic step so it should be in the model. Another option is to separate it totally (that's what we like most).
In that case you will have to create a queue where you put in all e-mails to send. That is a good design since you then know the amount of logic in the controller goes down and it is separated. That way you get an e-mail service.
For example you could ask the service to send a "new user" mail. Then you add the User object to it and it should handle itself. That way you can even scale since your service could be for example outsourced, you could expand multiple servers on the service etc.
Edit:
Good questions.
Steps to take:
Centralize the "sending e-mail" process first. So choose one location where to put it. The you can decide: Add to send e-mail to a queue or call the service directly. For example you could add shell task for sending the e-mails.
Call the shell: Now you have the problem to call the shell. In general you don't want to. Why not? Because a shell (a task) could run for a long time. So that's why we use queues in between. So you can ask the queue or let the queue message you that something is done. For example think about a mail server which is down. You have to retry etc. That should not be in a web request because the user is waiting for response.
Third step is to call the shell from your cron, now that's easy since you are already on the command line so you could use standard calls.
Anyhow, there are options to do a direct call from a controller but you should not. This post gives some very interesting insights: CakePHP: Run shell job from controller
Edit 31/08/'13: See the events system of CakePHP also for some examples: http://book.cakephp.org/2.0/en/core-libraries/events.html
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With