Home > OS >  Laravel working with a new instance from a custom request ignores validators
Laravel working with a new instance from a custom request ignores validators

Time:07-22

I'm trying to build a custom Artisan command with Laravel, this one can create a product by custom request and pass it to the controller to ensure that validators work fine. on the web, everything works fine, but when I run my custom command with an EMPTY field, the validators don't work and I get the integrity constraint violation error.

This is my custom command code:

<?php

namespace App\Console\Commands;

use App\Http\Controllers\Api\ProductController;
use App\Http\Requests\ProductRequest;
use App\Repositories\Interfaces\ProductRepositoryInterface;
use Illuminate\Console\Command;

class CreateProduct extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'product:create';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Create new product';

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        $productName = $this->ask("Product name");
        $productDescription = $this->ask("Product description");
        $productPrice = $this->ask("Product price");
        $productImageUrl = $this->ask("Product image url");

//        $productRequest = new ProductRequest([
//            "name" => $productName,
//            "description" => $productDescription,
//            "price" => $productPrice,
//            "image" => $productImageUrl
//        ]);
//        $productRequest->setMethod('POST');
//        $productRequest->replace(['foo' => 'bar']);

        $productController = app()->make(ProductController::class);
        $response = $productController->store(
            new ProductRequest([
                "name" => $productName,
                "description" => $productDescription,
                "price" => $productPrice,
                "image" => $productImageUrl
            ])
        );
        $this->info($response);
        return 0;
    }
}

This my custom request code:

<?php

namespace App\Http\Requests;

use Illuminate\Contracts\Validation\Validator;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\Exceptions\HttpResponseException;

class ProductRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array<string, mixed>
     */
    public function rules()
    {
        return [
            "name" => "required|string|min:3|max:255",
            "description" => "required",
            "price" => "required",
            "image" => "required|string|max:2048",
            'categories.*' => "exists:categories,id"
        ];
    }

    public function failedValidation(Validator $validator)
    {
        throw new HttpResponseException(response()->json([
            'success'   => false,
            'message'   => 'Validation errors',
            'data'      => $validator->errors()
        ]));
    }

    public function messages()
    {
        return [
            "name.required" => "The name of product is mandatory",
            "description.required" => "The description of product is mandatory",
            "price.required" => "The price of product is mandatory",
            "image.required" => "The image of product is mandatory",
        ];
    }
}

Controller:

<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Http\Requests\ProductRequest;
use App\Repositories\Interfaces\ProductRepositoryInterface;
use Illuminate\Http\Request;

class ProductController extends Controller
{
    private $productRepository;

    public function __construct(ProductRepositoryInterface $productRepository)
    {
        $this->productRepository = $productRepository;
    }

    public function index()
    {
        $products = $this->productRepository->all();
        return response()->json($products);
    }


    public function create()
    {
        //
    }


    public function store(ProductRequest $productRequest)
    {
        $product = $this->productRepository->create($productRequest->only(['name', 'description', 'price', 'image', 'categories']));
        return response()->json($product);
    }


    public function show($id)
    {
        //
    }


    public function edit($id)
    {
        //
    }


    public function update(ProductRequest $request, $id)
    {
        $productUpdated = $this->productRepository->update($request->only(['name', 'description', 'price', 'image']), $id);
        return response()->json($productUpdated);
    }


    public function destroy($id)
    {
        $deleted = $this->productRepository->delete($id);
        return response()->json([$deleted]);
    }

    public function filterProductsByCategory($id) {
        $products = $this->productRepository->filterProductsByCategory($id);
        return response()->json($products);
    }
}

CodePudding user response:

You can merge data into the Request that is bound to the Container, which is what gets used to create the FormRequest from:

$request = app('request');
$request->replace(['your' => 'data', ...]); // or merge([...])

Then you can ask the Container for an instance of your FormRequest (which it does things to when resolving to fill it and validate it):

$productController->store(app(YourRequestClass::class));

Ideally you would not be calling a Controller like this, instead using the Container to call the method for you, so it will do dependency injection for you:

$response = app()->call('YourController@method');

Though you should really refactor this code out of the Controller and put it somewhere that your Controller and Command can both makes call to it.

  • Related