skip to Main Content

I have this code:

class BunnyUpdateRequest extends FormRequest
{
    public function authorize(): bool
    {
        return true;
    }

    public function rules(): array
    {
        return [
            'id' => ['required', 'min:1'],
            'status' => ['required', Rule::enum(BunnyStatus::class)]
        ];
    }

    public function validationData(){
        return array_merge($this->all(), [
            'id' => $this->route()->parameter('bunny_id'),
            'status' => $this->route()->parameter('status')
        ]);
    }
}

class BunnyController extends Controller
{
    public function update(BunnyUpdateRequest $bunnyUpdateRequest)
    {
        $fields = $bunnyUpdateRequest->validated();
        $bunny = Bunny::findOrFail($fields['id']);
        if ($bunny->status === BunnyStatus::Done)
            return Response::json(null, 422);
        $bunny->status = $fields['status'];
        $bunny->save();
        return Response::json(null, 200);
    }
}

Normally I would use $request->input to get the validated body input field or query param. As far as I understand validation runs in this case. At least I hope so. The upper code gets path params which are empty if I try to get the same way. This is why I used the validated() array. I don’t like this approach, because I find the code ugly. Is there a way to get these path params as object properties on the request object just as body input fields and query params? Is there a way to get a Bunny instance instead of the id with request model binding somehow?

2

Answers


  1. Chosen as BEST ANSWER

    I have a solution:

    class BunnyUpdateRequest extends FormRequest
    {
        public function authorize(): bool
        {
            return true;
        }
    
        public function rules(): array
        {
            return [
                'bunny' => ['required'],
                'status' => ['required', Rule::enum(BunnyStatus::class)]
            ];
        }
    
        protected function prepareForValidation(): void {
            $this->merge([
                'bunny' => Bunny::findOrFail($this->route()->parameter('bunny_id')),
                'status' => $this->route()->parameter('status')
            ]);
        }
    }
    
    class BunnyController extends Controller
    {
        public function update(BunnyUpdateRequest $request)
        {
            $bunny = $request->bunny;
            if ($bunny->status === BunnyStatus::Done)
                return Response::json(null, 422);
            $bunny->status = $request->status;
            $bunny->save();
            return Response::json(null, 200);
        }
    }
    

    Instead of using the validationData method I use the prepareForValidation to merge the path params. I movee the findOrFail to the request. It gives 404 for a non existent bunny this way too.


  2. 1st of all the answer you provided is good one, from the conventional point of view i.e. you fetched the two parameters from the URL then validated (using custom Form Request) and updated the records if BunnyStatus not equal to DONE.

    But Laravel itself provides some in built features with which you can do the same operation with lesser lines of code as well as without custom Form Request.

    That is the reason I am writing this answer which will provide another perspective to do the same and also help you to manage 404 error automatically.

    Change Your Route defination like this.

    use AppHttpControllersBunnyController;
    use AppModelsBunny;
    use AppEnumsBunnyStatus;
    use IlluminateHttpRequest;
    use IlluminateSupportFacadesRedirect;
    
    Route::post('bunnies/{bunny}/update-status/{status}', [BunnyController::class, 'update'])
        ->missing(function (Request $request) {
            // Redirect to custom error page or wherever you want your user to redirect if if an implicitly bound `model/Enum` is not found.
            return Redirect::route('404'); 
        });
    

    Above I have implemented implicit model binding , implicit enum binding along with missing model/enum Behaviour.That is the reason you will not need custom form request because basic-validation will be done there.

    Then in the Controller do this. This will by default fetch & injected to the update() method the matching model instance of the Bunny model as Bunny $bunny & BunnyStatus $status as Enum passed to the URL after validation that is being done in the Route defination.

        public function update(Bunny $bunny, BunnyStatus $status): 
        {
            // Check if the status is 'DONE' and return a 422 response
            if ($status === BunnyStatus::DONE) {
                return Response::json(null, 422);
            }
    
            // Update the Bunny's status
            $bunny->status = $status->value;
            $bunny->save();
    
            
            return Response::json(null, 200);
        }
    

    Remember to import these class and also the orther required class to the top of the Controller class.

    use AppModelsBunny;
    use AppEnumsBunnyStatus;
    

    Citation:-

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search