Norgul
Norgul

Reputation: 4783

Transform Request to custom validation request

Is it possible to transform Illuminate\Http\Request to custom validation request you made with php artisan make:request MyRequest?

I would like validation to take place in a method down the road so that I have:

protected function register(Request $request)
{
   ...
   $this->userRepository->signup($request)
   ...
}

User repository:

public function signup(MyRequest $request)
{
    ...
}

Is this possible? I am getting an error now because one class is expected. Only thing that comes to mind is to make an interface, but I'm not sure if that could function.

Error I get

Type error: Argument 1 passed to UserRepository::signup() must be an instance of App\Http\Requests\MyRequest, instance of Illuminate\Http\Request given

Upvotes: 2

Views: 2710

Answers (5)

saud
saud

Reputation: 1

I know this is an old topic, but I will solve this problem so that other friends who come across this issue know how it can be solved.

protected function register(Request $request)
{
$request = MyRequest ::createFrom($request);
$this->userRepository->signup($request)
}

User repository:

public function signup(MyRequest $request)
{
$this->validate($request, $request->rules(),$request->messages()); 
    ...
}

Upvotes: 0

Manyang
Manyang

Reputation: 180

like @dbf answer but with automatic validation

use App\Http\Requests\MyRequest;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
public function someControllerMethod(Request $Request) {

   //this make your request class do validation
   try{
        app(MyRequest::class);
    } catch (ValidationException $ex){
        throw $ex;
    }

   //if no error you can continue to convert the request
   $MyRequest = MyRequest::createFrom($Request);
   // .. or
   $MyRequest = MyRequest::createFromBase($Request);
}

Upvotes: 1

dbf
dbf

Reputation: 3463

Well, you can convert any request to any other request, as long as it extends from Illuminate\Http\Request.

There are basically two methods Laravel uses to convert one request to another. The problem here is that it will never get a validation object or trigger the validation automatically, as part of the injection when MyRequest was passed as an argument. It might also miss a message bag and the error handler, but nothing you can fix by initializing the request just like Laravel does when injecting it.

So you still have to trigger all the sequences the FormRequest (if it extends FromRequest rather than Request) normally does when booting the trait, but still, it's entirely possible and with some little extra work, you could convert any request to any other request.

For example; I'm using this setup to call just one route profile/{section}/save for saving my profile settings. Depending on $section's value, I convert the given $Request to any of my custom form requests for that particular $section.


use App\Http\Requests\MyRequest;
use Illuminate\Http\Request;

...

public function someControllerMethod(Request $Request) {
   $MyRequest = MyRequest::createFrom($Request);
   // .. or
   $MyRequest = MyRequest::createFromBase($Request);
}

...

So to get people started with using a FormRequest as an example, it basically comes to this.

Instead of extending all your custom requests from the default Illuminate\Foundation\Http\FormRequest, use a base class which extends from FormRequest and add a custom method to transform and boot the request as if it were passed as an argument.

namespace App\Http\Requests;

use Illuminate\Routing\Redirector;
use Illuminate\Foundation\Http\FormRequest;

class BaseFormRequest extends FormRequest {
   public function convertRequest(string $request_class) : BaseFormRequest {
      $Request = $request_class::createFrom($this);

      $app = app();
      $Request
        ->setContainer($app)
        ->setRedirector($app->make(Redirector::class));

      $Request->prepareForValidation();
      $Request->getValidatorInstance();

      return $Request;
   }

    public function authorize() {
        return true;
    }

    public function rules() {
        return [];
    }
}

Let all your custom FormRequest extend your BaseFormRequest

namespace App\Http\Requests;

class MyRequest extends BaseFormRequest {
  ...
}

Now anywhere you want to convert a request, use the base class in your controller method and convert it using convertRequest with the custom request class you wish to convert.

public function someControllerMethod(BaseFormRequest $Request) {
  $MyRequest = $Request->convertRequest(MyRequest::class);
}

Upvotes: 3

Norgul
Norgul

Reputation: 4783

I didn't find it was possible to do what I wanted even with my custom class extending the Request because naturally one method expects an instance of one class while getting another one.

Maybe it would be possible to extract an interface out and wrap and bind it but that would be in my opinion a quickfix.

My opinion is that concept I had was wrong from the start, and it was more of an architecture problem so I transformed my app to a different approach and manage to avoid such issues in the first place.

Upvotes: 0

Marcin Nabiałek
Marcin Nabiałek

Reputation: 111859

Yes, there is no problem with that, you should create:

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class MyRequest extends FormRequest
{
   public function rules() {
       // here you put rules
   }
}

and in your controller:

public function signup(\App\Http\Requests\MyRequest $request)
{
    ...
}

Be aware you should also adjust authorize method in your request class (to return true or false depending on user access)

EDIT

After update - you should type hint your custom class in controller and in repository - it's up to you - I often use generic Illuminate\Http\Request, so you should do:

in controller:

public function controllerMethod(\App\Http\Requests\MyRequest $request)

in repository:

public function signup(\App\Http\Requests\MyRequest $request)

or

public function signup(\Illuminate\Http\Request $request)

So to sum up you should use Form request classes in controller - this is the place where validation will be made and later you can use either same class or generic \Illuminate\Http\Request - I personally often use in repositories or services just \Illuminate\Http\Request because they usually don't care about other things put into MyRequest class - they just want to get data from request class and that's it.

Upvotes: 0

Related Questions