I'm using silex and I'm trying to use controllers as services. This conception works fine but I can't figure out how to pass arguments to controller method. Here is what I mean
IndexController.php
class IndexController
{
public function pagesAction($page)
{
return $page;
}
}
//app.php
$app['index.controller'] = $app->share(function() use ($app) {
return new Controllers\IndexController();
});
$app->get('/pages/{num}', "index.controller:pagesAction");
When I access pages/3 I get
Controller "SD\Controllers\IndexController::pagesAction()" requires that you provide a value for the "$page" argument (because there is no default value or because there is a non optional argument after this one).
I also tried
$app->get('/pages/{num}', "index.controller:pagesAction:num");
Any ideas?
Change this
class IndexController
{
public function pagesAction($page) //what is $page? Is not into route
{
return $page;
}
}
to
class IndexController
{
public function pagesAction($num)
{
return $page;
}
}
This is because silex (and is also Symfony2 logic, of course) expects arguments name to be exactly the same from route to controller
OR
you should change your route to be parametrized for $page variable
Related
I am building my website in Slim 3 MVC framework.I need to call some commonly used functions for controller (Eg: For Alias of page title I am using a function called function getAlias(){.....}).
Where I have to create those functions? How can I call inside controllers?
There's a number of ways to do this. If the functions have no side effects, then one option would be to have a utilities class with static methods in it it.
Another option would be to extend all your route actions from a common class and use that:
// CommonAction.php
class CommonAction
{
protected function getAlias() { }
}
// HomeAction.php
class HomeAction extends CommonAction
{
public function __construct(/*dependencies here*/) { }
public function __invoke($request, $response, $args) {
// route action code here
return $response;
}
}
// index.php
$app = new Slim\App(require('settings.php'));
$container = $app->getContainer();
$container[HomeAction::class] = function ($c) {
return new HomeAction(/*dependencies*/);
}
$app->get('/', HomeAction::class);
$app->run();
If the functionality is part of your domain layer, then inject those classes into your route actions as a dependency.
Given the following Controller:
class Page {
public function about($section){
switch($section){}
}
}
How can I pass the a value to Page->about() directly from $f3->route?
Fat-Free will populate two parameters to each routing handler. So when you got this route:
$f3->route('GET /about/#section','\Page->about'); it will call your function with 1st parameter being the framework instance and 2nd is an array of all routing arguments.
class Page {
public function about($f3, $args){
switch($args['section']){}
}
}
See http://fatfreeframework.com/routing-engine#RoutesandTokens for more details.
I have started learning Symfony2. I came across a doubt: if I have this route:
# app/config/routing.yml
hello:
path: /hello/{name}
defaults: { _controller: AcmeHelloBundle:Hello:index }
And this controller:
// src/Acme/HelloBundle/Controller/HelloController.php
namespace Acme\HelloBundle\Controller;
use Symfony\Component\HttpFoundation\Response;
class HelloController
{
public function indexAction($name)
{
return new Response('<html><body>Ciao '.$name.'!</body></html>');
}
}
Internally Symfony2 (inside app/bootstrap.php.cache) calls the call user_func_array() PHP built-in function:
$arguments = $this->resolver->getArguments($request, $controller);
$response = call_user_func_array($controller, $arguments);
And the call to the getArguments() method returns an array of arguments to pass to the action method. But if the controller were:
// src/Acme/HelloBundle/Controller/HelloController.php
namespace Acme\HelloBundle\Controller;
use Symfony\Component\HttpFoundation\Response;
class HelloController
{
public function indexAction($n)
{
return new Response('<html><body>Ciao '.$n.'!</body></html>');
}
}
Symfony would have complained with a RuntimeException because no param $n is set.
My question is: how Symfony controls this behaviour, I mean, if a route has a {name} param, why the controller must have an action method with a $name parameter and the parameter must be called $name?
Cause in plain PHP, this would work:
$name = 'Alex';
function indexAction($n) {
echo $n;
}
$f = 'indexAction';
$arguments = array($name);
call_user_func_array($f, $arguments);
Even if the functions signature accepts a param named as $n and not as $name.
I hope that this question is understandable, if not, please tell me and I will make an edit.
Thanks for the attention!
It's all done in the Controller Resolver HttpKernel/Controller/ControllerResolver.php via getArguments() & doArguments().
For a better understanding, you will find what you need in Getting The Controller Arguments
Edit: Answer comment.
Does Symfony use the ReflectionParameter class internally in order to keep track of the params of the method's signature and match them with the route params?
Yes, the ControllerResolver uses:
ReflectionMethod to keep track of the params of the method's signature if $controller is a method
ReflectionObject if $controller is an object
ReflectionFunction if $controller is an function
Here is how:
public function getArguments(Request $request, $controller)
{
if (is_array($controller)) {
$r = new \ReflectionMethod($controller[0], $controller[1]);
} elseif (is_object($controller) && !$controller instanceof \Closure) {
$r = new \ReflectionObject($controller);
$r = $r->getMethod('__invoke');
} else {
$r = new \ReflectionFunction($controller);
}
return $this->doGetArguments($request, $controller, $r->getParameters());
}
The request is handled by the Symfony front controller (e.g. app.php);
The Symfony core (i.e. Kernel) asks the router to inspect the request;
The router matches the incoming URL to a specific route and returns information about the route, including the controller that should be executed;
The Symfony Kernel executes the controller, which ultimately returns a Response object.
Resource
In your example you only have one parameter on your action, so it's obvious to us that it needs to be populated from the route.
To extend your example, if you added another parameter to the route such as:
# app/config/routing.yml
hello:
path: /hello/{name}/{surname}
defaults: { _controller: AcmeHelloBundle:Hello:index }
And changed your controller to:
// src/Acme/HelloBundle/Controller/HelloController.php
namespace Acme\HelloBundle\Controller;
use Symfony\Component\HttpFoundation\Response;
class HelloController
{
public function indexAction($s, $n)
{
return new Response('<html><body>Ciao '.$n.' '.$s.'!</body></html>');
}
}
Symfony wouldn't know which variable $s or $n to populate with which route parameter.
If you change your action:
public function indexAction($surname, $name)
{
return new Response('<html><body>Ciao '.$name.' '.$surname.'!</body></html>');
}
Now, Symfony can look at your argument names and map them to the route parameters. It also means you can have your indexAction arguments in any order as long as their names match the route parameters. I believe Symfony internally uses the Reflection API to figure all this out.
I'm trying to make urls translatable in my Silex app.
First, I tried overriding UrlGenerator and RedirectableUrlMatcher, but that didn't really work.
Then, I tried overriding:
$app['route_class'] = 'My\Translatable\Route';
with code like this:
class Route extends Silex\Route
{
public function setPattern($pattern)
{
return parent::setPattern(str_replace('admin', 'admin2', $pattern));
}
}
But I'm getting https://gist.github.com/6c60ef4b2d8d6584eaa7.
What is the right way to achieve this?
So the solution is to extend RedirectableUrlMatcher and overwrite match method instead of Route.
Matcher.php
class Matcher extends Silex\RedirectableUrlMatcher
{
public function match($pathInfo)
{
return parent::match(str_replace('/admin', '/', $pathInfo));
}
}
app.php
$app['url_matcher'] = $app->share(function () use ($app) {
return new Matcher($app['routes'], $app['request_context']);
});
Now when I'm accessing http://domain.com/admin silex returns content for http://domain.com/.
Hope this is what you need.
How can i forward to other action inside the same controller avoiding repeat all dispatch proccess ?
Example:
If i point to User Controller the default action is indexAction() inside this funciton i use _forwad('list') ... but all dispatch proccess are repeated.. and i dont that
Whats is the right way ?
Usually, you will install routes to redirect your users to the proper (default) action, instead of the index action (read how to redirect from a given route using Zend_Router). But you can do everything manually if you really want to (however this is called "writing hacker code to achieve something dirty") directly from the controller.
Change your "view script" to be rendered, then call your action method....
// inside your controller...
public function indexAction() {
$this->_helper->viewRenderer('foo'); // the name of the action to render instead
$this->fooAction(); // call foo action now
}
If you tend on using this "trick" often, perhaps you may write a base controller that you extend in your application, which can simply have a method like :
abstract class My_Controller_Action extends Zend_Controller_Action {
protected function _doAction($action) {
$method = $action . 'Action';
$this->_helper->viewRenderer($action);
return $this->$method(); // yes, this is valid PHP
}
}
Then call the method from your action...
class Default_Controller extends My_Controller_Action
public function indexAction() {
if ($someCondition) {
return $this->_doAction('foo');
}
// execute normal code here for index action
}
public function fooAction() {
// foo action goes here (you may even call _doAction() again...)
}
}
NOTE : this is not the official way to do it, but it is a solution.
We Can Also use this Helper To redirect
$this->_helper->redirector->gotoSimple($action, $controller, $module, $params);
$this->_helper->redirector->gotoSimple('edit'); // Example 1
$this->_helper->redirector->gotoSimple('edit', null, null, ['id'=>1]); // Example 2 With Params
If you don't want to re-dispatch there is no reason you can't simply call the action - it's just a function.
class Default_Controller extends My_Controller_Action
{
public function indexAction()
{
return $this->realAction();
}
public function realAction()
{
// ...
}
}
You could also create a route. For example I have in my /application/config/routes.ini a section:
; rss
routes.rss.route = rss
routes.rss.defaults.controller = rss
routes.rss.defaults.action = index
routes.rssfeed.route = rss/feed
routes.rssfeed.defaults.controller = rss
routes.rssfeed.defaults.action = index
Now you only need one action and that is index action but the requess rss/feed also goes there.
public function indexAction()
{
...
}