Updated the UpdateProductRequest and UpdateRecipeRequest to use built-in validation rules for file uploads, removing custom validation methods for pictures and PDFs. This change streamlines the validation process and improves code readability. refactor(helper): remove unused FileStorageHelper Deleted the FileStorageHelper class as it was no longer needed, simplifying the codebase. refactor(toolbox): clean up ToolboxHelper methods Removed unused methods and simplified existing ones in ToolboxHelper to enhance maintainability. feat(models): add translation search method to Product and Recipe Added a new method `whereTranslationLike` to both Product and Recipe models to facilitate searching translations. chore(config): add storage service prefix configuration Introduced a new configuration option for storage service prefix to allow customization of media paths. fix(validation): add file existence validation message Added a French translation for the file existence validation message to improve user feedback. feat(routes): add media route for file access Implemented a new route for accessing media files through a MediaController, enhancing file management capabilities.
224 lines
7.3 KiB
PHP
224 lines
7.3 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Directories;
|
|
|
|
use App\Contracts\Directories\ProductDirectory;
|
|
use App\Dtos\CategoryProducts;
|
|
use App\Dtos\ProductSummary;
|
|
use App\Models\Product;
|
|
use App\Models\Recipe;
|
|
use Domain\Models\Entities\Product as DomainProduct;
|
|
use Domain\Models\ValueObjects\Entities\ProductId;
|
|
use Domain\Models\ValueObjects\Entities\RecipeId;
|
|
use Domain\Models\ValueObjects\Pagination;
|
|
use Domain\Models\ValueObjects\Products\ProductCategory;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
use Illuminate\Pagination\LengthAwarePaginator;
|
|
use Ramsey\Uuid\Uuid;
|
|
use Ramsey\Uuid\UuidInterface;
|
|
|
|
class EloquentProductDirectory implements ProductDirectory
|
|
{
|
|
public function find(ProductId $id): ?DomainProduct
|
|
{
|
|
return Product::query()
|
|
->with(['recipes'])
|
|
->where('id', (string) $id->value)
|
|
->limit(1)
|
|
->get()
|
|
->map(fn (Product $product) => $this->mapToEntity($product))
|
|
->first();
|
|
}
|
|
|
|
public function findBySlug(string $slug): ?DomainProduct
|
|
{
|
|
return Product::query()
|
|
->with(['recipes'])
|
|
->where('slug', $slug)
|
|
->limit(1)
|
|
->get()
|
|
->map(fn (Product $product) => $this->mapToEntity($product))
|
|
->first();
|
|
}
|
|
|
|
public function getAllProductsSummary(): array
|
|
{
|
|
return Product::query()
|
|
->select(['id', 'name', 'picture'])
|
|
->orderBy('name', 'asc')
|
|
->get()
|
|
->map(fn (Product $product) => $this->mapToSummary($product))
|
|
->all();
|
|
}
|
|
|
|
public function getPaginatedProducts(
|
|
Pagination $pagination,
|
|
string $search = '',
|
|
): LengthAwarePaginator {
|
|
$dbQueryCallback = function (Builder $query) {
|
|
/** @var \Illuminate\Database\Eloquent\Builder<Product> $query */
|
|
$query->with(['recipes']);
|
|
};
|
|
|
|
if (! empty($search)) {
|
|
$scout = Product::search($search)->query($dbQueryCallback);
|
|
|
|
/** @var \Illuminate\Pagination\LengthAwarePaginator<array-key,Product> $paginated */
|
|
$paginated = $scout->paginate(
|
|
$pagination->pageSize(),
|
|
'page',
|
|
$pagination->pageNumber()
|
|
);
|
|
|
|
return $paginated->through(fn ($product) => $this->mapToEntity($product));
|
|
}
|
|
|
|
/** @var \Illuminate\Database\Eloquent\Builder<Product> $query */
|
|
$query = Product::query();
|
|
$dbQueryCallback($query);
|
|
|
|
return $query->paginate(
|
|
perPage: $pagination->pageSize(),
|
|
page: $pagination->pageNumber()
|
|
)->through(fn (Product $product) => $this->mapToEntity($product));
|
|
}
|
|
|
|
/**
|
|
* @param \Domain\Models\ValueObjects\Products\BreedingMethod[] $breedingMethods
|
|
* @param \Domain\Models\ValueObjects\Products\Technology[] $technologies
|
|
* @param \Domain\Models\ValueObjects\Products\Business[] $businesses
|
|
*/
|
|
public function getCategoryProducts(
|
|
ProductCategory $category,
|
|
array $breedingMethods = [],
|
|
array $technologies = [],
|
|
array $businesses = [],
|
|
?bool $isEgalim = null,
|
|
string $search = '',
|
|
int $limitPerCategory = 6
|
|
): ?CategoryProducts {
|
|
|
|
$dbQueryCallback = function (Builder $query) use ($category, $breedingMethods, $technologies, $businesses, $isEgalim) {
|
|
/** @var \Illuminate\Database\Eloquent\Builder<Product> $query */
|
|
$query->where('category', $category->value)
|
|
->with(['recipes']);
|
|
|
|
if (! empty($breedingMethods)) {
|
|
$query->where(function ($q) use ($breedingMethods) {
|
|
foreach ($breedingMethods as $method) {
|
|
$q->orWhereJsonContains('breeding_methods', $method->value);
|
|
}
|
|
});
|
|
}
|
|
|
|
if (! empty($technologies)) {
|
|
$techValues = array_map(fn ($t) => $t->value, $technologies);
|
|
$query->whereIn('technology', $techValues);
|
|
}
|
|
|
|
if (! empty($businesses)) {
|
|
$query->where(function ($q) use ($businesses) {
|
|
foreach ($businesses as $business) {
|
|
$q->orWhereJsonContains('businesses', $business->value);
|
|
}
|
|
});
|
|
}
|
|
|
|
if ($isEgalim === true) {
|
|
$query->where('is_egalim', true);
|
|
}
|
|
};
|
|
|
|
$query = Product::query();
|
|
|
|
if (! empty($search)) {
|
|
$scout = Product::search($search);
|
|
// Get all matching IDs from Meilisearch
|
|
$matchingIds = $scout->keys();
|
|
|
|
if ($matchingIds->isEmpty()) {
|
|
return null;
|
|
}
|
|
|
|
$query->whereIn('id', $matchingIds);
|
|
}
|
|
|
|
$dbQueryCallback($query);
|
|
$count = $query->count();
|
|
|
|
if ($count === 0) {
|
|
return null;
|
|
}
|
|
|
|
$products = (clone $query)->limit($limitPerCategory)
|
|
->get()
|
|
->map(fn (Product $product) => $this->mapToEntity($product))
|
|
->all();
|
|
|
|
return new CategoryProducts(
|
|
category: $category,
|
|
total: $count,
|
|
products: $products,
|
|
);
|
|
}
|
|
|
|
private function mapToSummary(Product $product): ProductSummary
|
|
{
|
|
return new ProductSummary(
|
|
id: (string) $product->id,
|
|
name: $product->name,
|
|
picture: $product->picture?->value,
|
|
);
|
|
}
|
|
|
|
private function mapToEntity(Product $product): DomainProduct
|
|
{
|
|
return new DomainProduct(
|
|
id: new ProductId(Uuid::fromString($product->id)),
|
|
name: $product->name,
|
|
slug: $product->slug,
|
|
category: $product->category,
|
|
technology: $product->technology,
|
|
isEgalim: $product->is_egalim,
|
|
breedingMethods: $product->breeding_methods->all(),
|
|
businesses: $product->businesses->all(),
|
|
detailsAndAdvantages: $product->details_and_advantages,
|
|
formats: $product->formats->all(),
|
|
handlingAdvices: $product->handling_advices,
|
|
ingredients: $product->ingredients,
|
|
conservationBeforeOpening: $product->conservation_before_opening,
|
|
description: $product->description,
|
|
seoTitle: $product->seo_title,
|
|
seoDescription: $product->seo_description,
|
|
conservationAfterOpening: $product->conservation_after_opening,
|
|
nutritionalValues: $product->nutritional_values,
|
|
picture: $product->picture->value,
|
|
pictureAlt: $product->picture_alt,
|
|
pictureTitle: $product->picture_title,
|
|
pdfFilePath: $product->pdf?->value,
|
|
associatedRecipes: $product->recipes->map(
|
|
fn (Recipe $recipe): UuidInterface => $recipe->getId()->value
|
|
)->all()
|
|
);
|
|
}
|
|
|
|
public function getRelatedProducts(RecipeId $recipeId, int $limit = 4): array
|
|
{
|
|
$recipe = Recipe::query()
|
|
->where('id', $recipeId->value)
|
|
->first();
|
|
|
|
if (! $recipe) {
|
|
return [];
|
|
}
|
|
|
|
return $recipe->products()
|
|
->with(['recipes'])
|
|
->limit($limit)
|
|
->get()
|
|
->map(fn (Product $product) => $this->mapToEntity($product))
|
|
->all();
|
|
}
|
|
}
|