We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

addPost and action postfix i routing

Hi, I have a question. How can I add automaticly postfix for action, when http request method is post? I have

$router->addPost('/:action/:params',['controller'=>'print','action'=>1,'params'=>2]);

And I like "1" parameter replace on 1."Post" string. I tried concatonation and str_replace but without good results. Mayby anyone have idea, except redirect in normal action.

F.e. I'd like that when user call to /print/upload action then called will be upload action from print controller but if user will send post request to same url call shoud be uploadPostAction from print controller.

Thanks in advance Jarek

To select different actions on the same URL with different methods, you can define two routes:

$router->addGet('/print/upload/:params', ['controller'=>'print', 'action'=>'upload', 'params'=>1]);
$router->addPost('/print/upload/:params',['controller'=>'print', 'action'=>'uploadPost', 'params'=>1]);

Or, another way, check method inside one action.

I know that I can add it manualy. But I'd like match it automaticly. Every action from print controller is call with "Post" postfix whet http method is POST.

Of course I can add check method to base controller. But if in framework is routing I would make it only by routing.

For example in laravel it is possible by:

Route::post('{action?}/{params?}',['before'=>'csrf',function($action,$params=null){ return (new PrintController())->{$action.'Post'}($params); }]);

Maybe code above will helphul to explain, what I mean.

edited Apr '14

Does that laravel Route::post() also handle the GET request, or do you need two like phalcon?

If yes, then they are the same, apart from the fact that the phalcon one is more readable.

Route::get('{action?}/{params?}',['before'=>'csrf',function($action,$params=null){ return (new PrintController())->{$action}($params); }]);
Route::post('{action?}/{params?}',['before'=>'csrf',function($action,$params=null){ return (new PrintController())->{$action.'Post'}($params); }]);

Thank for your answer. As I understad, it should be like this code:

$router = new \Phalcon\Mvc\Router();

$router->add('/',['controller'=>'print','action'=>'index']);

$router->addGet('/:action/:params',['controller'=>'print','action'=>1,'params'=>2]);

$router->addPost('/:action/:params',[function($action,$params=null){ return (new PrintController())->{$action.'PostAction'}($params); }]); return $router;

But it not work. I also tried replace predefined :action on regular expresion.

Thanks.

edited Apr '14

First of all, $router->addPost() is what it's named - add POST, not GET. To match both methods - use $router->add() and check methods in code or use two routes.

Next, you can do such action name override but do it in BaseController::beforeExecuteRoute() - not in the routes configuration. Don't think it'll make the big difference :) BaseController - some controller class that will be extended by your controllers to not copy-paste this check to every single controller.

Another elegant way is to use annotations router - you'll have again two routes (for GET and for POST) but they will be defined just in place near the corresponding actions.

PS: don't know if there is any way to transform routing parameters with a custom functions - I guess, there is no...

@vladimmi let me know, if you can what should I do in beforeExecuteRoute

When I will call to beforeExecuteRoute what shoutd I do? Because if I will set redirect I will lose POST data.

Unless content replacted in beforeExecuteRoute is return as action to route class.

edited Apr '14

You should not redirect but forward - this will not create a new request, just point dispatcher to another action. Like this:

class BaseController extends \Phalcon\Mvc\Controller
{
    public function beforeExecuteRoute($dispatcher)
    {
        if($_SERVER['REQUEST_METHOD'] == 'POST'] && !$dispatcher->wasForwarded()) {
            $dispatcher->forward($dispatcher->getActionName() . 'Post');    //if POST, 'some' becomes 'somePost'
        }
    }
}

Notice: getActionName() returns name without trailing 'Action', so appending 'Post' will forward request from Controller::someAction to Controller::somePostAction, not Controller::someActionPost.

Thanks for your example, If I use beforeExecureRoute this action is called 2 times and in result I see: action uploadPostPost was not found.

public function beforeExecuteRoute($dispatcher){
        if($_SERVER['REQUEST_METHOD']=='POST'){
            $dispatcher->forward(['action'=>$dispatcher->getActionName().'Post']);
        }
    }
edited Apr '14

Thanks for your example, If I use beforeExecureRoute this action is called 2 times and in result I see: action uploadPostPost was not found.

public function beforeExecuteRoute($dispatcher){
        if($_SERVER['REQUEST_METHOD']=='POST'){
            $dispatcher->forward(['action'=>$dispatcher->getActionName().'Post']);
        }
    }

But I solved problem in Router. Below is sample code, maybe will be helpful for someone.

$router->add('/:action/:params',['controller'=>'print','action'=>1,'params'=>2])->via(['POST'])->convert('action',function($action){
    return $action.'Post';
});

Interesting is fact that I tried before same function, but with addPost. With addPost didn't work.

edited Apr '14

Yep, edited my example with an additional check for already forwarded action.

Thank you, you found an interesting way to transform parameters, didn't know about it :) But this route should not match GET requests, yes? Because ->add()->via('POST') should be the same as ->addPost() (and ->addPost()->convert() should work the same). Do you use another route for GETs?

edited Apr '14

@vladimmi yes I use 4 rules. For GET, POST, PUT, DELETE method.


$router->add('/:action/:params',['controller'=>'print','action'=>1,'params'=>2])->via(['GET']);
$router->add('/:action/:params',['controller'=>'print','action'=>1,'params'=>2])->via(['POST']);
$router->add('/:action/:params',['controller'=>'print','action'=>1,'params'=>2])->via(['PUT']);
$router->add('/:action/:params',['controller'=>'print','action'=>1,'params'=>2])->via(['DELETE']); 

It's very helpfull when you want explode actions same every time, without "if" or method with "if" for each request.

In phalconphp or laravel it's very comfortable to implement, but it's not standard in each framework.

Thanks for your involvement in discussions.

Regards, Jarek