Symfony2: Call a Controller /File on EVERY action - php

I'm doing a huge project with Symfony2. Frontend is javascript/html5 canvas. For site changes I use ajax requests.
Now I want to call a php file which should be executed with EVERY user action. Is there an elegant way to do so?
For better understanding: I'm doing some kind of game and the php checks, if something happend (recruitments done, buildings finished etc.).

Take a look into JMSAopBundle

If by user action you mean executing a controller action (i.e. server side), what I would do is listen to the kernel.controller event: http://symfony.com/doc/current/book/internals.html
so you can load your script and execute it juste before the target controller is invoked
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
public function onKernelController(FilterControllerEvent $event)
{
$controller = $event->getController();
//...
// call your php file here
// the controller can be changed to any PHP callable
$event->setController($controller);
}
I don't think you even need to modify the controller so you can remove the first and last line...

Related

Redirect from service in Symfony

I have a service that is requested in the constructor of my base controller, to make sure it always runs. This service checks some things from the database, and might error. If it errors, I want to redirect the user to a page that displays this error. I cannot find any way to accomplish this, as redirections can only be returned from a controller's action. What I want is to be able to 'return' a redirection from outside an action. What do I need to do to be able to redirect from my service? Do I have to implement my service differently, or not at all? I do not want to call this method manually on every action.
Relevant code:
// Every other controller in my bundle extends this one
class Controller extends SymfonyController
{
public function setContainer(ContainerInterface $container = null)
{
parent::setContainer($container);
$this->containerInitialized();
}
protected function containerInitialized()
{
// Initialize my.service before running action of every page
try {
$this->get('my.service');
} catch (SomeException $ex) {
// I want redirection to happen here
}
}
}
EDIT: I know I can use the controller kernel event to make something run on every page, which is what I am going to use. This still does not solve the redirection situation though.
Services are not supposed to decide if a redirect should happen or not or how to deal with the client's request and response. A service is meant to just provide a service to the controller that can be used independent of how the controller was called.
If you have a service that can trigger a redirect to a specific site, how do you use that service in another controller, another site or even in a command-line interface where redirect do not exist? You cannot, and that is bad.
Error-Handling in services are meant to be done with custom exceptions. If a service comes to a point where it cannot continue normally, an exception is thrown that describes the error that happened, but not how the error should be handled. The decision of how to deal with the error/exception should be done by the controller or command depending on what makes sense in the context of the controller. If there are different types of errors that a service can cause, these different types should be distinguishable by the exception (or the data in the exception) on which the controller can make the decision to redirect somewhere (or not). A service or exception should never impose on the controller how it should handle the error, only provide information for the controller(s) or command(s) to decide by themself.

Laravel - calling method without user request

I have my own library class which is designed for gathering data from external web sites (uses curl). This class has constructor which receives parameters (e.g. URL, xpaths) and method updateDatabase. How to pass parameters to constructor and call method updadeDatabase in app internally? This method should be fired e.g. two times per day without user request (using the cron) So I don't want insert this code in controller and create appropriate route. How to do this?
class Source
{
public function __construct(Http $http, array $params)
{
...
}
public function updateDatabase()
{
...
}
}
Your best bet is probably to create an artisan command. This gives you access to to run your code within the Laravel framework from the command line (or cron job).
All the information you need for creating an artisan command can be found here.
Basically, inside the command's fire() method you would do something like:
public function fire() {
$source = App::make('Source'); // or however you instantiate your class
$source->updateDatabase();
// output a message to the command line
$this->info('All done.');
}
Your command can also take arguments and options from the command line, if you need to pass those in.
The documentation linked above will tell you all you need to know.

How to call a zend controller action from an independent php file?

I am having a controller IndexController.php in which action is something like this
class IndexController extends CustomControllerAction {
public function preDispatch() {
if (!$this->view->authenticated) {
$this->_redirect('/users/login');
}
}
public function indexemailAction() {
//somecode which calculates certain things
}
}
NOw,I need to call the action "indexmailAction" inside the IndexController.php with an independent php file
The php file is indextest.php
<?php
//Need to write some code to call indexmailAction in IndexController.php
?>
What should I write in this file ......
Thanks in advance
I know this is a few years old, and this may not be the intended use of the classes/functions, but I've found the following quite useful in isolated files that are called from the command line.
The problem this solves for me is that it eliminates spawning of Apache processes. The solution is great because I can access the some Controller/Action needed that I would from the URL.
In almost any ZF1 based app, you can copy your index file and keep everything the same and just comment out the following line.
$application->run();
Anything below this line you can access with your autoloaders etc. It's crude, but it works. Unfortunately, you'll soon find yourself with limited access to a lot of the files your application has, and the feeling the only way you can access the files needed is through a Controller/Action.
Instead, I use the following in a new file below $application->bootstrap() ( still removing the $application->run() ):
$front = Zend_Controller_Front::getInstance();
// You can put more here if you use non-default modules
$front->setControllerDirectory(array(
'default' => APPLICATION_PATH.'/controllers'
));
$viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer();
$viewRenderer->setNeverRender(true);
Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);
$req = new Zend_Controller_Request_Http("http://anydomain.tld/controller/action");
// Example just to see how this can be extended
$req->setParam("someVar", "someValue");
$front->setRequest($req);
$front->dispatch();
In the end you have a isolated PHP file that bootstraps everything the same as your main index.php for the web, but you can manually trigger a controller/action as needed, giving you easier access to the rest of the files with how ZF1 intended you to access them.
Controllers are designed to be used in an MVC, not by scripts. Your controller should assemble request variables, direct them to models and return an HTTP response of some sort. Your scripts should act directly on the models instead.
Anyhow, if you insist, you can instantiate a controller class and call methods just like any other class as long as you inject any dependencies that the MVC would have.
If you want logic used in multiple places in your actions, then it should go in an action helper or if very generic code, then in a custom library (/library/custom/)
NB: The authentication would be better suited in a plugin rather than the pre-dispatch method in every controller.
You should not have to call a controller action for this, your logic should reside in your models. Then you can create a new instance of your model and invoke the appropriate methods. example :
require_once '/path/to/mymodel.php';
$mymodel = new Mymodel();
$data = $mymodele->fetchAll();
PS: Maybe you should think of creating a restful api to handle calls from outside your application
UPDATE:
ok, I see now what you need, The best way to achieve it is to call a url instead of a file (e.g. website.com/emails/send), if you are worried about security you can use some preshared key to make sure the request comes from you, send it with the request and check if it's correct in your action.

Zend Framework: Method after controller action, but before view rendered

I'm new to Zend framework and can't find a clear answer on this. I essentially want some code to execute after a controller's logic for a page, but before the layout and view are rendered.
For example, I want to auto-refresh the flash messages and provide them to the layout/view automatically, so that I don't need to do so in every controller. This obviously needs to happen after the controller code has executed since it might add messages.
$this->view->messages = $this->_helper->flashMessenger->getMessages();
The easiest way to do this is with a controller plugin, see http://framework.zend.com/manual/1.12/en/zend.controller.plugins.html. The postDispatch() method runs after your controller code but before the page is rendered.
I just use:
public function init()
{
if ($this->getHelper('FlashMessenger')->hasMessages()) {
$this->view->messages = $this->getHelper('FlashMessenger')->getMessages();
}
}
I use this in init() and it works fine. You could use it in postDispatch(), preDispatch() or dispatchLoopShutdown() if you like. A controller plugin would not be out of line, I just haven't gotten around to doing one yet.

How to make Zend automatically switch view and layout with contexts?

I have a mobile site that I added detection to for iPhones and other iOS devices. The iOS page needs a different layout and views than the regular pages (which are actually for older mobile devices). So, I have some code that does mobile detection, that part was easy. What I'd like to do is make it so that Zend automagically finds and uses the correct layout and view when an iOS device is detected, but that has turned out to be surprisingly hard...
I needed it to be up and running ASAP, so I did a quick and dirty hack that worked: in each action function, I have a simple If statement that detects if the iOS boolean flag has been set (which happens in the controller's init), and if so, overrides the layout and view explicitly. Existing code (in the actions):
if ($_SESSION['user']['iPhone']) {
$this->_helper->layout->setLayout('osriphone'); // 'osr' is the name of the app
$this->_helper->viewRenderer->setRender('iphone/index');
}
So this works, but it's kinda ugly and hacky and has to be put in each action, and each action's Renderer has to be set, etc. I got to reading about the Zend ContextSwitch, and that seemed like exactly the kind of thing I should use (I'm still kind of new to Zend), so I started messing around with it, but can't quite figure it out.
In the controller's init, I'm initializing the ContextSwitch, adding a context for 'iphone' and setting the suffix to 'iphone', and now what I'd like to do is have a single place where it detects if the user is an iOS device and sets the context to 'iphone', and that should make it automatically use the correct layout and view. New code (in the controller's init):
$this->_helper->contextSwitch()->initContext();
$contextSwitch = $this->_helper->getHelper('contextSwitch');
$contextSwitch->addContext('iphone', array('suffix' => 'iphone'));
$contextSwitch->setAutoDisableLayout(false);
if ($_SESSION['user']['iPhone']) {
//$this->_currentContext = 'iphone'; // Doesn't work.
//$contextSwitch->initContext('iphone'); // Doesn't work.
//$contextSwitch->setContext('iPhone'); // Not the function I'm looking for...
// What to put here, or am I barking up the wrong tree?
}
I did some reading on the contextSwitcher, and it seems like there is a lot of stuff on, e.g. setting it to be specific to each particular action (which I don't need; this needs to happen on every action in my app), and going through and modifying all the links to something like /osr/format/iphone to switch the context (which I also don't really need or want; it's already a mobile site, and I'd like the layout/view switch to be totally transparent to the user and handled only from the backend as it is with my quick and dirty hack). These seem like basically an equal amount of code to my quick and dirty hack. So... Anyone have some suggestions? I'm really hoping for just a single line like "$contextSwitch->setContext('iphone');" that I could use in an If statement in my controller's init, but the Zend documentation is awful, and I can't seem to find any examples of people doing something like this on Google or SO.
Ok I think I figured out how to put this into a plugin:
The Plugin:
//This is my own namespace for ZF 1.x library, use your own
class My_Controller_Plugin_Ios extends Zend_Controller_Plugin_Abstract {
public function preDispatch(Zend_Controller_Request_Abstract $request) {
parent::preDispatch($request);
if ($_SESSION['user']['iPhone']) {
$this->_helper->layout->setLayout('osriphone');
$this->_helper->viewRenderer->setRender('iphone/index');
}
}
}
register the plugin in your application.ini
resources.frontController.plugins.ios = "My_Controller_Plugin_Ios"
I think that's all there is to it. Although you may want to look into the userAgent plugin
ContextSwitch operates off the "format" property in the request object (by default). You need to set it somewhere in your app
$requestObject->setParam('format', 'iphone').
I'd set it in a bootstrap, or more appropriately, a controller plugin, but where it goes really depends on your app.
I don't use Zend ContextSwitch so I can't really help there, but you could use some inheritance in your controllers to set all layouts in just a couple of lines. Even though it might still be classed as a "hack" it is a way better hack
Now whenever you execute a action Zend first fires a number of other functions within the framework first, such as the routing, the preDispatch, Action helpers and so on. It also fires a number of things after the action such as PostDispatch. This can be used to your advantage.
First create a controller called something like "mainController" and let it extend Zend_Controller_action and in this controller create a function called predispatch()
Second. Extend your normal controllers to mainController. Since we now have a function called predispatch() Zend will automatically fire this on every controller, and if you do your iPhone/iOS check there it will automagically be performed on every action on every controller, as long as you don't overwrite the method in your controller (you can make this method final to prevent this). You can offcourse use a multitude of different non-Zend functions and/or helpers within the mainctroller to make the code as compact and reusable as possible Se example code below:
<?php
/**
*Maincontroller
*/
class MainController extends Zend_Controller_Action
{
/**
* Predispatch function is called everytime an action is called
*/
final public function preDispatch(){
//for security reasons, make sure that no one access mainController directly
$this->request = $this->getRequest();
if (strtolower($this->request->controller)=='main')
$this->_redirect('/index/index/');
//Check for iPhone
if ($_SESSION['user']['iPhone']) {
$this->_helper->layout->setLayout('osriphone'); // 'osr' is the name of the app
$this->_helper->viewRenderer->setRender('iphone/index');
}
}
}
<?php
/**
*Othercontroller
*/
class OtherController extends MainController
{
/**
* The correct layout for IndexAction is already set by the inherited preDispatch
*/
public function indexAction(){
/* YOUR CODE HERE */
}
}
For a good overview of the dispatch process check these links (same picture in both):
http://nethands.de/download/zenddispatch_en.pdf
http://img.docstoccdn.com/thumb/orig/22437345.png

Categories