Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel: How to set globally available default route parameters

Tags:

php

laravel

I'm trying to set a handful of default route parameters that will work globally in my application regardless of context. In the documentation for URL generation the example given is using middleware which is fine for HTTP, but won't get called during non-HTTP contexts. I also need this to work when called from the CLI.

My first idea is to have a Service Provider that calls the defaults method on boot:

<?php

namespace App\Providers;

use Illuminate\Routing\UrlGenerator;
use Illuminate\Support\ServiceProvider;

class UrlDefaults extends ServiceProvider
{
    public function boot(UrlGenerator $urlGenerator): void
    {
        $urlGenerator->defaults([
            'foo' => 'abc',
            'bar' => 'xyz',
        ]);
    }
}

But this does not work for HTTP requests:

Route::get('test', function (\Illuminate\Routing\UrlGenerator $urlGenerator) {
    dump($urlGenerator->getDefaultParameters());
});

Outputs []

I believe this is because in the UrlGenerator, the setRequest method unconditionally sets the routeGenerator property to null. My Service Provider's boot method is called during the bootstrapping process, but then the request is set afterwards clobbering my defaults.

//Illuminate/Routing/UrlGenerator.php

    public function setRequest(Request $request)
    {
        $this->request = $request;

        $this->cachedRoot = null;
        $this->cachedSchema = null;
        $this->routeGenerator = null;
    }

Dumping the UrlGenerator during boot and then again in my routes file can demonstrate this:

UrlGenerator

As you can see, the UrlGenerator instance is the same both times, but the RouteUrlGenerator on the routeGenerator property has changed.

I am unsure of a better way to set these defaults.

like image 550
Vusys Avatar asked Jan 16 '19 13:01

Vusys


1 Answers

Not sure why this is getting attention almost a year later, but I ended up finding a solution by myself.

To add a bit more information to the original question, the purpose of this was to allow us to have the same instance of the code powering both our live and sandbox application. There's more involved to get this working, but this issue was just about URL generation for links in views. All links generated always both a subdomain and tld, so this code injects these values always.

These views are rendered both as a response to a HTTP request, e.g. in our client areas, but also as part of a non HTTP request, e.g. a scheduled task generating invoices and emailing them to clients.

Anyway, the solution:

For non HTTP contexts, a service provider can set the defaults:

<?php namespace App\Providers;

use App\Support\UrlDefaults;
use Illuminate\Routing\UrlGenerator;
use Illuminate\Support\ServiceProvider;

class UrlDefaultsServiceProvider extends ServiceProvider
{
    public function boot(UrlGenerator $urlGenerator): void
    {
        $urlGenerator->defaults(UrlDefaults::getDefaults());
    }
}

Since the there's no routing going on to cause the problem I asked originally, this just works.

For HTTP contexts, the RouteMatched event is listened for and the defaults injected then:

<?php namespace App\Listeners;
use App\Support\UrlDefaults;
use Illuminate\Routing\Router;
use Illuminate\Routing\UrlGenerator;

/**
 * Class SetUrlDefaults
 *
 * This class listeners for the RouteMatched event, and when it fires, injects the route paramaters (subdomain, tld,
 * etc) into the defaults of the UrlGenerator
 *
 * @package App\Listeners
 */
class SetUrlDefaults
{
    private $urlGenerator;
    private $router;

    public function __construct(UrlGenerator $urlGenerator, Router $router)
    {
        $this->urlGenerator = $urlGenerator;
        $this->router       = $router;
    }

    public function handle(): void
    {
        $paramaters = array_merge(UrlDefaults::getDefaults(), $this->router->current()->parameters);
        $this->urlGenerator->defaults($paramaters);
    }
}

UrlDefaults is just a simple class that returns an array:

<?php namespace App\Support;

class UrlDefaults
{
    public static function getDefaults(): array
    {
        return [
            'tld' => config('app.url.tld'),
            'api' => config('app.url.api'),
            'foo' => config('app.url.foo'),
            'bar' => config('app.url.bar'),
        ];
    }
}
like image 121
Vusys Avatar answered Sep 20 '22 12:09

Vusys