hightechredneckwoman's avatar

Can routes be reloaded/refreshed from a controller?

I built a CMS using Laravel 5.2. I have a Pages module in my CMS where pages can be created, updated, and deleted. The page data is stored in the database.

When a page is created, I add a route to a custom route file that gets included at the bottom of my main routes.php file. I did this so that the database wouldn't need to be queried just to figure out what page to display. Here is an example of a route for a page created by this module ...

Route::get('/about-us', ['as' => 'page-1', function(){ return App::make('\App\Http\Controllers\PageController')->callAction('view', [1]); }]);

I am now trying to work in the generation of a sitemap. I created an Artisan command that does the work to actually generate the sitemap. That includes the static pages, the dynamic pages, and the blog posts and is working perfectly.

I was hoping to call this Artisan command when a new page has been created or an existing page has been updated (in order to update the lastmod field in the sitemap). This is where I have run into an issue. The route is written to my custom file and then I call the Artisan command ... on the same page load. Therefore, the new route is not included in the routes that the app is aware of.

Is there a method that I can call from within my controller to make the application reload the routes (and thus include the newly created route) before I make the call to my Artisan command? Or do I need to set up some sort of middleware to handle this? Any help/direction would be greatly appreciated. :)

0 likes
12 replies
ahuggins's avatar

If you just want to do it in the controller use the Aritsan facade to fire of the command you created.

davorminchorov's avatar

Why not just use caching for the database query for pages? I am guessing, these pages will probably won't be changed every day.

hightechredneckwoman's avatar

@ahuggins ... First off, I am using the Artisan facade to fire my command in my controller. The issue is that I need to reload the routes BEFORE the command is run. I tried your suggestion of an Eloquent event, but I run into the same issue ... the newly created route isn't loaded into the app prior to the event being triggered.

@Ruffles ... I have no intention of changing how my Pages module functions. I already did my research and came up with the best solution for my situation.

===

I need to know if it is possible to reload the routes file on-demand ... some way to refresh the routes that are available to the App. Something like Route::refresh() or App::reloadRoutes() or whatever. Some sort of command/function/method that tells the App to load the routes again fresh. If nothing like that exists, then I guess I will have to schedule my generation command and have it run daily.

ahuggins's avatar

can you provide more info on the issue? It is not clear to me what the issue is.

You are adding a new page via a gui, you submit the new page, the page is created, then you are redirected somewhere...when are you needing the routes refreshed? And why is it not currently working?

ahuggins's avatar

What is in your custom file? and what does your Artisan command do?

hightechredneckwoman's avatar

I thought I laid everything out pretty well in my initial message. But here is some clarification ...

The store() method in my controller is where I am doing everything ... creating the page, writing the route to my custom file, and running the command to generate the sitemap. Here is a stripped down version of my method ...

public function store(SavePageRequest $request)
{
    // get the form values
    $form_values = $request->all();

    // [ ... code snipped .. ]
    
    // create the page
    $page = Page::create($form_values);

    // [ ... code snipped .. ]

    // cache a route for this page
    PageHelper::cache_route($page);

    // update the sitemap
    Artisan::call('sitemap:generate');

    // redirect to the edit interface and display a success message
    return Redirect::route('admin::pages.edit', ['page' => $page->id])->withSuccess('The page has successfully been created.');
}

The PageHelper::cache_route($page) call is writing the route (refer to my original post) to a file ... app/Http/custom-routes.php. I'm not caching the route in the sense that the app is caching data. I'm writing the route to an actual php file. This file is included at the bottom of my app/Http/routes.php file ...

include 'custom-routes.php';

Since this route is written after the routes are originally loaded into the application, when my Artisan command is called, it isn't aware of the route and thus blows up.

I need to add something between the "caching" of the route and the updating of my sitemap that will tell the application to reload the routes.php file (into memory) so that it knows about the new route when the sitemap is generated.

As you can see in my original message, I give my routes names. I am using those names to get the routes to use in my sitemap. That is why the application needs to know about the route before my Artisan command is called.

willvincent's avatar

What if your artisan command loads and evals the custom routes file directly? Maybe a little bit hacky, but might solve the problem.

Or, since you're already generating the route to add it to a file, could you not just manually call the Route::get() method too?

hightechredneckwoman's avatar

After digging around in the code, I'm pretty sure it isn't possible to reload routes on-demand. So I'm going to take a different approach. I'm going to try something with middleware.

martinbean's avatar

@hightechredneckwoman I’d suggest using dynamic routing instead of trying to write routes to a file. It’s a fragile approach and just takes something going slightly wrong (file being locked for writing, only partially written) to break your application.

At the bottom of your routes/web.php file, add a catch-all route like this:

Route::get('/{slug}', [PageController::class, 'show'])->where('slug', '.*');

You can then use this controller to view the requested page:

class PageController extends Controller
{
    public function show($slug)
    {
        $page = Page::query()->where('slug', '=', $slug)->firstOrFail();

        return view('page.show', compact('page'));
    }
}
hightechredneckwoman's avatar

@martinbean Thank you, but I'm not looking to change how I have things set up. Things work for how I need them to work. I didn't decide on this method without a lot of research and testing of options. I am already using dynamic routes. Plus ... my URLs have more than just a slug in them depending on how they are nested.

hightechredneckwoman's avatar
Level 1

My middleware approach is working like a charm.

I added Eloquent events to my AppServiceProvider for the models that I need to watch (Pages and Posts). If either is saved or deleted, it sets a session flash variable that indicates the sitemap needs to be updated.

My middleware checks for that session flash variable, and if it is set, it calls the Artisan command to update the sitemap.

I kinda wish I had thought of this solution from the start. :D

1 like

Please or to participate in this conversation.