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

Overwrite parameters in beforeMatch

Hello,

I'm trying to match routes on-fly by looking in the database, and so far I did this on Kohana using the following:

Route::set('category', '(<link_rewrite>(/p<page>)(/<filters>))', array(
        'page'      => '\d+',
        'filters'   => '.*',
    ))
    ->filter(function($route, $params, $request) 
    {
        if (empty($params['link_rewrite']))
            return false;
        $result = DB::select('c.id_category')
            ->from(array('category', 'c'))
            ->join(array('category_lang', 'cl'), 'inner')->on('c.id_category', '=', 'cl.id_category')
            ->where('cl.id_lang', '=', 1)
            ->where('cl.link_rewrite', 'like', $params['link_rewrite'])
            ->limit(1)
            ->execute();
        if ($result->count() > 0)
        {
            $params['controller']   = 'Category';
            $params['action']       = 'index';
            $params['id_category']  = $result->get('id_category');
            return $params;
        }
        return false;
    });

I have tried to use beforeMatch but so far I'm guessing that the return must be boolean. Also I tried to match all to a single controller-action and from then to use dispatcher->forward() but $router->getControllerName() and $router->getActionName() doesn't get overwritten.

There is any way to look to the database for a slug and depending on what it found, to return a controller, action and params?



5.1k
edited Nov '14

You have two options:

  1. You can create functionality like this using dispatcher, check docs here: https://docs.phalcon.io/pl/latest/reference/dispatching.html
  2. Make some kind of wildcard route that match all cases that you need (ex. "/[0-9a-z-_\/]"). You catch this url in specific action that will run searching in database and then redirect to specific page.


1.3k

Thanks for your reply.

  1. I tried it, but the status code for the new page is always 404.
  2. I tried already and like I said in my original post, $router->getControllerName() and $router->getActionName() doesn't get overwritten.


5.1k

Ad 1. - Without example of your code it will be hard to check what is wrong. My guess: use beforeDispatch event instead event that you are using right now. Add plugin to this event that will search for match. Ad 2. - I pointed on "redirect" not "forward" - they are different.



1.3k

I managed to get where I wanted by using beforeMatch.

$router->add('/(?P<link_rewrite>[A-Za-z0-9\-]+)(?:\.(?P<attribute>[A-Za-z\-]+)\-(?P<id_attribute>[0-9]+)?)?(?:\.html?)?')
            ->beforeMatch(function($uri, $route) {
                if (preg_match($route->getCompiledPattern(), $uri, $matches))
                {
                    // here goes some db query-ing to extract id_product based on link_rewrite...
                    $route->reConfigure($route->getPattern(), array(
                        'controller'    => 'product',
                        'action'        => 'index',
                        'id_product'    => (int) $id_product_from_db,
                        'link_rewrite'  => isset($matches['link_rewrite']) ? $matches['link_rewrite'] : '',
                        'id_attribute'  => isset($matches['id_attribute']) ? $matches['id_attribute'] : '',
                    ));
                    return true;
                }
                return false;
            }
        );


1.3k

Are there any ways to send a literal integer?

'cause if I send $id_product_from_db = 1, then $this->dispatcher->getParam('id_product') is set to {link_rewrite}'s value.



1.3k
edited Dec '14

There is a way to send a literal integer 1 or boolean without being overwriten?



5.1k

What do you mean? This is not the issue of type that you are sending. Your link_rewrite just match integers too.



1.3k

This is my code:

$router->add('/{slug:(?P<slug>[a-z0-9\-]+)}', [
            'controller'    => 'category',
            'action'        => 'index'
        ])->beforeMatch(function($uri, $route) use ($di) {
            if (preg_match($route->getCompiledPattern(), $uri, $match))
            {
                $result = Category::findFirstBySlug($match['slug']);
                if ($result)
                {
                    $params = [
                        'id_category' => $result->id_category,
                        'true' => true,
                    ];
                    $route->reConfigure($route->getPattern(), array_merge($route->getPaths(), $params));
                    return true;
                }
            }
        })->setName('category');

Actual $this->dispatcher->getParams():

Array
(
    [slug] => women
    [id_category] => women
    [true] => women
)

Expected $this->dispatcher->getParams():

Array
(
    [slug] => women
    [id_category] => 1
    [true] => 1
)


5.1k
edited Dec '14

Ah now I see where is the problem :)

The effect of this:
array_merge($route->getPaths(), $params)

looks like:

   array(
            'controller' => 'category',
            'action' => 'index',
            'slug' => 1,
            'id_category' => 1,
            'true' => true
        );

So you are reconfiguring your router to use your first matched value (that means this "1" or "true") as each param. Test the reconfiguration of your route as follow:

             $route->reConfigure($route->getPattern(), array_merge($route->getPaths(), ['customParams' => $params]));


1.3k

Nice one! But it comes with a warning:

Warning: Illegal offset type in /home/www/index.php on line 376 

index.php line 376

echo $application->handle()->getContent();


5.1k
Accepted
answer
edited Dec '14

Yes, it's true. But why not use something like this:

        $router->getRouter()->add('/{slug:(?P<slug>[a-z0-9\-]+)}', [
            'controller' => 'category',
            'action' => 'index'
        ])->beforeMatch(function($uri, $route) use ($di) {
                if (preg_match($route->getCompiledPattern(), $uri, $match))
                {
                    $category = new \stdClass();
                    $category->id = 1;
                    $category->something = true;

                    $di->set('category', $category);
                    return true;
                }
            });

And then in your action just use $this->category :)



1.3k

Will do. Thanks! :)