Alternative for deprecated Router::getRouteCollection()->parse()

Hi all!

When I use this code in CakePHP 4.5.2.:

Router::getRouteCollection()->parse(‘/nl/blog/categories’);

I get a deprecation error:

Deprecated : 4.5.0 - Use parseRequest() instead. A:\Projects\cms\src\Controller\AppController.php, line: 291

Does anybody know the alternative for transfering a random URL (not the current url) to get this output:

[
‘pass’ => ,
‘action’ => ‘overview’,
‘controller’ => ‘Categories’,
‘plugin’ => ‘Blog’,
‘language’ => ‘nl’,
‘_route’ => object(Cake\Routing\Route\DashedRoute) id:0 { },
‘_matchedRoute’ => ‘/nl/blog/categorieen’,
]

The most important is that I can get the action, controller, plugin, … out of the output.
I tried a couple of things, this is one of them:

$testRequest = new ServerRequest('/nl/blog/categories');
$test2 = Router::parseRequest($testRequest);

Apparently, I can’t add a string to the ServerRequest object, while the documentation says it should: ‘You can supply the data as either an array or as a string.’

I wonder if this is a good approach. Maybe somebody can help me?
Thanks in advance!

Cheers,
Sam

$request = new ServerRequest(['url' => $url]);
$route = Router::parseRequest($request);

should be fine.

Thanks for your reply @KevinPfeifer! I tried this code before, but the problem is dat it outputs the data of the current request and not the data of the given url string. Here’s my try like you suggested:

$testRequest = new ServerRequest(['url' => '/nl/blog/categorieen']);
$testRoute = Router::parseRequest($testRequest);

debug($testRoute);

And this is the output when I debug the ‘$testRoute’-variable on my contact page:

[
‘pass’ => ,
‘controller’ => ‘Contact’,
‘action’ => ‘index’,
‘language’ => ‘nl’,
‘plugin’ => null,
‘_route’ => object(Cake\Routing\Route\DashedRoute) id:0 { },
‘_matchedRoute’ => ‘/nl/contact’,
]

So it looks like the ‘url’-parameter in the new ServerRequest object is ignored :confused:

I can’t confirm this

@KevinPfeifer very strange! What CakePHP 4 version do you have?
Here’s my attempt that doesn’t work:

I tested it with current CakePHP 5.0.4 but now I also just quickly spun up a CakePHP 4.5.3 and it works as I described.

BUT do you use fallback routes in your router? I mean this line

Or are you certain that this specific URL is mapped in your router?

Hi @KevinPfeifer,

Thanks for the additional tests! It’s good to know that there’s no bug in the CakePhp 4 core.

I’ll show you all my related code. This is my default routes.php:

<?php
/**
 * Routes configuration.
 *
 * In this file, you set up routes to your controllers and their actions.
 * Routes are very important mechanism that allows you to freely connect
 * different URLs to chosen controllers and their actions (functions).
 *
 * It's loaded within the context of `Application::routes()` method which
 * receives a `RouteBuilder` instance `$routes` as method argument.
 *
 * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
 * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
 *
 * Licensed under The MIT License
 * For full copyright and license information, please see the LICENSE.txt
 * Redistributions of files must retain the above copyright notice.
 *
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
 * @link          https://cakephp.org CakePHP(tm) Project
 * @license       https://opensource.org/licenses/mit-license.php MIT License
 */

use Cake\Routing\Route\DashedRoute;
use Cake\Routing\RouteBuilder;

return static function (RouteBuilder $routes) {
    /*
     * The default class to use for all routes
     *
     * The following route classes are supplied with CakePHP and are appropriate
     * to set as the default:
     *
     * - Route
     * - InflectedRoute
     * - DashedRoute
     *
     * If no call is made to `Router::defaultRouteClass()`, the class used is
     * `Route` (`Cake\Routing\Route\Route`)
     *
     * Note that `Route` does not do any inflections on URLs which will result in
     * inconsistently cased URLs when used with `:plugin`, `:controller` and
     * `:action` markers.
     */
    $routes->setRouteClass(DashedRoute::class);

    $routes->scope('/', function (RouteBuilder $root) {
        $root->setExtensions(['json']);

        $root->prefix('Admin', function (RouteBuilder $admin) {
            $admin->redirect(
                '/',
                ['controller' => 'Users', 'action' => 'index'],
                ['persist' => true]
            );

            /*$admin->scope('/articles', ['plugin' => 'Blog'], function (RouteBuilder $blog) { // deze moet nog veranderen
                $blog->fallbacks(DashedRoute::class);
            });*/
            
            $admin->fallbacks(DashedRoute::class);
        });

        $root->scope('/nl', ['language' => 'nl'], function (RouteBuilder $language) {
            $language->connect(
                '/contact',
                ['controller' => 'Contact', 'action' => 'index']
            );

            $language->connect(
                '/albums/album-{id}',
                ['controller' => 'Albums', 'action' => 'index']
            )->setPatterns(['id' => '[0-9]+']
            )->setPass(['id']);

            $language->connect(
                '/{slug}',
                ['controller' => 'Pages', 'action' => 'detail']
            )->setPatterns(['slug' => '[0-9a-zA-Z-]+']
            )->setPass(['slug']);

            $language->connect(
                '/',
                ['controller' => 'Home', 'action' => 'index']
            );
        });

        /*
         * Here, we are connecting '/' (base path) to a controller called 'Pages',
         * its action called 'display', and we pass a param to select the view file
         * to use (in this case, templates/Pages/home.php)...
         */
        $root->connect('/', ['controller' => 'Pages', 'action' => 'display', 'home']);

        /*
         * ...and connect the rest of 'Pages' controller's URLs.
         */
        //$builder->connect('/pages/*', 'Pages::display');

        /*
         * Connect catchall routes for all controllers.
         *
         * The `fallbacks` method is a shortcut for
         *
         * ```
         * $builder->connect('/:controller', ['action' => 'index']);
         * $builder->connect('/:controller/:action/*', []);
         * ```
         *
         * You can remove these routes once you've connected the
         * routes you want in your application.
         */
        $root->fallbacks();
    });

    /*
     * If you need a different set of middleware or none at all,
     * open new scope and define routes there.
     *
     * ```
     * $routes->scope('/api', function (RouteBuilder $builder) {
     *     // No $builder->applyMiddleware() here.
     *
     *     // Parse specified extensions from URLs
     *     // $builder->setExtensions(['json', 'xml']);
     *
     *     // Connect API actions here.
     * });
     * ```
     */
};

I have also a routes.php in my plugin (‘/plugins\Blog’):

<?php
use Cake\Routing\Route\DashedRoute;
use Cake\Routing\RouteBuilder;

return static function (Cake\Routing\RouteBuilder $routes) {
    $routes->scope('/', function (RouteBuilder $root) {
        $root->prefix('Admin', function (RouteBuilder $admin) { // hier er nog voor zorgen dat er niet 2x 'articles' in de slug staat ('/admin/articles/articles')
            $admin->plugin('Blog', ['path' => '/articles'], function ($articles) {
                $articles->fallbacks(DashedRoute::class);
            });
            /*$admin->scope('/articles', ['plugin' => 'Blog'], function (RouteBuilder $blog) { // deze moet nog veranderen
                $blog->fallbacks(DashedRoute::class);
            });*/
            
            //$admin->fallbacks(DashedRoute::class);
        });

        $root->scope('/nl', ['language' => 'nl'], function (RouteBuilder $language) { // Met de tweede parameter zorg je dat 'language' ook in de request zit.
            $language->scope('/blog', ['plugin' => 'Blog'], function (RouteBuilder $blog) {
                $blog->connect(
                    '/',
                    ['controller' => 'Articles', 'action' => 'overview']
                );

                $blog->connect(
                    '/{slug}',
                    ['controller' => 'Articles', 'action' => 'detail']
                )->setPatterns(['slug' => '[0-9a-zA-Z-]+']
                )->setPass(['slug']);

                $blog->scope('/categorieen', ['controller' => 'Categories'], function (RouteBuilder $categories) {
                    $categories->connect(
                        '/',
                        ['action' => 'overview']
                    );

                    $categories->connect(
                        '/{slugs}',
                        ['action' => 'detail']
                    )->setPatterns(['slugs' => '[\/0-9a-zA-Z-]+']
                    )->setPass(['slugs']);
                });

                $blog->connect(
                    '/tags/{slug}',
                    ['controller' => 'Tags', 'action' => 'detail']
                )->setPatterns(['slug' => '[0-9a-zA-Z-]+']
                )->setPass(['slug']);
            });

            $language->connect(
                '/{slug}',
                ['controller' => 'Pages', 'action' => 'index']
            )->setPatterns(['slug' => '[0-9a-zA-Z-]+']
            )->setPass(['slug']);
        });
    });
};

As you can see, the fallbacks line is included in the default route.php:

$root->fallbacks();

I’m also pretty sure thet the URL is mapped on the router. I see the right page when it’s in the address bar:

Another fact is that this deprecated code works for me:

$test = Router::getRouteCollection()->parse('/nl/blog/categorieen');
debug($test);

The last code outputs the right data:

I’ll dive further in the route.php code. Fingers crossed I found what I’m doing wrong.
Unless you know another possibility? :sweat_smile: