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

Dispatch model to controller based on slug

Hello. What i want to achieve is to pass a model to controller, which will be loaded only if the slug param in route is in format: "id-name" and both of them are correct for some record.
Here is the example URL: "hostname/post/edit/10-some-post-name" where in the last part "10" is the post id and "some-post-name" is some other index in table.

Here is what I've got. It is working but I wonder if there is a more native way of implementing this. I am aware that in 2.1.0 there is something like model binding but thats not the thing i want, since I want this to be strict connection of two table indexes.

$route->add('hostname/post/edit/{post}', 'Posts::edit');
class PostsController
{
    public function beforeExecuteRoute($dispatcher)
    {
        // One of the route parameters must be named as controller's default model
        // so we will know that it is our slug. 
        // Just like :int and :controller  modifiers does
        if (($slug = $dispatcher->getParam('post', 'string')) !== null) {
            if (preg_match('/^([0-9]+)-([a-z0-9-]+)$/', $slug, $matches)) {
                $post = Posts::findFirst([
                    'id'    => $matches[1],
                    'title' => str_replace('-', ' ', $matches[2])
                ]);

                if ($post) {
                    $dispatcher->setParam('post', $post);
                    return true;
                }
            }

            $this->view->disable();  
            $this->flashSession->error('The requested post does not exist');
            $this->response->redirect(['for' => 'post-search']);
            return false;
        }

        return true;
    }

    public function editAction(Posts $post)
    {
        // This time $post is not in zombie state
        // which can happen when using model binding
    }
}
edited Apr '16
$route->add('hostname/post/edit/{post}', 'Posts::edit');

Why just not add proper route path in this case ? Or maybe sometimes you don't gonna have id as first ?



545
edited Apr '16

Well, the problem is somewhere else. I want to avoid the code repetition in every action, which is responsible for loading the model basing on the slug and if it could not be loaded, the proper redirection with an error message happens.

Basically this part will land at the very beginning in every action which is using my convention:

$post = Posts::findFirst([
    'id'    => $id,
    'title' => str_replace('-', ' ', $title) // unslug title
]);

if (!$post) {
    $this->view->disable();
    $this->flashSession->error('The requested post does not exist');
    $this->response->redirect(['for' => 'post-search']);
    return;
}

Of course I can just add a route like this one but I want the proper model to be passed to the action, not and id and title

$route->add('hostname/post/edit/{id:[0-9]+}-{title:[a-z0-9-]+}, 'Posts::edit');

Oh you mean this. There i guess there is no other idea. Perhaps you could use conversors maybe.

https://docs.phalcon.io/pl/latest/reference/routing.html#using-conversors



545

Its getting too dirty when using conversors for this. I mean, loading and returning model at that level is ok, but redirection is at least ugly. If there is no other way I will have to stick to this.