Tim Schmidt
Tim Schmidt

Reputation: 684

Laravel: How to change UrlGenerator (Core Binding) against custom Implementation?

I need to use a custom Implementation of UrlGenerator. So how can I change the default binding of laravel, that is implemented somewhere deep in the core as

 'url'                  => ['Illuminate\Routing\UrlGenerator', 'Illuminate\Contracts\Routing\UrlGenerator'],

against my own implementation?

Furthermore I am not shure. I assume this line above does actually two things. it will store the bindinung under the key "url" and it will also do the mapping of the Interface to the class. So I actually need to override both! How to do that? Furthemore how to find out if this must be bound as "shared"(singleton) or "new instance every time"?

Thanks very much!

Upvotes: 6

Views: 5608

Answers (3)

Daniel Katz
Daniel Katz

Reputation: 2408

I tried the Kosta's approach but it didn't fully work for me because it somehow created an endless recursion loop in the framework. Nonetheless, I ended up with this code:

namespace App\Providers;

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

class UrlGeneratorServiceProvider extends ServiceProvider
{
    public function register()
    {
        $this->app->singleton("url", function($app) {
            $routes = $app['router']->getRoutes();
            return new UrlGenerator(  // this is actually my class due to the namespace above
                $routes, $app->rebinding(
                    'request', $this->requestRebinder()
                ), $app['config']['app.asset_url']
            );
        });
    }

    protected function requestRebinder()
    {
        return function ($app, $request) {
            $app['url']->setRequest($request);
        };
    }
}

And of course, registered the above provider in config/app.php under 'providers'

Upvotes: 3

Kosta
Kosta

Reputation: 1867

I did what Nestor said in his answer, but it didn't quite work for me. So this is what I did to make it work. Inside my service provider in method register I first tried this:

$this->app->bind('url', MyCustomProvider::class);

This did register my URL provider instead of the default one. The problem was that now my provider didn't have any access to routes. I checked the Laravel code for \Illuminate\Routing\RoutingServiceProvider because it has a method registerUrlGenerator for registering the URL provider. This method did a direct instantiation of the Laravel URL generator Illuminate\Routing\UrlGenerator and giving proper parameters in the constructor.

So, I did the same in my service provider. Instead of doing $this->app->bind I did $this->app->singleton('url', function ($app) { ... }) and provided basically the same code in the closure function as in RoutingServiceProvider::registerUrlGenerator but created the instance of my URL generator. This then worked properly, and my generator is now called every time. The final code was this:

// the code is copied from the \Illuminate\Routing\RoutingServiceProvider::registerUrlGenerator() method
$this->app->singleton('url', function ($app) {
    /** @var \Illuminate\Foundation\Application $app */
    $routes = $app['router']->getRoutes();

    $app->instance('routes', $routes);

    // *** THIS IS THE MAIN DIFFERENCE ***
    $url = new \My\Specific\UrlGenerator(
        $routes,
        $app->rebinding(
            'request',
            static function ($app, $request) {
                $app['url']->setRequest($request);
            }
        ),
        $app['config']['app.asset_url']
    );

    $url->setSessionResolver(function () {
        return $this->app['session'] ?? null;
    });

    $url->setKeyResolver(function () {
        return $this->app->make('config')->get('app.key');
    });

    $app->rebinding('routes', static function ($app, $routes) {
        $app['url']->setRoutes($routes);
    });

    return $url;
});

I hate copying the code, so it seems to me that the problem is in the base implementation. It should take the correct contract for URL generator instead of making direct instantiation of a base class.

Upvotes: 7

Nestor Mata Cuthbert
Nestor Mata Cuthbert

Reputation: 1359

Take a look at the Service Container guide http://laravel.com/docs/5.1/container

In this specific case I think all you need to do is to tell the app to replace the alias that already exists.

To do that I would recommend creating a ServiceProvider, registering int the config/app.php file and inside that one in the register method put something like:

$this->app->bind('Illuminate\Routing\UrlGenerator', 'yourownclasshere');

Let us know if it works.

Update: I removed the option that didn't work and left only the one that worked.

Upvotes: 7

Related Questions