简体   繁体   中英

Laravel: How to set globally available default route parameters

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:

网址生成器

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.

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, eg in our client areas, but also as part of a non HTTP request, eg 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'),
        ];
    }
}

So digging into the source for routing classes a bit more, there's a defaults() method on the UrlGenerator class, but it's not a singleton, so any defaults you set in a service provider aren't persisted.

I seem to have got it working by setting the defaults in some middleware:

   Route::domain('{domain}')->middleware('route.domain')->group(function () {
    //
  });

  namespace App\Http\Middleware;
  use Illuminate\Contracts\Routing\UrlGenerator;

  class SetRouteDomain
  {
    private $url;

    public function __construct(UrlGenerator $url)
    {
      $this->url = $url;
    }

   public function handle($request, Closure $next)
   {
     $this->url->defaults([
        'domain' => $request->getHost(),
     ]);

    return $next($request);
   }
  }

A possible work around would be to have the route point to a PHP page that has a range of redirects based upon a token you pass. See this Stackoverflow post as an example:

PHP multiple redirects

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM