diff --git a/app/Http/Controllers/Bo/NewsBreedingController.php b/app/Http/Controllers/Bo/NewsBreedingController.php new file mode 100644 index 00000000..4b6b2ba0 --- /dev/null +++ b/app/Http/Controllers/Bo/NewsBreedingController.php @@ -0,0 +1,179 @@ +authorizeResource(NewsBreeding::class); + } + /** + * Display a listing of the resource. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Contracts\View\View + */ + public function index(Request $request): \Illuminate\Contracts\View\View + { + /** @var \Illuminate\Database\Eloquent\Builder $newsBreedingModels */ + $newsBreedingModels = NewsBreeding::query(); + + /** @var string $search Search field */ + $search = $request->search; + if ($search) { + $this->searchQuery( + $newsBreedingModels, + $search, + null, + 'title', + 'subtitle', + 'content' + ); + } + $searchFields = \implode(', ', [ + trans('validation.attributes.text'), + ]); + + /** Sort columns with a query */ + $this->sortQuery($newsBreedingModels); + + /** Custom pagination */ + $newsBreedingModels = $this->paginate($newsBreedingModels); + + return view('bo.pages.news_breedings.index', compact('newsBreedingModels', 'search', 'searchFields')); + } + + /** + * Show the form for creating a new resource. + * + * @param \App\Models\NewsBreeding $newsBreeding + * @return \Illuminate\Contracts\View\View + */ + public function create(NewsBreeding $newsBreeding): \Illuminate\Contracts\View\View + { + return view('bo.pages.news_breedings.create', ['newsBreedingModel' => $newsBreeding]); + } + + /** + * Store a newly created resource in storage. + * + * @param \App\Http\Requests\Bo\NewsBreeding\StoreNewsBreedingRequest $request + * @return \Illuminate\Http\RedirectResponse + */ + public function store(StoreNewsBreedingRequest $request): \Illuminate\Http\RedirectResponse + { + return DB::transaction(function () use ($request) { + $newsBreeding = new NewsBreeding(); + $newsBreeding->fill($request->validated()); + if ($newsBreeding->saveOrFail()) { + return redirect()->route('bo.news_breedings.edit', $newsBreeding) + ->with('success', __('crud.messages.has_been_created', [ + 'model' => Str::ucfirst(__('models.classes.news_breeding')) + ])); + } + return redirect()->back() + ->with('error', __('crud.messages.cannot_be_created', [ + 'model' => Str::ucfirst(__('models.classes.news_breeding')) + ])); + }); + } + + /** + * Display the specified resource. + * + * @param \App\Models\NewsBreeding $newsBreeding + * @return \Illuminate\Contracts\View\View + */ + public function show(NewsBreeding $newsBreeding): \Illuminate\Contracts\View\View + { + return view('bo.pages.news_breedings.show', ['newsBreedingModel' => $newsBreeding]); + } + + /** + * Show the form for editing the specified resource. + * + * @param \App\Models\NewsBreeding $newsBreeding + * @return \Illuminate\Contracts\View\View + */ + public function edit(NewsBreeding $newsBreeding): \Illuminate\Contracts\View\View + { + return view('bo.pages.news_breedings.edit', ['newsBreedingModel' => $newsBreeding]); + } + + /** + * Update the specified resource in storage. + * + * @param \App\Http\Requests\Bo\NewsBreedings\UpdateNewsBreedingRequest $request + * @param \App\Models\NewsBreeding $newsBreeding + * @return \Illuminate\Http\RedirectResponse + */ + public function update( + UpdateNewsBreedingRequest $request, + NewsBreeding $newsBreeding + ): \Illuminate\Http\RedirectResponse { + return DB::transaction(function () use ($request, $newsBreeding) { + $newsBreeding->fill($request->validated()); + if ($newsBreeding->saveOrFail()) { + return redirect()->route('bo.news_breedings.edit', $newsBreeding) + ->with('success', trans('crud.messages.has_been_updated', [ + 'model' => Str::ucfirst(trans('models.classes.news_breeding')) + ])); + } + return redirect()->route('bo.news_breedings.edit', $newsBreeding) + ->with('error', trans('crud.messages.cannot_be_updated', [ + 'model' => Str::ucfirst(trans('models.classes.news_breeding')) + ])); + }); + } + + /** + * Remove the specified resource from storage. + * + * @param \App\Models\NewsBreeding $newsBreeding + * @return \Illuminate\Http\RedirectResponse + */ + public function destroy(NewsBreeding $newsBreeding): \Illuminate\Http\RedirectResponse + { + /** @var array */ + $previousQueries = []; + \parse_str(\parse_url(url()->previous(), \PHP_URL_QUERY), $previousQueries); + if ($newsBreeding->deleteOrFail()) { + return redirect()->route('bo.news_breedings.index', $previousQueries) + ->with('success', trans('crud.messages.has_been_deleted', [ + 'model' => Str::ucfirst(trans('models.classes.news_breeding')) + ])); + } + return redirect()->back() + ->with('error', trans('crud.messages.cannot_be_deleted', [ + 'model' => Str::ucfirst(trans('models.classes.news_breeding')) + ])); + } + + /** + * Duplicate the specified resource. + * + * @param \App\Models\NewsBreeding $newsBreeding + * @return \Illuminate\Contracts\View\View + */ + public function duplicate(NewsBreeding $newsBreeding): \Illuminate\Contracts\View\View + { + return $this->create($newsBreeding->replicate()); + } +} diff --git a/app/Http/Controllers/Bo/NewsCorporateController.php b/app/Http/Controllers/Bo/NewsCorporateController.php index 05d908fc..6dfdc1cf 100644 --- a/app/Http/Controllers/Bo/NewsCorporateController.php +++ b/app/Http/Controllers/Bo/NewsCorporateController.php @@ -3,8 +3,8 @@ namespace App\Http\Controllers\Bo; use App\Http\Controllers\Controller; -use App\Http\Requests\Bo\NewsCorporate\StoreNewsCorporateRequest; -use App\Http\Requests\Bo\NewsCorporate\UpdateNewsCorporateRequest; +use App\Http\Requests\Bo\NewsCorporates\StoreNewsCorporateRequest; +use App\Http\Requests\Bo\NewsCorporates\UpdateNewsCorporateRequest; use App\Models\NewsCorporate; use App\Traits\Controllers\UpdateModelPublished; use App\Traits\Controllers\ChangesModelOrder; diff --git a/app/Http/Requests/Bo/NewsBreedings/StoreNewsBreedingRequest.php b/app/Http/Requests/Bo/NewsBreedings/StoreNewsBreedingRequest.php new file mode 100644 index 00000000..4fb17a97 --- /dev/null +++ b/app/Http/Requests/Bo/NewsBreedings/StoreNewsBreedingRequest.php @@ -0,0 +1,78 @@ +mergePicture('picture'); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules(): array + { + $rules = [ + 'seo_title' => 'required|string|between:45,70', + 'seo_description' => 'required|string|between:50,160', + 'title' => 'required|string|between:3,255', + 'subtitle' => 'required|string|between:3,255', + 'content' => 'required|string|between:3,255', + 'picture_alt' => 'required_with:picture|between:3,255', + 'picture_title' => 'required_with:picture|between:3,255', + 'published' => 'required|boolean', + 'publish_at' => 'nullable|date', + 'unpublish_at' => 'nullable|date|after:publish_at', + ]; + return \array_merge( + $rules, + $this->pictureRules('picture', 700, 400, 700, 400), + ); + } + + /** + * Get custom attributes for validator errors. + * + * @return array + */ + public function attributes(): array + { + return [ + 'title' => trans('validation.attributes.title'), + 'content' => trans('validation.attributes.content'), + 'url' => trans('validation.custom.url'), + 'picture' => trans('validation.attributes.image'), + 'picture_alt' => trans('validation.custom.attribute_alt'), + 'picture_title' => trans('validation.custom.attribute_title'), + 'published' => trans('validation.attributes.published'), + 'publish_at' => trans('validation.custom.publish_at'), + 'unpublish_at' => trans('validation.custom.unpublish_at'), + ]; + } +} diff --git a/app/Http/Requests/Bo/NewsBreedings/UpdateNewsBreedingRequest.php b/app/Http/Requests/Bo/NewsBreedings/UpdateNewsBreedingRequest.php new file mode 100644 index 00000000..ddc64ad4 --- /dev/null +++ b/app/Http/Requests/Bo/NewsBreedings/UpdateNewsBreedingRequest.php @@ -0,0 +1,18 @@ +route('news_breeding')); + } +} diff --git a/app/Http/Requests/Bo/NewsCorporate/StoreNewsCorporateRequest.php b/app/Http/Requests/Bo/NewsCorporates/StoreNewsCorporateRequest.php similarity index 97% rename from app/Http/Requests/Bo/NewsCorporate/StoreNewsCorporateRequest.php rename to app/Http/Requests/Bo/NewsCorporates/StoreNewsCorporateRequest.php index c8cb13f9..f8192cbe 100644 --- a/app/Http/Requests/Bo/NewsCorporate/StoreNewsCorporateRequest.php +++ b/app/Http/Requests/Bo/NewsCorporates/StoreNewsCorporateRequest.php @@ -1,6 +1,6 @@ */ protected $fillable = [ + 'seo_title', + 'seo_description', 'title', 'subtitle', 'content', + 'slug', 'picture', 'picture_alt', 'picture_title', @@ -50,7 +54,7 @@ class NewsBreeding extends Model * @var array */ protected $casts = [ - 'published' => 'boolean', + 'published' => 'boolean', 'publish_at' => 'datetime', 'unpublish_at' => 'datetime', ]; @@ -63,11 +67,14 @@ class NewsBreeding extends Model protected static function booted(): void { static::creating(function (self $model) { + self::setOrder($model); self::makeStatusSense($model); + self::assertTitleIsUnique($model->title); $model->picture = FileStorageHelper::storeFile($model, $model->picture, true); }); static::updating(function (self $model) { self::makeStatusSense($model); + self::assertTitleIsUnique($model->title, $model->getKey()); $model->picture = FileStorageHelper::storeFile($model, $model->picture, true); }); static::updated(function (self $model) { @@ -80,6 +87,17 @@ class NewsBreeding extends Model // * METHODS + /** + * Set order after the last element of the list. + * + * @param self $model + * @return void + */ + private static function setOrder(self $model): void + { + $model->order = \intval(self::query()->max('order')) + 1; + } + /** * Adjusts breedingNews status using current status and publish dates * @@ -104,6 +122,22 @@ class NewsBreeding extends Model } } + /** + * Asserts using validation that the title is unique + * + * @param string|null $title + * @param integer|null $id + * @return void + * @throws \Illuminate\Validation\ValidationException If an actu title already exists. + */ + private static function assertTitleIsUnique(string|null $title, ?int $id = null): void + { + $table = (new self())->getTable(); + ToolboxHelper::assertFieldIsUnique($table, 'title', $title, $id); + ToolboxHelper::assertFieldIsUnique($table, 'slug', str($title)->slug(), $id); + } + + /** * Getter the current status following planification dates planificated_status * diff --git a/app/Policies/NewsBreedingPolicy.php b/app/Policies/NewsBreedingPolicy.php new file mode 100644 index 00000000..c802d0b9 --- /dev/null +++ b/app/Policies/NewsBreedingPolicy.php @@ -0,0 +1,97 @@ +where('type', StaticPageType::accessibility)->first()], ['name' => 'productStaticPageModel', 'value' => StaticPage::query() ->where('type', StaticPageType::product)->first()], + ['name' => 'breedingPoultryStaticPageModel', 'value' => StaticPage::query() + ->where('type', StaticPageType::breedingPoultry)->first()], + ['name' => 'breedingJobStaticPageModel', 'value' => StaticPage::query() + ->where('type', StaticPageType::breedingJob)->first()], + ['name' => 'breedingFoodStaticPageModel', 'value' => StaticPage::query() + ->where('type', StaticPageType::breedingFood)->first()], + ['name' => 'breedingHealthStaticPageModel', 'value' => StaticPage::query() + ->where('type', StaticPageType::breedingHealth)->first()], ])->map(function ($array) { View::share($array['name'], (function () use ($array) { return $array['value'] ?: null; diff --git a/database/migrations/2024_09_03_155707_create_news_breeding_table.php b/database/migrations/2024_09_03_155707_create_news_breeding_table.php index 31b86ba1..8322ffa3 100644 --- a/database/migrations/2024_09_03_155707_create_news_breeding_table.php +++ b/database/migrations/2024_09_03_155707_create_news_breeding_table.php @@ -22,7 +22,7 @@ return new class extends Migration $table->string('picture')->comment('Picture path.'); $table->string('picture_alt')->comment('Picture alt attribute.'); $table->string('picture_title')->comment('Picture title attribute.'); - $table->string('slug')->unique()->comment('Unique title slug'); + $table->string('slug')->comment('Unique title slug'); $table->text('content')->comment('The content of the news breeding.'); $table->integer('order')->comment('News breeding order'); $table->boolean('published')->comment('News breeding publish status.'); diff --git a/database/migrations/2024_09_03_171124_add_breeding_to_static_pages.php b/database/migrations/2024_09_03_171124_add_breeding_to_static_pages.php index 38fd4230..345e1c79 100644 --- a/database/migrations/2024_09_03_171124_add_breeding_to_static_pages.php +++ b/database/migrations/2024_09_03_171124_add_breeding_to_static_pages.php @@ -14,7 +14,12 @@ return new class extends Migration */ public function up(): void { - $enumTypeArr = [StaticPageType::breedingFood, StaticPageType::breedingHealth, StaticPageType::breedingFood, StaticPageType::breedingJob]; + $enumTypeArr = [ + StaticPageType::breedingFood, + StaticPageType::breedingHealth, + StaticPageType::breedingFood, + StaticPageType::breedingJob + ]; foreach ($enumTypeArr as $enumType) { $staticPage = new StaticPage(); $staticPage->type = $enumType; @@ -45,7 +50,12 @@ return new class extends Migration */ public function down(): void { - $enumTypeArr = [StaticPageType::selling, StaticPageType::accessibility]; + $enumTypeArr = [ + StaticPageType::breedingFood, + StaticPageType::breedingHealth, + StaticPageType::breedingFood, + StaticPageType::breedingJob + ]; foreach ($enumTypeArr as $type) { StaticPage::query()->where('type', $type)->first()->delete(); } diff --git a/routes/web-bo.php b/routes/web-bo.php index 2f38ef11..eff29a29 100644 --- a/routes/web-bo.php +++ b/routes/web-bo.php @@ -13,6 +13,7 @@ use App\Http\Controllers\Bo\KeyNumberController; use App\Http\Controllers\Bo\MainSlideController; use App\Http\Controllers\Bo\MenuNodeDataController; use App\Http\Controllers\Bo\MeatController; +use App\Http\Controllers\Bo\NewsBreedingController; use App\Http\Controllers\Bo\NewsCorporateController; use App\Http\Controllers\Bo\OfferController; use App\Http\Controllers\Bo\ProductController; @@ -368,6 +369,18 @@ Route::middleware(['throttle:web'])->prefix('bo')->name('bo.')->group(function ( NewsCorporateController::class, 'duplicate' ])->name('news_corporates.duplicate'); + // * NEWS BREEDING. + Route::resource('news_breedings', NewsBreedingController::class); + Route::patch('/news_breedings/{news_breeding}/change-published', [ + NewsBreedingController::class, 'changePublished' + ])->name('news_breedings.change-published'); + Route::patch('/breedings/{news_breeding}/change-order/{direction}', [ + NewsBreedingController::class, 'changeOrder' + ])->where('direction', 'up|down')->name('breedings.change-order'); + Route::get('/news_breedings/{news_breeding}/duplicate', [ + NewsBreedingController::class, 'duplicate' + ])->name('news_breedings.duplicate'); + Route::middleware('can:isConceptor')->group(function () { // * ACTIVITY LOGS. Route::resource('activity_logs', ActivityLogController::class)->only(['index', 'show']);