Automatic Routing
Created 5 years ago by ryanthompson

Something that is SUPER wasteful with resources and just not necessary.. the majority of our routing.

The idea here is to automate CRUD and define a best case for control panel routing. Here is my proposal:

admin/* // Engages automatic routing

admin/{slug} // Defines the module to use which will use same name stream for controller by default (pages/pages), otherwise first stream defined if none match the namespace/slug

admin/{slug}/{stream} // Defines the stream to access CRUD for based on the slug / namespace and the index method.

admin/{slug}/{stream}/{action} // Defines all as well as the controller action

Anything past this it will look into subdirectories, then a controller, then index method OR method if provided.

Let's open this up for discussion and point out any best cases you know of and pick it apart!

ryanthompson  —  5 years ago Best Answer

This is done and in 3.7 - HUGE thanks to you @frednwt thank you!

ryanthompson  —  5 years ago

https://pyrocms.com/forum/channels/roadmap/manual-loader

https://pyrocms.com/forum/channels/roadmap/automatic-routing

Just opened up a couple roadmap discussions on this. I can push up initial code I've got lying around at home later tonight if anyone is open to digging in and helping test / profile.

frednwt  —  5 years ago

I agree, it is how I build my admin routes usually (on custom app). I don't see any down for the admin part, and the manual routing can still be keep if needed.

ryanthompson  —  5 years ago

@frednwt care to share some code on how you handle route patterns to controllers?

frednwt  —  5 years ago

A very basic routing for that. I'm using a anonymous function but it should be move in a class (else it cannot be cached).

        $router->any(
            'admin/{module}/{stream?}/{action?}/{id?}',
            function ($module, $stream = null, $action = null, $id = null) use ($router) {
                if (!$module = app('module.collection')->get($module)) {
                    abort(404);
                } else {
                    $namespace = (new \ReflectionClass($module))->getNamespaceName();
                }

                if (!$stream) {
                    // TODO: How to make that generic?
                    $stream = 'contents';
                }

                $controller = vsprintf('%sController', [ucfirst($stream)]);

                if (!$action) {
                    $action = 'index';
                }

                $action = vsprintf('%s\Http\Controller\Admin\%s@%s', [
                    $namespace,
                    $controller,
                    $action,
                ]);

                $route = $router->current();

                return $route
                    ->uses($action)
                    ->run();
            }
        );

The only thing we currently cannot guess is the default $stream if we are on the default route (e.g. /admin/contents).

frednwt  —  5 years ago

Note that mine is a bit more complex because I'm checking if the controller exists. If not I'm using a default controller with basic CRUB.

ryanthompson  —  5 years ago

@frednwt I think this might be best served somewhere earlier up the request chain right? (see last line here for my concern). Like near/in the kernel. But yes you'd have to check for a controller regardless - there will be two class checks I think. One for the literal path to class mapping. And one for the use case where stream == module so no stream would be listed in the URI.

I would also like to allow crawling into other controller directories like Admin/Api/Controller if the path indicated it..

So, is this routed first? Last? How do you deal with when the CRUD controller stuff already exists? I would assume this needs to be first.

frednwt  —  5 years ago

Right we can just stream == module. It may not be always true but it is probably the best. And maybe adding somewhere in the Module a $defaultStream = null that can be override if needed?

No idea how to manage other directories. It will be tricky. Maybe using some naming convention like /admin/contents/api-contents/create. But using another / will be complicated.

For the order, I'm currently routing it as last. To be sure no custom routes override. And my routing is more something like:

        $controller = vsprintf('%s\Http\Controller\Admin\%s', [
            $namespace,
            $controller,
        ]);

        if (!class_exists($controller)) {
            $controller = 'App\Http\Controller\Admin\BreadController';
        }

        $action = vsprintf('%s@%s', [
            $controller,
            $action,
        ]);

Then every admin controllers extends the BreadController.

ryanthompson  —  5 years ago Best Answer

This is done and in 3.7 - HUGE thanks to you @frednwt thank you!

frednwt  —  5 years ago

You're welcome ;)