544 lines
24 KiB
PHP
544 lines
24 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Fo;
|
|
|
|
use App\Enums\StaticPageType;
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\Meat;
|
|
use App\Models\Product;
|
|
use App\Models\Range;
|
|
use App\Models\Recipe;
|
|
use App\Models\StaticPage;
|
|
use App\Models\Submeat;
|
|
use App\Models\Subrange;
|
|
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Collection;
|
|
use Illuminate\Support\Facades\Cache;
|
|
|
|
class ProductController extends Controller
|
|
{
|
|
/**
|
|
* Use for pagination,
|
|
* number of product models per page.
|
|
*
|
|
* @var integer
|
|
*/
|
|
private $productModelsPerPage = 8;
|
|
|
|
/**
|
|
* Get products by a specific range into a json response.
|
|
*
|
|
* @param \Illuminate\Contracts\Pagination\LengthAwarePaginator|null $productModelsFiltered
|
|
* @param \Illuminate\Support\Collection<\App\Models\Subrange>|null $subrangeModelsFiltered
|
|
* @param \Illuminate\Support\Collection<\App\Models\Submeat>|null $submeatModelsFiltered
|
|
* @return \Illuminate\Contracts\View\View
|
|
*/
|
|
public function index(
|
|
LengthAwarePaginator $productModelsFiltered = null,
|
|
Collection $subrangeModelsFiltered = null,
|
|
Collection $submeatModelsFiltered = null,
|
|
): \Illuminate\Contracts\View\View {
|
|
|
|
/** @var \Illuminate\Contracts\Pagination\LengthAwarePaginator $productModels */
|
|
$productModels = $productModelsFiltered ?? Product::query()
|
|
->where('published', true)
|
|
->orderBy('slug')
|
|
->paginate($this->productModelsPerPage);
|
|
|
|
$productDiscoverModels = Product::getPublishedDiscovered();
|
|
$staticPageModel = StaticPage::getCachedStaticPage(StaticPageType::products);
|
|
$rangeModels = Range::getPublishedOrderedWithSubranges();
|
|
$subrangeModels = $subrangeModelsFiltered ?? Subrange::getPublishedOrdered();
|
|
$meatModels = Meat::getPublishedOrderedWithSubmeats();
|
|
$submeatModels = $submeatModelsFiltered ?? Submeat::getPublishedOrdered();
|
|
return view('fo.pages.products.index', [
|
|
'viewsPaginate' => $this->getViewsFromPaginateModels($productModels),
|
|
'productDiscoverModels' => $productDiscoverModels,
|
|
'staticPageModel' => $staticPageModel,
|
|
'rangeModels' => $rangeModels,
|
|
'subrangeModels' => $subrangeModels,
|
|
'meatModels' => $meatModels,
|
|
'submeatModels' => $submeatModels,
|
|
'searchPropositions' => $this->getProductModelsTitles(),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Get the view of a specific product.
|
|
*
|
|
* @param string $slug
|
|
* @return \Illuminate\Contracts\View\View
|
|
*/
|
|
public function show(string $slug): \Illuminate\Contracts\View\View
|
|
{
|
|
/** @var \App\Models\Product|null $productModel Current product model */
|
|
$productModel = Product::query()->where('slug', $slug)->first();
|
|
if (is_null($productModel)) {
|
|
abort(404);
|
|
}
|
|
$productModelSibling = $this->getSiblingModels($productModel);
|
|
|
|
$staticPageModel = StaticPage::getCachedStaticPage(StaticPageType::product);
|
|
|
|
/** @var \App\Models\Recipe|null $recipeModel */
|
|
$recipeModel = $productModel->recipes->isNotEmpty()
|
|
? $productModel->recipes()->with(['products', 'consumptionMoment'])->first()
|
|
: Recipe::query()->whereHas('products', function (Builder $q) use ($productModel) {
|
|
$q->when($productModel->subrange, function (Builder $q) use ($productModel) {
|
|
$q->whereHas('subrange', function (Builder $q) use ($productModel) {
|
|
$q->where('id', $productModel->subrange->getKey());
|
|
});
|
|
})->when($productModel->range, function (Builder $q) use ($productModel) {
|
|
$q->whereHas('range', function (Builder $q) use ($productModel) {
|
|
$q->where('id', $productModel->range->getKey());
|
|
});
|
|
});
|
|
})->with(['products', 'consumptionMoment'])->first();
|
|
/** @var \App\Models\Video $videoModel */
|
|
$videoModel = $productModel->videos->sortByDesc('created_at')->first();
|
|
|
|
$nutritionnalValueModel = $productModel->getNutritionnalValue();
|
|
|
|
return view('fo.pages.products.show', [
|
|
'productModel' => $productModel,
|
|
'nutritionnalValueModel' => $nutritionnalValueModel,
|
|
'prevProductModel' => $productModelSibling[0],
|
|
'nextProductModel' => $productModelSibling[1],
|
|
'productModels' => $this->getProductsFiltered($productModel),
|
|
'recipeModels' => $this->getRecipesFiltered($productModel),
|
|
'recipeModel' => $recipeModel,
|
|
'videoModel' => $videoModel,
|
|
'staticPageModel' => $staticPageModel,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Get product models titles for search input propositions.
|
|
*
|
|
* @param \App\Models\Product $productModel
|
|
* @return array
|
|
*/
|
|
public function getSiblingModels(Product $productModel): array
|
|
{
|
|
/** @var string $slug */
|
|
$slug = (Cache::get('productsFilter'))
|
|
? (((is_null($productModel->{Cache::get('productsFilter')}))
|
|
? $productModel->{"sub" . Cache::get('productsFilter')}->{Cache::get('productsFilter')}
|
|
: $productModel->{Cache::get('productsFilter')})->slug)
|
|
: "";
|
|
/** @var \Illuminate\Support\Collection $productModelsList */
|
|
$productModelsList = Product::query()
|
|
->with('range', 'subrange', 'meat', 'submeat')
|
|
->when(in_array(Cache::get('productsFilter'), ['range', 'meat']), function (Builder $q) use ($slug) {
|
|
$q->where('published', true)
|
|
->whereHas(Cache::get('productsFilter'), function (Builder $q) use ($slug) {
|
|
$q->where('published', true)->where('slug', $slug);
|
|
})
|
|
->orWhereHas('sub' . Cache::get('productsFilter'), function (Builder $q) use ($slug) {
|
|
$q->where('published', true)
|
|
->whereHas(Cache::get('productsFilter'), function (Builder $q) use ($slug) {
|
|
$q->where('published', true)->where('slug', $slug);
|
|
});
|
|
});
|
|
})
|
|
->when(in_array(Cache::get('productsFilter'), ['subrange', 'submeat']), function (Builder $q) use ($slug) {
|
|
$q->where('published', true)
|
|
->whereHas(Cache::get('productsFilter'), function (Builder $q) use ($slug) {
|
|
$q->where('published', true)->where('slug', $slug);
|
|
});
|
|
})
|
|
->where('published', true)
|
|
->orderBy('slug')
|
|
->get();
|
|
|
|
$collectionIndex = $productModelsList->search(function ($productModelItem, $key) use ($productModel) {
|
|
if ($productModelItem->getKey() === $productModel->getKey()) {
|
|
return $key;
|
|
};
|
|
});
|
|
|
|
$productModelSibling[0] = (is_null($productModelsList->get($collectionIndex - 1)))
|
|
? $productModelsList->last()
|
|
: $productModelsList->get($collectionIndex - 1);
|
|
$productModelSibling[1] = (is_null($productModelsList->get($collectionIndex + 1)))
|
|
? $productModelsList->first()
|
|
: $productModelsList->get($collectionIndex + 1);
|
|
return $productModelSibling;
|
|
}
|
|
|
|
/**
|
|
* Get product models titles for search input propositions.
|
|
*
|
|
* @param string|null $search
|
|
* @return \Illuminate\Pagination\LengthAwarePaginator
|
|
*/
|
|
public function getProductModelsTitles(string|null $search = null): \Illuminate\Pagination\LengthAwarePaginator
|
|
{
|
|
$query = Product::query();
|
|
return $this->getModelsTitles($query, 'label', $search);
|
|
}
|
|
|
|
/**
|
|
* Get a list of words for the search input propositions.
|
|
*
|
|
* @param \Illuminate\Http\Request $request
|
|
* @return \Illuminate\Http\JsonResponse
|
|
*/
|
|
public function getWordsList(Request $request): \Illuminate\Http\JsonResponse
|
|
{
|
|
return response()->json($this->getProductModelsTitles($request->input('search')));
|
|
}
|
|
|
|
/**
|
|
* Get products by specific words,
|
|
* the response can be a json or a view.
|
|
*
|
|
* @param \Illuminate\Http\Request $request
|
|
* @return \Illuminate\Http\JsonResponse|\Illuminate\Contracts\View\View
|
|
*/
|
|
public function getProductsByWords(Request $request): \Illuminate\Http\JsonResponse|\Illuminate\Contracts\View\View
|
|
{
|
|
/** @var \Illuminate\Database\Eloquent\Builder $productModels */
|
|
$productModels = Product::query()
|
|
->with(['range', 'subrange', 'meat', 'submeat'])
|
|
->where('published', true)
|
|
->orderBy('slug');
|
|
|
|
/** @var string $search Search field */
|
|
$search = $request->input('words');
|
|
if ($search) {
|
|
$this->searchQuery(
|
|
$productModels,
|
|
str_replace(",", " ", $request->input('words')),
|
|
null,
|
|
'label',
|
|
);
|
|
}
|
|
$productModels = $productModels->paginate($this->productModelsPerPage);
|
|
|
|
if ($request->ajax()) {
|
|
return response()->json([
|
|
'viewsPaginate' => $this->getViewsFromPaginateModels($productModels),
|
|
'children' => [],
|
|
]);
|
|
}
|
|
return $this->index($productModels, collect());
|
|
}
|
|
|
|
/**
|
|
* Get products by a specific meat,
|
|
* the response can be a json or a view.
|
|
*
|
|
* @param \Illuminate\Http\Request $request
|
|
* @return \Illuminate\Http\JsonResponse|\Illuminate\Contracts\View\View
|
|
*/
|
|
public function getProductsByRange(
|
|
Request $request
|
|
): \Illuminate\Http\JsonResponse|\Illuminate\Contracts\View\View {
|
|
Cache::put('productsFilter', 'range');
|
|
$rangeModel = Range::query()
|
|
->where('slug', $request->model_slug)
|
|
->where('published', true)
|
|
->first();
|
|
$subrangeModels = Subrange::query()
|
|
->when(!is_null($rangeModel), function (Builder $q) use ($rangeModel) {
|
|
$q->where('range_id', $rangeModel->getKey());
|
|
})
|
|
->where('published', true)
|
|
->orderBy('order')
|
|
->get();
|
|
|
|
$productModels = Product::query()
|
|
->with(['range', 'subrange', 'meat', 'submeat'])
|
|
->where('published', true)
|
|
->when(!is_null($rangeModel), function (Builder $q) use ($rangeModel, $request) {
|
|
$q->whereHas('range', function (Builder $q) use ($rangeModel) {
|
|
$q->where('published', true)->where('id', $rangeModel->getKey());
|
|
})->orWhereHas('subrange', function (Builder $q) use ($rangeModel, $request) {
|
|
if($request->child_slug) {
|
|
$q->where('published', true)->where("slug", $request->child_slug);
|
|
} else {
|
|
$q->where('published', true)->whereIn('id', $rangeModel->subranges->pluck('id')->toArray());
|
|
}
|
|
});
|
|
})
|
|
->where('published', true)
|
|
->orderBy('slug')
|
|
->paginate($this->productModelsPerPage);
|
|
if ($request->ajax()) {
|
|
return response()->json([
|
|
'viewsPaginate' => $this->getViewsFromPaginateModels($productModels),
|
|
'children' => $subrangeModels,
|
|
]);
|
|
}
|
|
|
|
return $this->index($productModels, $subrangeModels);
|
|
}
|
|
|
|
/**
|
|
* Get products by a specific submeat,
|
|
* the response can be a json or a view.
|
|
*
|
|
* @param \Illuminate\Http\Request $request
|
|
* @return \Illuminate\Http\JsonResponse|\Illuminate\Contracts\View\View
|
|
*/
|
|
public function getProductsBySubrange(
|
|
Request $request
|
|
): \Illuminate\Http\JsonResponse|\Illuminate\Contracts\View\View {
|
|
Cache::put('productsFilter', 'subrange');
|
|
$rangeModel = Range::query()
|
|
->where('slug', $request->model_slug)
|
|
->where('published', true)
|
|
->first();
|
|
$subrangeModel = Subrange::query()
|
|
->when(!is_null($rangeModel), function (Builder $q) use ($rangeModel) {
|
|
$q->where('range_id', $rangeModel->getKey());
|
|
})
|
|
->where('slug', $request->child_slug)
|
|
->where('published', true)
|
|
->first();
|
|
$subrangeModels = Subrange::query()
|
|
->when(!is_null($rangeModel), function (Builder $q) use ($rangeModel) {
|
|
$q->where('range_id', $rangeModel->getKey());
|
|
})
|
|
->where('published', true)
|
|
->orderBy('order')
|
|
->get();
|
|
$productModels = Product::query()
|
|
->with(['range', 'subrange', 'meat', 'submeat'])
|
|
->where('published', true)
|
|
->when(!is_null($subrangeModel), function (Builder $q) use ($subrangeModel) {
|
|
$q->whereHas('subrange', function (Builder $q) use ($subrangeModel) {
|
|
$q->where('published', true)->where('id', $subrangeModel->getKey());
|
|
});
|
|
})
|
|
->orderBy('slug')
|
|
->paginate($this->productModelsPerPage);
|
|
if ($request->ajax()) {
|
|
return response()->json([
|
|
'viewsPaginate' => $this->getViewsFromPaginateModels($productModels),
|
|
'children' => [],
|
|
]);
|
|
}
|
|
return $this->index($productModels, $subrangeModels);
|
|
}
|
|
|
|
/**
|
|
* Get products by a specific meat,
|
|
* the response can be a json or a view.
|
|
*
|
|
* @param \Illuminate\Http\Request $request
|
|
* @return \Illuminate\Http\JsonResponse|\Illuminate\Contracts\View\View
|
|
*/
|
|
public function getProductsByMeat(
|
|
Request $request
|
|
): \Illuminate\Http\JsonResponse|\Illuminate\Contracts\View\View {
|
|
Cache::put('productsFilter', 'meat');
|
|
$meatModel = Meat::query()
|
|
->where('slug', $request->model_slug)
|
|
->where('published', true)
|
|
->first();
|
|
$submeatModels = Submeat::query()
|
|
->when(!is_null($meatModel), function (Builder $q) use ($meatModel) {
|
|
$q->where('meat_id', $meatModel->getKey());
|
|
})
|
|
->where('published', true)
|
|
->orderBy('order')
|
|
->get();
|
|
$productModels = Product::query()
|
|
->with(['range', 'subrange', 'meat', 'submeat'])
|
|
->when(!is_null($meatModel), function (Builder $q) use ($meatModel) {
|
|
$q->whereHas('meat', function (Builder $q) use ($meatModel) {
|
|
$q->where('published', true)->where('id', $meatModel->getKey());
|
|
})->orWhereHas('submeat', function (Builder $q) use ($meatModel) {
|
|
$q->where('published', true)->whereIn('id', $meatModel->submeats->pluck('id')->toArray());
|
|
});
|
|
})
|
|
->where('published', true)
|
|
->orderBy('slug')
|
|
->paginate($this->productModelsPerPage);
|
|
if ($request->ajax()) {
|
|
return response()->json([
|
|
'viewsPaginate' => $this->getViewsFromPaginateModels($productModels),
|
|
'children' => $submeatModels,
|
|
]);
|
|
}
|
|
return $this->index($productModels, null, $submeatModels);
|
|
}
|
|
|
|
/**
|
|
* Get products by a specific submeat,
|
|
* the response can be a json or a view.
|
|
*
|
|
* @param \Illuminate\Http\Request $request
|
|
* @return \Illuminate\Http\JsonResponse|\Illuminate\Contracts\View\View
|
|
*/
|
|
public function getProductsBySubmeat(
|
|
Request $request
|
|
): \Illuminate\Http\JsonResponse|\Illuminate\Contracts\View\View {
|
|
Cache::put('productsFilter', 'submeat');
|
|
$meatModel = Meat::query()
|
|
->where('slug', $request->model_slug)
|
|
->where('published', true)
|
|
->first();
|
|
$submeatModel = Submeat::query()
|
|
->when(!is_null($meatModel), function (Builder $q) use ($meatModel) {
|
|
$q->where('meat_id', $meatModel->getKey());
|
|
})
|
|
->where('slug', $request->child_slug)
|
|
->where('published', true)
|
|
->first();
|
|
$submeatModels = Submeat::query()
|
|
->when(!is_null($meatModel), function (Builder $q) use ($meatModel) {
|
|
$q->where('meat_id', $meatModel->getKey());
|
|
})
|
|
->where('published', true)
|
|
->orderBy('order')
|
|
->get();
|
|
$productModels = Product::query()
|
|
->with(['range', 'subrange', 'meat', 'submeat'])
|
|
->where('published', true)
|
|
->when(!is_null($submeatModel), function (Builder $q) use ($submeatModel) {
|
|
$q->whereHas('submeat', function (Builder $q) use ($submeatModel) {
|
|
$q->where('published', true)->where('id', $submeatModel->getKey());
|
|
});
|
|
})
|
|
->orderBy('slug')
|
|
->paginate($this->productModelsPerPage);
|
|
if ($request->ajax()) {
|
|
return response()->json([
|
|
'viewsPaginate' => $this->getViewsFromPaginateModels($productModels),
|
|
'children' => [],
|
|
]);
|
|
}
|
|
return $this->index($productModels, null, $submeatModels);
|
|
}
|
|
|
|
/**
|
|
* Get products by a selected product model,
|
|
* filtered first by product subrange/range,
|
|
* and by meats/submeats.
|
|
*
|
|
* @param \App\Models\Product $productModel
|
|
* @return \Illuminate\Support\Collection
|
|
*/
|
|
public function getProductsFiltered(Product $productModel): \Illuminate\Support\Collection
|
|
{
|
|
/** @var int $nbrMaxProducts Maximum number of products required */
|
|
$nbrMaxProducts = 9;
|
|
|
|
/** @var \Illuminate\Support\Collection<\App\Models\Product> $productModels products by same product */
|
|
$productModels = Product::query()
|
|
->whereNot('id', $productModel->getKey())
|
|
->where('published', true)
|
|
->when(!is_null($productModel->range), function (Builder $q) use ($productModel) {
|
|
$q->whereHas('range', function (Builder $q) use ($productModel) {
|
|
$q->where('published', true)->where('id', $productModel->range->getKey());
|
|
})->orWhereHas('subrange', function (Builder $q) use ($productModel) {
|
|
$q->whereHas('range', function (Builder $q) use ($productModel) {
|
|
$q->where('published', true)->where('id', $productModel->range->getKey());
|
|
});
|
|
});
|
|
})->when(!is_null($productModel->subrange), function (Builder $q) use ($productModel) {
|
|
$q->whereHas('subrange', function (Builder $q) use ($productModel) {
|
|
$q->where('published', true)->where('id', $productModel->subrange->getKey());
|
|
});
|
|
})
|
|
->get();
|
|
|
|
if (count($productModels) < $nbrMaxProducts) {
|
|
$test = Product::query()
|
|
->whereNot('id', $productModel->getKey())
|
|
->where('published', true)
|
|
->when(!is_null($productModel->meat), function (Builder $q) use ($productModel) {
|
|
$q->whereHas('meat', function (Builder $q) use ($productModel) {
|
|
$q->where('published', true)->where('id', $productModel->meat->getKey());
|
|
})->orWhereHas('submeat', function (Builder $q) use ($productModel) {
|
|
$q->whereHas('meat', function (Builder $q) use ($productModel) {
|
|
$q->where('published', true)->where('id', $productModel->meat->getKey());
|
|
});
|
|
});
|
|
})->when(!is_null($productModel->submeat), function (Builder $q) use ($productModel) {
|
|
$q->whereHas('submeat', function (Builder $q) use ($productModel) {
|
|
$q->where('published', true)->where('id', $productModel->submeat->getKey());
|
|
});
|
|
})
|
|
->get();
|
|
}
|
|
return $productModels->slice(0, $nbrMaxProducts);
|
|
}
|
|
|
|
/**
|
|
* Get recipes by a selected product model,
|
|
* filtered first by products associated,
|
|
* by meats/submeats and finally by consumption moment.
|
|
*
|
|
* @param \App\Models\Product $productModel
|
|
* @return \Illuminate\Support\Collection
|
|
*/
|
|
public function getRecipesFiltered(Product $productModel): \Illuminate\Support\Collection
|
|
{
|
|
/** @var int $nbrMaxRecipes Maximum number of recipes required */
|
|
$nbrMaxRecipes = 9;
|
|
|
|
/** @var \Illuminate\Support\Collection<\App\Models\Recipe> $recipeModels Recipes by same product */
|
|
$recipeModels = Recipe::query()
|
|
->with(['products', 'products.meat', 'products.submeat', 'consumptionMoment'])
|
|
->where('published', true)
|
|
->whereHas('products', function (Builder $q) use ($productModel) {
|
|
$q->where('id', $productModel->getKey());
|
|
})
|
|
->get();
|
|
|
|
if (count($recipeModels) < $nbrMaxRecipes) {
|
|
/** @var \Illuminate\Support\Collection<\App\Models\Recipe> $recipeModels Recipes by same product range */
|
|
$recipeModels = $recipeModels->merge(Recipe::query()
|
|
->with(['products', 'products.meat', 'products.submeat', 'consumptionMoment'])
|
|
->where('published', true)
|
|
->whereHas('products', function (Builder $q) use ($productModel) {
|
|
$q->when(!is_null($productModel->range), function (Builder $q) use ($productModel) {
|
|
$q->whereHas('range', function (Builder $q) use ($productModel) {
|
|
$q->where('published', true)->where('id', $productModel->range->getKey());
|
|
})->orWhereHas('subrange', function (Builder $q) use ($productModel) {
|
|
$q->whereHas('range', function (Builder $q) use ($productModel) {
|
|
$q->where('published', true)->where('id', $productModel->range->getKey());
|
|
});
|
|
});
|
|
})->when(!is_null($productModel->subrange), function (Builder $q) use ($productModel) {
|
|
$q->whereHas('subrange', function (Builder $q) use ($productModel) {
|
|
$q->where('published', true)->where('id', $productModel->subrange->getKey());
|
|
});
|
|
});
|
|
})
|
|
->get());
|
|
} //end if
|
|
return $recipeModels->slice(0, $nbrMaxRecipes);
|
|
}
|
|
|
|
/**
|
|
* Get an array of views for each product models given.
|
|
*
|
|
* @param \Illuminate\Contracts\Pagination\LengthAwarePaginator $productModels
|
|
* @return array
|
|
*/
|
|
public function getViewsFromPaginateModels(LengthAwarePaginator $productModels): array
|
|
{
|
|
// @phpstan-ignore-next-line
|
|
return $productModels->through(function ($productModel) {
|
|
return view('fo.partials.product-card', ['model' => $productModel])->render();
|
|
})->toArray();
|
|
}
|
|
|
|
/**
|
|
* Get products by a specific range into a json response.
|
|
*
|
|
* @param \Illuminate\Http\Request $request
|
|
* @return \Illuminate\Http\JsonResponse
|
|
*/
|
|
public function jsonGetProductsViewsByRange(Request $request): \Illuminate\Http\JsonResponse
|
|
{
|
|
return response()->json(Product::getProductsViewsByRange($request->LEGENDSLUG));
|
|
}
|
|
}
|