shaedrich
shaedrich

Reputation: 5735

Laravel route resolve custom data type

I have the following routes in routes/api.php:

Route::get('items/{item}', function(Guid $item) {...});
Route::get('users/{user}', function(Guid $user) {...});

Since Guid is a custom type, how can I resolve that via dependency injection? As shown, the route parameter {item} differs from the callback parameter type-hint:Guid so it can not be automatically resolved.


That's what I've tried in app/Providers/AppServiceProvider.php:

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        $this->app->bind(Guid::class, function(Application $app, array $params) {
            return Guid::fromString($params[0]);
        });
    }
}

I'd expect $params to be something like this: [ 'item' => 'guid' ] -- but it is: [].

Upvotes: 0

Views: 1635

Answers (2)

joshuaeasy
joshuaeasy

Reputation: 41

simple helper to utilize route binding callback.

if (!function_exists('resolve_bind')) {
    function resolve_bind(string $key, mixed $value) {
        return call_user_func(Route::getBindingCallback($key), $value);
    }
}

usage

resolve_bind('key', 'value');

Upvotes: 1

Maksim
Maksim

Reputation: 2957

You can make use of explicit binding Laravel Routing:

in RouteServiceProvider::boot():

public function boot()
{
    Route::model('item', Guid $item);
    Route::model('user', Guid $user);
}

If Guid is not a model use a Closure to map onto the string:

Route::bind('user', function ($value) {
   return Guid::fromString($value);
});

UPDATED

And I found another way, much better - implement UrlRoutable contract Lavaravel API:

<?php

namespace App\Models;

use Illuminate\Contracts\Routing\UrlRoutable;

class Guid implements UrlRoutable
{
    private string $guid;

    public function setGuid(string $guid)
    {
        $this->guid = $guid;
        return $this;
    }

    public function getGuid(): string
    {
        return $this->guid;
    }

    public static function fromString(string $guid): self
    {
        //you cannot set props from constructor in this case
        //because binder make new object of this class
        //or you can resolve constructor depts with "give" construction in ServiceProvider
        return (new self)->setGuid($guid);
    }
    public function getRouteKey()
    {
        return $this->guid;
    }

    public function getRouteKeyName()
    {
        return 'guid';
    }

    public function resolveRouteBinding($value, $field = null)
    {
        //for using another "fields" check documentation
        //and maybe another resolving logic
        return self::fromString($value);
    }

    public function resolveChildRouteBinding($childType, $value, $field)
    {
        //or maybe you have relations
        return null;
    }
}

And, with this, you can use routes like you want as Guid now implements UrlRoutable and can turn {item} (or whatever) URL-path sub-string markers into Guids per dependency injection (by the type-hint as you asked for it):

Route::get('items/{item}', function(Guid $item) {
   return $item->getGuid();
});

BTW: NEVER EVER use closures in routes as you cannot cache closure routes - and routes are good to be optimized, and caching helps with that in Laravel routing.

Upvotes: 2

Related Questions