ldc-le-gaulois-pro/app/Http/Controllers/Controller.php
Jérémy Munsch 3a0d4e9d20
PHPstan OK
2024-02-05 09:33:08 +01:00

310 lines
12 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Enums\Advices\AdviceTypeEnum;
use App\Enums\Pagination\ItemsPerPaginationEnum;
use App\Enums\Products\ProductSpeciesEnum;
use App\Lib\Helpers\ToolboxHelper;
use App\Models\Advice;
use App\Models\News;
use App\Models\Product;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Str;
class Controller extends BaseController
{
use AuthorizesRequests;
use ValidatesRequests;
/**
* Build Search query on specified fields
* splitting search into words or using whole sentence
*
* @param \Illuminate\Database\Eloquent\Builder $query The eloquent query builder.
* @param string $search The query string.
* @param callable $searchQuery The query string.
* @param string ...$fields Either a simple array or a string
* in case of a string the two fields
* would be concatenate to make the search.
* @return \Illuminate\Database\Eloquent\Builder
*/
protected function searchQuery(
Builder $query,
string $search,
callable $searchQuery = null,
string ...$fields
): \Illuminate\Database\Eloquent\Builder {
if (count($fields)) {
// Search using words.
$words = explode(' ', $search);
$query->where(function ($query) use ($searchQuery, $words, $search, $fields) {
foreach ($words as $word) {
if (!strlen($word)) {
continue;
}
$this->searchOnFields($query, $word, $fields);
} //end foreach
// Search whole using whole sentence.
$this->searchOnFields($query, $search, $fields);
if ($searchQuery) {
$searchQuery($query);
}
});
} //end if
return $query;
}
/**
* Build query search on all fields
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param string $search
* @param array $fields
* @return \Illuminate\Database\Eloquent\Builder
*/
private function searchOnFields(
Builder $query,
string $search,
array $fields
): \Illuminate\Database\Eloquent\Builder {
$table = $query->getModel()->getTable();
foreach ($fields as $field) {
$field = str_replace('?', '\?', $field);
if (is_array($field)) {
$query->orWhereRaw(
sprintf("UPPER(CONCAT(%s)) LIKE UPPER(?)", collect($field)->map(function ($q) use ($table) {
return "`$table`.`$q`";
})->implode(', \' \', ')),
['%' . htmlspecialchars($search) . '%']
);
return $query;
}
// Get models woth enum's label.
if (is_array($this->getSearchValue($query, $search, $field))) {
foreach ($this->getSearchValue($query, $search, $field) as $array) {
$query->orWhereRaw(
"UPPER(`$table`.`$field`) LIKE UPPER(?)",
['%' . htmlspecialchars($array->value) . '%']
);
}
return $query;
}
// Get models with relation's name/label.
if (Str::endsWith($field, '_id')) {
$query->orWhereHas(Str::remove('_id', $field), function ($queryRelation) use ($field, $search) {
if (Schema::hasColumn(Str::remove('_id', $field) . 's', 'name')) {
$queryRelation->where('name', 'like', '%' . $search . '%');
} elseif (Schema::hasColumn(Str::remove('_id', $field) . 's', 'label')) {
$queryRelation->where('label', 'like', '%' . $search . '%');
}
});
return $query;
}
$query->orWhereRaw(
"UPPER(`$table`.`$field`) LIKE UPPER(?)",
['%' . htmlspecialchars($this->getSearchValue($query, $search, $field)) . '%']
);
} //end foreach
return $query;
}
/**
* Get the search value (on string or Enum).
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param string $search
* @param string $field
* @return string|array
*/
protected function getSearchValue(Builder $query, string $search, string $field): string|array
{
$cast = $query->getModel()->getCasts()[$field] ?? null;
if (
count($query->getModel()->getCasts()) and // Check if the model has casts.
$cast and // Check if there is cast about specific field.
enum_exists($cast) // Check if the cast is an enum.
) {
/** @var \App\Traits\Enums $cast */
// @phpstan-ignore-next-line
$cases = collect($cast::toArray());
$enum = $cases->filter(function ($item) use ($search) {
return preg_match('/' . Str::lower($search) . '/', Str::lower($item->label));
});
return $enum->toArray();
} else {
return $search;
}
}
/**
* Sort columns with a query.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param array $ignore
* @return \Illuminate\Database\Eloquent\Builder
*/
protected function sortQuery(Builder $query, array $ignore = []): \Illuminate\Database\Eloquent\Builder
{
$rName = request()->route()->getName();
$table = $query->getModel()->getTable();
if (request()->input('sort_col')) {
Session::put("$rName.sort_col", request()->input('sort_col'));
Session::put("$rName.sorted", true);
}
if (request()->input('sort_way')) {
Session::put("$rName.sort_way", request()->input('sort_way'));
Session::put("$rName.sorted", true);
}
if (request()->rst) {
Session::remove("$rName.sorted");
Session::remove("$rName.sort_col");
Session::remove("$rName.sort_way");
}
if (
request()->input('sort_col') and
request()->input('sort_way') and
Schema::hasColumn($table, request()->input('sort_col'))
) {
if (!in_array(request()->input('sort_col'), $ignore)) {
$query = $query->orderBy(
\sprintf("$table.%s", request()->input('sort_col')),
request()->input('sort_way')
);
}
} elseif (Schema::hasColumn($table, 'order')) {
$query = $query->orderBy('order', 'ASC');
Session::put("$rName.sort_col", 'order');
Session::put("$rName.sort_way", 'asc');
} elseif (Schema::hasColumn($table, 'updated_at')) {
$query = $query->orderBy('updated_at', 'DESC');
Session::put("$rName.sort_col", 'updated_at');
Session::put("$rName.sort_way", 'desc');
} elseif (Schema::hasColumn($table, 'created_at')) {
$query = $query->orderBy('created_at', 'DESC');
Session::put("$rName.sort_col", 'created_at');
Session::put("$rName.sort_way", 'desc');
}
return $query;
}
/**
* Customize pagination with cache or config (default).
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param \App\Enums\Pagination\ItemsPerPaginationEnum|null $pagination
* @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
*/
protected function paginate(
Builder $query,
ItemsPerPaginationEnum $pagination = null
): \Illuminate\Contracts\Pagination\LengthAwarePaginator {
$currentRoutePath = Str::slug(request()->route()->getName());
$cacheKey = "pagination.{$currentRoutePath}";
$pagination = $pagination ?? ItemsPerPaginationEnum::twelve;
$pagination = ToolboxHelper::getValidatedEnum(
Cache::get($cacheKey, $pagination->value()),
'pagination',
'\App\Enums\Pagination\ItemsPerPaginationEnum',
);
if (Cache::get($cacheKey) !== $pagination) {
Cache::put($cacheKey, $pagination);
}
$query = $query->paginate($pagination);
return $query;
}
// Front Search methods.
/**
* Get product query search
*
* Par leur désignation
* Par espèce
* Par gamme
* Par ingrédients
* Par atout produits
* Par type produit
*
* @param string $search
* @return \Illuminate\Database\Eloquent\Builder
*/
protected function getProductQuery(string $search): Builder
{
return Product::query()->where('status', true)->where(function ($query) use ($search) {
// Par leur désignation.
$query->where('label', 'like', "%{$search}%");
// Par ingrédients.
$query->orWhere('ingredients', 'like', "%{$search}%");
// Par atout produits.
$query->orWhere('advantage', 'like', "%{$search}%");
// Par gamme.
$query->orWhereHas('subrange', function (Builder $q) use ($search) {
$q->whereHas('range', function (Builder $q) use ($search) {
$q->where('label', 'like', "%{$search}%");
});
});
// Par type de produit (sous gamme).
$query->orWhereHas('subrange', function (Builder $q) use ($search) {
$q->where('label', 'like', "%{$search}%");
});
// Par espèce.
if (\strlen($search)) {
/** @var \App\Enums\Products\ProductSpeciesEnum|null $specie */
$specie = \collect(ProductSpeciesEnum::toArray())
->filter(function (mixed $specie) use ($search) {
return \strpos(Str::lower($specie->label), Str::lower($search)) !== false;
})->first();
if ($specie) {
$query->orWhere('species', $specie->value);
}
}
});
}
/**
* Get news query search
*
* Par mots dans le titre
* Par mots dans le texte
*
* @param string $search
* @return \Illuminate\Database\Eloquent\Builder
*/
protected function getNewsQuery(string $search): Builder
{
return News::query()->where('status', true)->where(function ($query) use ($search) {
$query->where('title', 'like', "%{$search}%");
$query->orWhere('subtitle', 'like', "%{$search}%");
$query->orWhere('content', 'like', "%{$search}%");
$query->orWhere('content2', 'like', "%{$search}%");
});
}
/**
* Get recipe query search
*
* Par mots dans le titre
* Par mots dans le texte de la recette
*
* @param string $search
* @return \Illuminate\Database\Eloquent\Builder
*/
protected function getRecipeQuery(string $search): Builder
{
return Advice::query()->where('status', true)
->where('type', '=', AdviceTypeEnum::recipes->value)
->where(function ($query) use ($search) {
$query->where('title', 'like', "%{$search}%");
$query->orWhere('preparation', 'like', "%{$search}%");
$query->orWhere('advice', 'like', "%{$search}%");
$query->orWhere('ingredients', 'like', "%{$search}%");
});
}
}