-
|
While we're looking forward to greater integration with Blade, routing to a template instead of a controller action seems to be rather unusual for Laravel developers. Besides better separating application logic from presentation, there are several other use cases where routing through a controller is preferable (for both Twig and Blade):
Some quick and dirty experiments for now: As a start and without looking deeply into Craft's code, we experimented with the if (str_starts_with($template, 'route:')) {
$parts = explode(':', $template);
$event->handled = true;
$event->route = ['/tmproute', []];
$actionTrigger = app(GeneralConfig::class)->actionTrigger;
Route::get("/{$actionTrigger}/tmproute", [$parts[1], $parts[2]]);
}which seems to work at first glance, but messes up Laravel functionality like paginators page url generation. Or setting up custom routes matching the sections URI settings, which seem to 'win' over Craft's routing (is that guarenteed??) Route::any('{site}/demo/{slug}', [DemoController::class, 'showByManualRoute'])->middleware([
ResolveSite::class,
CustomGetMatchedElement::class,
]);(where This also seems feasible, but it requires setting up many routes, especially in multi-site installations, and requires a better understanding of how Craft's middleware works. Or pointing the route to a 'proxy' template: {{ new App\Http\Controllers\DemoControllerDemoController()->show() }}No idea whether this would always work. So it would be great to have official support for routing to controller actions, whether through the CP UI, event handling, documentation, or any other supported mechanism. |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 1 reply
-
Yes, your own application routes will always win from Craft's, the Craft site route is registered as a fallback route. I do agree it would be cumbersome, so I've added a way to define this when using the You can look at the tests in that commit on usage. The matched element can be added as a parameter to your controller's method, either through |
Beta Was this translation helpful? Give feedback.
-
|
Craft will transparently “proxy” the return from This is roughly equivalent to the 5.x behavior, but should be compatible. The one major limitation in the Laravel implementation that I can see is that it will only accept “action” routes, which we're slowly moving away from. All this means is that it's technically only possible for plugins to register, via their Here’s a working example that replaces the controller for an entry: # In your plugin’s `bootPlugin()` method…
Event::listen(function (\CraftCms\Cms\Element\Events\SetRoute $event) {
if (! $event->element instanceof Entry) {
// …or other conditions!
return;
}
$event->handled = true;
// Legacy “action route” format:
$event->route = [
'my-plugin/some-element-route',
[
'variables' => [
'entry' => $event->element,
],
],
];
});# routes/actions.php
# Bind the action path to your controller:
Route::get('some-element-route', MyController::class);(The second step is not necessary if you are using the adapter and your controller is automatically given an action route!) I'm curious what Rias will recommend, as far as making this future-friendly. It would be great to be able to set a class name (i.e. (Oh, he already replied!!!) |
Beta Was this translation helpful? Give feedback.
-
|
Phantastic, that was quick, thanks. Just played around with the dev branch which works great, this is what our experiment looks like now: <?php
namespace wsydney76\craft6blade\Listeners;
use CraftCms\Cms\Element\Events\SetRoute;
use CraftCms\Cms\Entry\Elements\Entry;
use CraftCms\Cms\Route\ControllerRoute;
use http\Exception\InvalidArgumentException;
class HandleSetRoute
{
public function handle(SetRoute $event): void
{
$element = $event->element;
// Provisionally only top-level entries
if (!($element instanceof Entry) || !$element->section) {
return;
}
$template = $element->section->getSiteSettings()[$element->siteId]->template ?? null;
if (!$template) {
return;
}
if (str_starts_with($template, 'route:')) {
$action = explode(':', $template);
array_shift($action);
if (count($action) !== 2) {
throw new InvalidArgumentException('Template must have exactly 2 route parts after prefix, separated by colon.');
}
$event->route = new ControllerRoute($action, [
'format' => request()->query('format', 'html'), // project-specific convention
]);
$event->handled = true;
}
}
}Just some questions (no priority)
PS. The blade directives do not register, I (well, AI) had to change in ViewServiceProvider::registerBladeDirectives: // replace
$this->app->afterResolving('blade.compiler', function (BladeCompiler $blade): void {}
// with
$this->callAfterResolving('blade.compiler', function(BladeCompiler $blade): void {} |
Beta Was this translation helpful? Give feedback.
Yes, your own application routes will always win from Craft's, the Craft site route is registered as a fallback route.
I do agree it would be cumbersome, so I've added a way to define this when using the
SetRouteevent: f6903a2You can look at the tests in that commit on usage.
The matched element can be added as a parameter to your controller's method, either through
ElementInterface $elementor by its refname, in the case of an entry this would beEntry $entry