|null $submeatModelsFiltered * @return \Illuminate\Contracts\View\View */ public function index( LengthAwarePaginator $recipeModelsFiltered = null, Collection $submeatModelsFiltered = null, ): \Illuminate\Contracts\View\View { /** @var \Illuminate\Contracts\Pagination\LengthAwarePaginator $recipeModels */ $recipeModels = $recipeModelsFiltered ?? Recipe::query() ->with(['products', 'products.submeats', 'consumptionMoment']) ->where('published', true) ->orderBy('slug') ->paginate($this->recipeModelsPerPage); $recipeModel = Recipe::query()->where('published', true)->where('highlight', true)->first(); $generalSettingModel = GeneralSetting::query()->where('type', SettingsType::recipes)->first(); $consumptionMomentModels = ConsumptionMoment::query()->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.recipes.index', [ 'viewsPaginate' => $this->getViewsFromPaginateModels($recipeModels), 'recipeModel' => $recipeModel, 'generalSettingModel' => $generalSettingModel, 'consumptionMomentModels' => $consumptionMomentModels, 'meatModels' => $meatModels, 'submeatModels' => $submeatModels, 'searchPropositions' => $this->getRecipeModelsTitles(), ]); } /** * Get the view of a specific recipe. * * @param string $slug * @return \Illuminate\Contracts\View\View */ public function show(string $slug): \Illuminate\Contracts\View\View { /** @var \App\Models\Recipe|null $recipeModel Current recipe model */ $recipeModel = Recipe::query()->where('slug', $slug)->with('products.submeats')->first(); if (is_null($recipeModel)) { abort(404); } $generalSettingModel = GeneralSetting::query()->where('type', SettingsType::recipe)->first(); return view('fo.pages.recipes.show', [ 'recipeModel' => $recipeModel, 'recipeModels' => $this->getRecipesFiltered($recipeModel), 'generalSettingModel' => $generalSettingModel, ]); } /** * Get recipe models titles for search input propositions. * * @param string|null $search * @return \Illuminate\Pagination\LengthAwarePaginator */ public function getRecipeModelsTitles(string|null $search = null): \Illuminate\Pagination\LengthAwarePaginator { $recipeModelsTitles = []; Recipe::query() ->with(['products', 'products.submeats', 'consumptionMoment']) ->when(!is_null($search), function (Builder $q) use ($search) { $q->where('title', 'like', "%{$search}%"); }) ->where('published', true) ->inRandomOrder() ->pluck('title') ->each(function (string $title) use (&$recipeModelsTitles, $search) { if (count($recipeModelsTitles) < 5) { foreach (explode(" ", $title) as $word) { $word = Str::lower($word); if ( strlen($word) > 3 && !in_array($word, $recipeModelsTitles) && ((!is_null($search)) ? str_contains($word, $search) : true) ) { $recipeModelsTitles[] = $word; } } return $recipeModelsTitles; } }) ->toArray(); \sort($recipeModelsTitles); $titles = array_slice($recipeModelsTitles, 0, 5); return new LengthAwarePaginatorCustom($titles, count($titles), (count($titles) <= 0) ? 1 : count($titles)); } /** * 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->getRecipeModelsTitles($request->input('search'))); } /** * Get recipes 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 getRecipesByWords(Request $request): \Illuminate\Http\JsonResponse|\Illuminate\Contracts\View\View { /** @var \Illuminate\Database\Eloquent\Builder $recipeModels */ $recipeModels = Recipe::query() ->with(['products', 'products.submeats', 'consumptionMoment']) ->where('published', true) ->orderBy('slug'); /** @var string $search Search field */ $search = $request->input('words'); if ($search) { $this->searchQuery( $recipeModels, str_replace(",", " ", $request->input('words')), null, 'title', ); } $recipeModels = $recipeModels->paginate($this->recipeModelsPerPage); if ($request->ajax()) { return response()->json([ 'viewsPaginate' => $this->getViewsFromPaginateModels($recipeModels), 'children' => [], ]); } return $this->index($recipeModels, collect()); } /** * Get recipes by a specific consumption_moment, * the response can be a json or a view. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\JsonResponse|\Illuminate\Contracts\View\View */ public function getRecipesByConsumptionMoment( Request $request ): \Illuminate\Http\JsonResponse|\Illuminate\Contracts\View\View { $consumptionMomentModel = ConsumptionMoment::query() ->where('slug', $request->model_slug) ->first(); $recipeModels = Recipe::query() ->with(['products', 'products.submeats', 'consumptionMoment']) ->when(!is_null($consumptionMomentModel), function (Builder $q) use ($consumptionMomentModel) { $q->where('consumption_moment_id', $consumptionMomentModel->getKey()); }) ->where('published', true) ->orderBy('slug') ->paginate($this->recipeModelsPerPage); if ($request->ajax()) { return response()->json([ 'viewsPaginate' => $this->getViewsFromPaginateModels($recipeModels), 'children' => [], ]); } return $this->index($recipeModels, collect()); } /** * Get recipes 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 getRecipesByMeat( 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(); $recipeModels = Recipe::query() ->with(['products', 'products.submeats', 'consumptionMoment']) ->where('published', true) ->when(!is_null($meatModel), function (Builder $q) use ($meatModel) { $q->whereHas('products', function (Builder $q) use ($meatModel) { $q->whereHas('meats', function (Builder $q) use ($meatModel) { $q->where('published', true)->where('id', $meatModel->getKey()); })->orWhereHas('submeats', function (Builder $q) use ($meatModel) { $q->where('published', true)->whereIn('id', $meatModel->submeats->pluck('id')->toArray()); }); }); }) ->orderBy('slug') ->paginate($this->recipeModelsPerPage); if ($request->ajax()) { return response()->json([ 'viewsPaginate' => $this->getViewsFromPaginateModels($recipeModels), 'children' => $submeatModels, ]); } return $this->index($recipeModels, $submeatModels); } /** * Get recipes 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 getRecipesBySubmeat( 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(); $recipeModels = Recipe::query() ->with(['products', 'products.submeats', 'consumptionMoment']) ->where('published', true) ->when(!is_null($submeatModel), function (Builder $q) use ($submeatModel) { $q->whereHas('products', function (Builder $q) use ($submeatModel) { $q->whereHas('submeats', function (Builder $q) use ($submeatModel) { $q->where('published', true)->where('id', $submeatModel->getKey()); }); }); }) ->orderBy('slug') ->paginate($this->recipeModelsPerPage); if ($request->ajax()) { return response()->json([ 'viewsPaginate' => $this->getViewsFromPaginateModels($recipeModels), 'children' => [], ]); } return $this->index($recipeModels, $submeatModels); } /** * Get an array of views for each recipe models given. * * @param \Illuminate\Contracts\Pagination\LengthAwarePaginator $recipeModels * @return array */ public function getViewsFromPaginateModels(LengthAwarePaginator $recipeModels): array { // @phpstan-ignore-next-line return $recipeModels->through(function ($recipeModel) { return view('fo.partials.recipe-card', ['recipeModel' => $recipeModel])->render(); })->toArray(); } /** * Get recipes by a selected recipe model, * filtered first by products associated, * by meats/submeats and finally by consumption moment. * * @param \App\Models\Recipe $recipeModel * @return \Illuminate\Support\Collection */ public function getRecipesFiltered(Recipe $recipeModel): \Illuminate\Support\Collection { /** @var int $nbrMaxRecipes Maximum number of recipes required */ $nbrMaxRecipes = 9; /** @var array $recipeProducts Id of recipe products relation */ $recipeProducts = $recipeModel->products->pluck('id')->toArray(); /** @var \Illuminate\Support\Collection<\App\Models\Recipe> $recipeWithProducts Recipe query builder for products relation */ $recipeWithProducts = Recipe::query() ->with(['products', 'products.submeats', 'consumptionMoment']) ->whereNot('id', $recipeModel->id) ->where('published', true) ->whereHas('products', function (Builder $q) use ($recipeProducts) { $q->where('id', $recipeProducts[0]) ->when(isset($recipeProducts[1]), function (Builder $q) use ($recipeProducts) { $q->orWhere('id', $recipeProducts[1]); }); }) ->get(); /** @var \Illuminate\Support\Collection<\App\Models\Recipe> $recipeModels All collections of recipes merged */ $recipeModels = collect(); if (count($recipeWithProducts) < $nbrMaxRecipes) { /** @var array $recipeProductsSubmeats */ $recipeProductsSubmeats = []; $recipeModel->products->map(function ($product) use (&$recipeProductsSubmeats) { $recipeProductsSubmeats = array_merge( $recipeProductsSubmeats, $product->submeats->pluck('id')->toArray() ); }); /** @var \Illuminate\Support\Collection<\App\Models\Recipe> $recipeWithMeats Recipe query builder for products relation */ $recipeWithMeats = Recipe::query() ->with(['products', 'products.submeats', 'consumptionMoment']) ->whereNot('id', $recipeModel->id) ->where('published', true) ->whereHas('products', function (Builder $q) use ($recipeProducts, $recipeProductsSubmeats) { $q->where('id', $recipeProducts[0]) ->whereHas('submeats', function (Builder $q) use ($recipeProductsSubmeats) { $q->where('published', true)->whereIn('id', $recipeProductsSubmeats); }) ->when( isset($recipeProducts[1]), function (Builder $q) use ($recipeProducts, $recipeProductsSubmeats) { $q->orWhere('id', $recipeProducts[1]) ->whereHas('submeats', function (Builder $q) use ($recipeProductsSubmeats) { $q->where('published', true)->whereIn('id', $recipeProductsSubmeats); }); } ); }) ->get(); $recipeModels = $recipeWithProducts->merge($recipeWithMeats); if (count($recipeModels) < $nbrMaxRecipes) { /** @var \Illuminate\Support\Collection<\App\Models\Recipe> $recipeWithConsumptionMoment Recipe query builder for consumption moment relation */ $recipeWithConsumptionMoment = Recipe::query() ->with(['products', 'products.submeats', 'consumptionMoment']) ->whereNot('id', $recipeModel->id) ->where('published', true) ->where('consumption_moment_id', $recipeModel->consumption_moment_id) ->get(); $recipeModels = $recipeModels->merge($recipeWithConsumptionMoment); } } //end if return $recipeModels->slice(0, $nbrMaxRecipes); } /** * Print product commercial sheet * * @param string $slug * @return \Illuminate\Http\Response */ public function printCommericalSheet(string $slug): \Illuminate\Http\Response { $recipeModel = Recipe::query()->where('slug', $slug)->first(); $title = trans('Fiche commerciale de la recette : :title - :appname', [ 'label' => $recipeModel->title, 'appname' => \config('app.name', 'Le Gaulois') ]); $filename = trans('fiche-commerciale-:slug-:date', [ 'slug' => Str::slug($recipeModel->title), 'date' => \now()->format('Y-m-d') ]); /** * ! Réponse mano car enfait \Mccarlosen\LaravelMpdf\LaravelMpdf * ! ne retourne pas vraiemnt une http response, mais provoque une 500 * ! apres avoir header et echo dans le corps de la réponse php (Nasty trick) */ return \response( $this->generateRecipeCommercialPdf($recipeModel, $title)->output(), 200, [ 'Content-Type' => 'application/pdf', 'Content-disposition' => "inline; filename=\"{$filename}\"", 'Cache-Control' => 'public, must-revalidate, max-age=0', 'Pragma' => 'public', 'X-Generator' => 'mPDF', 'Expires' => 'Sat, 26 Jul 1997 05:00:00 GMT', 'Last-Modified' => gmdate('D, d M Y H:i:s') . ' GMT', ] ); } /** * Generate recipe commercial sheet PDF * * @param \App\Models\Recipe $recipeModel * @param string $title * @return \Mccarlosen\LaravelMpdf\LaravelMpdf */ private function generateRecipeCommercialPdf( Recipe $recipeModel, string $title ): \Mccarlosen\LaravelMpdf\LaravelMpdf { /** @var \Mccarlosen\LaravelMpdf\LaravelMpdfWrapper */ $mpdf = app('laravel-mpdf'); /** @var \Mccarlosen\LaravelMpdf\LaravelMpdf */ $pdf = $mpdf->loadView( 'fo.documents.recipe-commercial-sheet', // * Dirty convert to object. [ 'title' => $title, 'recipeModel' => \json_decode(\json_encode(new RecipeCommericalSheetResource($recipeModel))) ], [], [ 'mode' => 'utf-8', 'format' => 'A4', 'default_font_size' => '12', 'default_font' => 'sans-serif', 'margin_left' => 0, 'margin_right' => 0, 'mirrorMargins' => true, 'margin_top' => 0, 'margin_bottom' => 0, 'margin_header' => 0, 'margin_footer' => 0, 'orientation' => 'P', 'title' => $title, 'author' => \config('app.name', 'Le Gaulois'), 'watermark' => '', 'show_watermark' => false, 'watermark_font' => 'sans-serif', 'display_mode' => 'fullpage', 'watermark_text_alpha' => 0.1, 'custom_font_dir' => '', 'custom_font_data' => [], 'auto_language_detection' => false, 'temp_dir' => rtrim( ini_get('upload_tmp_dir') ? ini_get('upload_tmp_dir') : sys_get_temp_dir(), DIRECTORY_SEPARATOR ), 'pdfa' => false, 'pdfaauto' => false, ] ); return $pdf; } }