Laravel - how to pass a parameter to a route? Is t

2019-05-24 18:38发布

问题:

The simplified scenario is the following:

I have a few pages in the database (ideally not more than 10-20, but there is no hard limit). They all have some content and a "slug" (there are slugs that contain a forward slash, if that matters). I'm trying to register route(s) to these slugs to a PageController that displays the content of the given page. Here are my ideas about the approach:

  • Register a single "catch 'em all" route (which should be the very last registered route). This is definitely bad.
  • Query all links from the database and register individual routes to them. This is easy, but I have to query the DB (which might not be optimal) and register tens, potentially hundreds of routes.
  • What I also tried: register a HTTP Middleware, get the query parameter by using $request->path(), check if this is an existing slug, and if so, pass it to the PageController. I achieve this by simply calling Route::get($request->path(), 'PageController@show').

Both of the last two methods seem to work, but there is a question: how do I tell the Controller which page to dislay?

Or is there a better approach?

Thanks in advance

Edit: I definitely don't want to use a 'prefix' and add the slug as a parameter to it. The defined slug should be the exact URL to the page.

回答1:

All approaches you've listed can be a valid approaches to solving this problem (even the first one if you attach a where condition on the route). However in my opinion the best approach, performance wise, would be based on the second solution, and would be to generate and write the routes to a file when needed and then caching them. So here's how I'd go about doing it:

Warning: This approach only works if you don't have any Closure based routes

1. Create a separate routes file where you'll be storing your page routes, let's say in app/Http/Routes/page.php. This is where you'll be writing the route definitions for your pages. You'll also need to add this to the map method of the App\Providers\RouteServiceProvider class:

public function map(Router $router)
{
    $router->group(['namespace' => $this->namespace], function ($router) {
        require app_path('Http/routes.php');
        require app_path('Http/Routes/page.php');
    });
}

2. Then you need to generate and write the route definitions for the pages to that file. Something like this should suffice:

$path = app_path('Http/Routes/page.php');
$definition = "Route::get('%s', 'PageController@show');\n";

// Remove the routes file so you can regenerate it
if (File::exists($path)) {
    File::delete($path);
}

// Write the new routes to the file
foreach (App\Page::all() as $page) {
    File::append(sprintf($definition, $page));
}

// Rebuild Laravel's route cache so it includes the changes
Artisan::call('route:cache');

The code above should be executed on specific events that you can attach to the Page model: created, deleted, updated (but only if the slug was modified during the update).

3. To access the page details in your controller you just need to use the path of the request, since that's your slug. So this would do:

public function show(Request $request)
{
    // You need to prepend the slash for the condition since
    // the path() method returns the request path without one
    $page = App\Page::where('slug', '/' . $request->path())->get();

    // Do your stuff...
}

There you go, now you have route definitions for all your pages. And the fact that they're cached mitigates any performance penalties you would get when there are lots of routes and you're only touching the database when you are making changes to those routes, not on every request.