Solved thread

This post is marked as solved. If you think the information contained on this thread must be part of the official documentation, please contribute submitting a pull request to its repository.

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?



3.3k
edited Nov '14

You have two options:

  1. You can create functionality like this using dispatcher, check docs here: http://docs.phalconphp.com/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.


787

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.


3.3k

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.



787

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;
            }
        );


787

Are there any ways to send a literal integer?

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



787
edited Dec '14

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



3.3k

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



787

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
)


3.3k
edited Dec '14

Ah now I see where is the problem :)

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

looks like:

```php 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:

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



787

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();


3.3k
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 :)



787

Will do. Thanks! :)