ldc-legaulois/app/Http/Controllers/Fo/ProductController.php
2024-06-20 12:29:19 +02:00

456 lines
19 KiB
PHP

<?php
namespace App\Http\Controllers\Fo;
use App\Enums\Settings\SettingsType;
use App\Http\Controllers\Controller;
use App\Models\GeneralSetting;
use App\Models\Meat;
use App\Models\Product;
use App\Models\Range;
use App\Models\Recipe;
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;
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::query()->where('published', true)->where('discover', true)->get();
$generalSettingModel = GeneralSetting::query()->where('type', SettingsType::products)->first();
$rangeModels = Range::query()->where('published', true)->with('subranges')->orderBy('slug')->get();
$subrangeModels = $subrangeModelsFiltered ?? Subrange::query()->where('published', true)
->orderBy('slug')->get();
$meatModels = Meat::query()->where('published', true)->with('submeats')->orderBy('slug')->get();
$submeatModels = $submeatModelsFiltered ?? Submeat::query()->where('published', true)
->orderBy('slug')->get();
return view('fo.pages.products.index', [
'viewsPaginate' => $this->getViewsFromPaginateModels($productModels),
'productDiscoverModels' => $productDiscoverModels,
'generalSettingModel' => $generalSettingModel,
'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);
}
/** @var \App\Models\GeneralSetting|null $generalSettingModel Current general setting */
$generalSettingModel = GeneralSetting::query()->where('type', SettingsType::product)->first();
return view('fo.pages.products.show', [
'productModel' => $productModel,
'productModels' => $this->getProductsFiltered($productModel),
'recipeModels' => $this->getRecipesFiltered($productModel),
'generalSettingModel' => $generalSettingModel,
]);
}
/**
* 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 {
$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('slug')
->get();
$productModels = Product::query()
->with(['range', 'subrange', 'meat', 'submeat'])
->where('published', true)
->when(!is_null($rangeModel), function (Builder $q) use ($rangeModel) {
$q->whereHas('range', function (Builder $q) use ($rangeModel) {
$q->where('published', true)->where('id', $rangeModel->getKey());
})->orWhereHas('subrange', function (Builder $q) use ($rangeModel) {
$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 {
$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('slug')
->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 {
$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('slug')
->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 {
$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('slug')
->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->RANGESLUG));
}
}