Нужно проверить 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'],
        ];
    }