@lexstile

Как провалидировать список на laravel в кастомном правиле?

Нужно проверить 2 кейса:
1. Что блюдо принадлежит тому меню, которое принадлежит текущему проекту
2. Сумма всех блюд должна находиться в диапазоне 1-150000.

Кажется, что что-то я делаю не так.
1. $fail не работает внутри коллекции, я мог бы это исправить переведя на обычный foreach или установить флаг на уровне ф-ции __invoke и менять его, в случае ошибки и проверять уже за пределами коллекции (но кажется, что что-то не то).
2. Стоит ли мешать 2 подобные проверки в одном месте?
<?php

namespace App\Rules;

use App\Models\Dishe;
use App\Models\Order;
use App\Models\Project;
use Illuminate\Contracts\Validation\InvokableRule;
use Illuminate\Support\Arr;

class DishesRule implements InvokableRule
{
    private Project $project;
    private ?Order $order;

    public function __construct (Project $project, Order $order = null) {
        $this->project = $project;
        $this->order = $order;
    }

    public function __invoke($attribute, $value, $fail)
    {
        $dishesRequest = collect($value);

        $dishesRequest->each(function($dishe) use($fail) {
            $disheId = Arr::get($dishe, 'id');

            # проверяем принадлежность блюда и меню к одному проекту
            $exists = $disheId && Dishe::where('id', $disheId)->Active()->whereHas('menu', function ($query) {
                return $query->where('project_id', $this->project->id)->Active();
            })->exists();

            if (!$exists) {
                return $fail(__('validation.exists'));
            }
        });

        # Получаем список блюд по массиву идентификаторов
        $dishes = Dishe::whereIn('id', $dishesRequest->pluck('id'))->get();
        $totalDishes = $dishes->sum('price');
        $total = $this->order ? $totalDishes + $this->order->totalPrice : $totalDishes;

        if ($total < 1 || $total > 150000) {
            return $fail('Сумма заказа должна быть от 1 ₽ до 150 000 ₽');
        }
    }
}

# rules

    public function rules()
    {
        return [
            'table_id' => [
                'required',
                'numeric',
                Rule::unique('orders')->where(function ($query) {
                    # проверка доступности стола (нет ли на нем заказов в указанных статусах)
                    return $query->where('table_id', $this->table_id)->whereIn('status', [Order::NEW, Order::WORK]);
                }),
            ],
            'dishes' => ['required', 'array', 'min:1', new DishesRule($this->table->project)],
            'dishes.*.id' => ['required', 'numeric'],
            'dishes.*.quantity' => ['required', 'numeric', 'digits_between:1,99'],
            'dishes.*.comment' => ['nullable', 'string', 'min:1', 'max:255'],
        ];
    }
  • Вопрос задан
  • 76 просмотров
Решения вопроса 1
iMedved2009
@iMedved2009
Не люблю людей
public function __invoke($attribute, $value, $fail)
    {
        $dishesRequest = collect($value);
        $data = Dishe::whereIn('id', $dishesRequest)->Active()->whereHas('menu', function ($query) {
                return $query->where('project_id', $this->project->id)->Active();
        })->selectRaw('count(1) as count, sum(price) as sum');
        if($data->count !=  $dishesRequest->count()){
                return $fail(__('validation.exists'));
        }
        # Получаем список блюд по массиву идентификаторов
        $totalDishes = $data->sum;
        $total = $this->order ? $totalDishes + $this->order->totalPrice : $totalDishes;

        if ($total < 1 || $total > 150000) {
            return $fail('Сумма заказа должна быть от 1 ₽ до 150 000 ₽');
        }
    }


Но вторую проверку я бы наверное вынес. Но не уверен
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы