D.R.
D.R.

Reputation: 2868

Laravel 5.7. Route model binding validation

I'd like to use sweet Laravel model binding and then run some complex validation checks on the model itself.

Something like

Route::post('/do-something/{something}', 'SomeController@store');

and

$request->validate([
    'something' => [
        new MyFirstVeryComplexRule,
        new MySecondVeryComplexRule,
        new MyThirdVeryComplexRule,
        //...
    ],
]);

I assume, that $value passed to each rule will be an instance of App\Something class.

Is is possible to achieve that?

The closest option I can think of is to pass id of a model and then run App\Some::find($value) in each rule instance, but this kills the performance and is not scalable.

Answer

No, this is not possible because of x,y,z, try a,b,c

will also be accepted.

Upvotes: 0

Views: 2149

Answers (2)

Joel Mellon
Joel Mellon

Reputation: 3855

From Laravel's Routing documentation:

Alternatively, you may override the resolveRouteBinding method on your Eloquent model. This method will receive the value of the URI segment and should return the instance of the class that should be injected into the route:

class YourModel {
    public function resolveRouteBinding($value, $field = null)
    {
        return $this->where('name', $value)->firstOrFail();
    }
}

You can use this just like @Chin Leung does:

class YourModel {
    public function resolveRouteBinding($value, $field = null)
    {
        $instance = Model::find($value) ?? abort(404);

        $validator = Validator::make([
            'something' => $instance,
        ], [
            'something' => [
                new MyFirstVeryComplexRule,
                new MySecondVeryComplexRule,
                new MyThirdVeryComplexRule
            ]
        ]);

        if ($validator->errors()->any()) {
            // perform on error tasks
        }

        return $instance;
    }
}

Upvotes: 0

Chin Leung
Chin Leung

Reputation: 14921

You could create a custom binding in your RouteServiceProvider like this:

public function boot()
{
    parent::boot();

    Route::bind('something', function ($value) {
        $instance = Model::find($value) ?? abort(404);

        $validator = Validator::make([
            'something' => $instance,
        ], [
            'something' => [
                new MyFirstVeryComplexRule,
                new MySecondVeryComplexRule,
                new MyThirdVeryComplexRule
            ]
        ]);

        if ($validator->errors()->any()) {
            // perform on error tasks
        }

        return $instance;
    });
}

Then the $value of each rule will receive the instance of your Model.

For more information, you can take a look at Customizing The Resolution Logic under Route Model Binding: https://laravel.com/docs/5.8/routing#route-model-binding

Upvotes: 1

Related Questions