ldc-ovoteam/app/Http/Requests/Back/Recipes/StoreRecipeRequest.php
Jérémy Munsch ca9c0fbc4e
feat(policy): allow ProductDto and RecipeDto in policies
Updated ProductPolicy and RecipePolicy to accept ProductDto in addition to Product. This change enhances the flexibility of the policies by allowing DTOs to be used for authorization checks.

refactor(provider): simplify service provider bindings

Refactored AppServiceProvider to simplify the binding of ProductDirectory and RecipeDirectory. Removed unnecessary dependencies and improved clarity.

feat(route): update route bindings for front-end

Changed route bindings for products and recipes to use frontProduct and frontRecipe. This improves clarity and consistency in route handling.

fix(breadcrumbs): update breadcrumb checks for DTOs

Updated breadcrumb generation to check for ProductDto and RecipeDto instead of their model counterparts. This ensures breadcrumbs function correctly with the new DTO structure.

refactor(types): remove FilePath type and update schemas

Removed the FilePath type definition and updated related schemas to use string directly. This simplifies type handling for file paths.

style(entities): standardize naming conventions

Updated naming conventions in Product and Recipe entities to use snake_case for consistency across the codebase.

feat(frontend): update product and recipe pages for new DTO structure

Modified front-end components to accommodate changes in the product and recipe data structures, ensuring compatibility with the new DTOs.
2026-11-05 16:49:42 +01:00

157 lines
6.9 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Http\Requests\Back\Recipes;
use App\Cqs\Actions\Recipes\Create\CreateRecipeInput;
use App\Lib\PublicS3Disk;
use App\Rules\MaxStrippedLength;
use App\Services\AdminAuthService;
use Domain\Models\ValueObjects\Products\ProductCategory;
use Domain\Models\ValueObjects\Recipes\ConsumptionTime;
use Domain\Models\ValueObjects\Recipes\Inspiration;
use Domain\Models\ValueObjects\Recipes\Season;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\UploadedFile;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Rules\Enum;
use Uwil\Storage\Contracts\StorageService;
use Uwil\Storage\Lib\ValueObjects\StorageFilePath;
class StoreRecipeRequest extends FormRequest
{
protected const int MAX_FILE_SIZE_KB = 5120;
protected const int PICTURE_WIDTH = 800;
protected const int PICTURE_HEIGHT = 800;
public function __construct(
protected readonly AdminAuthService $authService,
protected readonly StorageService $storageService,
protected readonly PublicS3Disk $s3Disk,
) {
}
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return $this->authService->check();
}
public function toCreateRecipeInput(): CreateRecipeInput
{
$picture = null;
if ($this->hasFile('picture')) {
$picture = $this->storeFile($this->file('picture'));
}
return new CreateRecipeInput(
name: $this->string('name')->trim()->value(),
seoTitle: $this->string('seo_title')->trim()->value(),
seoDescription: $this->string('seo_description')->trim()->value(),
productCategory: $this->enum('product_category', ProductCategory::class, ProductCategory::HARD_BOILED_EGGS),
consomptionTime: $this->enum('consomption_time', ConsumptionTime::class, ConsumptionTime::BREAK_FAST_BRUNCH),
inspiration: $this->enum('inspiration', Inspiration::class, Inspiration::FRENCH_COOKING),
season: $this->enum('season', Season::class, Season::ALL_YEAR),
description: $this->filled('description') ? $this->string('description')->trim()->value() : null,
details: $this->filled('details') ? $this->string('details')->trim()->value() : null,
ingredientsList: $this->string('ingredients_list')->trim()->value(),
preparationSteps: $this->string('preparation_steps')->trim()->value(),
tipsAndAdvices: $this->filled('tips_and_advices') ? $this->string('tips_and_advices')->trim()->value() : null,
picture: $picture,
pictureAlt: $this->string('picture_alt')->trim()->value(),
pictureTitle: $this->string('picture_title')->trim()->value(),
products: array_filter($this->array('products'), fn ($product) => is_string($product)),
);
}
protected function storeFile(UploadedFile $file): StorageFilePath
{
return $this->storageService->storeUploadedOntoDisk(
$file,
$this->s3Disk->disk
);
}
/**
* Prepare the data for validation.
*/
protected function prepareForValidation(): void
{
$this->merge([
'name' => $this->string('name')->trim()->value(),
'seo_title' => $this->string('seo_title')->trim()->value(),
'seo_description' => $this->string('seo_description')->trim()->value(),
'picture_alt' => $this->string('picture_alt')->trim()->value(),
'picture_title' => $this->string('picture_title')->trim()->value(),
]);
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Validation\Rule|array<mixed>|string>
*/
public function rules(): array
{
return [
'name' => ['required', 'string', 'min:3', 'max:255'],
'seo_title' => ['required', 'string', 'between:45,70'],
'seo_description' => ['required', 'string', 'between:50,160'],
'product_category' => ['required', new Enum(ProductCategory::class)],
'consomption_time' => ['required', new Enum(ConsumptionTime::class)],
'inspiration' => ['required', new Enum(Inspiration::class)],
'season' => ['required', new Enum(Season::class)],
'description' => ['nullable', 'string', 'min:15', new MaxStrippedLength(800)],
'details' => ['nullable', 'string', 'min:15', new MaxStrippedLength(255)],
'ingredients_list' => ['required', 'string', 'min:15', new MaxStrippedLength(1000)],
'preparation_steps' => ['required', 'string', 'min:15', new MaxStrippedLength(2000)],
'tips_and_advices' => ['nullable', 'string', 'min:15', new MaxStrippedLength(500)],
'picture' => [
'required',
Rule::imageFile()
->max(self::MAX_FILE_SIZE_KB)
->dimensions(
Rule::dimensions()
->width(static::PICTURE_WIDTH)
->height(static::PICTURE_HEIGHT)
),
],
'picture_alt' => ['required', 'string', 'min:4', 'max:75'],
'picture_title' => ['required', 'string', 'min:4', 'max:75'],
'products' => ['nullable', 'array'],
'products.*' => ['string', 'uuid', 'exists:products,id'],
];
}
/**
* Get custom attributes for validator errors.
*
* @return array<string, string>
*/
public function attributes(): array
{
return [
'name' => trans('validation.attributes.name'),
'seo_title' => trans('validation.attributes.seo_title'),
'seo_description' => trans('validation.attributes.seo_description'),
'product_category' => trans('validation.attributes.product_category'),
'consomption_time' => trans('validation.attributes.consomption_time'),
'inspiration' => trans('validation.attributes.inspiration'),
'season' => trans('validation.attributes.season'),
'description' => trans('validation.attributes.description'),
'details' => trans('validation.attributes.details'),
'ingredients_list' => trans('validation.attributes.ingredients_list'),
'preparation_steps' => trans('validation.attributes.preparation_steps'),
'tips_and_advices' => trans('validation.attributes.tips_and_advices'),
'picture' => trans('validation.attributes.picture'),
'picture_alt' => trans('validation.attributes.picture_alt'),
'picture_title' => trans('validation.attributes.picture_title'),
'products' => trans('validation.attributes.associated_products'),
];
}
}