wip: newsBreeding; staticPageTypeEnum + BreedingTypeEnum + controller + routes + providers + model + migration DONE; todo : CRUD bo and front

This commit is contained in:
Nicolas Demangeat 2024-09-05 16:40:55 +02:00
parent 1474f56a08
commit 636fb44364
13 changed files with 447 additions and 8 deletions

View file

@ -0,0 +1,179 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\NewsBreedings\StoreNewsBreedingRequest;
use App\Http\Requests\Bo\NewsBreedings\UpdateNewsBreedingRequest;
use App\Models\NewsBreeding;
use App\Traits\Controllers\UpdateModelPublished;
use App\Traits\Controllers\ChangesModelOrder;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class NewsBreedingController extends Controller
{
use UpdateModelPublished;
use ChangesModelOrder;
/**
* Create the controller instance.
*/
public function __construct()
{
$this->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<string,string> */
$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());
}
}

View file

@ -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;

View file

@ -0,0 +1,78 @@
<?php
namespace App\Http\Requests\Bo\NewsBreedings;
use App\Models\NewsBreeding;
use App\Traits\Requests\HasPicture;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Gate;
class StoreNewsBreedingRequest extends FormRequest
{
use HasPicture;
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return Gate::check('create', NewsBreeding::class);
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation(): void
{
$this->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'),
];
}
}

View file

@ -0,0 +1,18 @@
<?php
namespace App\Http\Requests\Bo\NewsBreedings;
use Illuminate\Support\Facades\Gate;
class UpdateNewsBreedingRequest extends StoreNewsBreedingRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return Gate::check('update', $this->route('news_breeding'));
}
}

View file

@ -1,6 +1,6 @@
<?php
namespace App\Http\Requests\Bo\NewsCorporate;
namespace App\Http\Requests\Bo\NewsCorporates;
use App\Models\NewsCorporate;
use App\Traits\Requests\HasPicture;

View file

@ -1,6 +1,6 @@
<?php
namespace App\Http\Requests\Bo\NewsCorporate;
namespace App\Http\Requests\Bo\NewsCorporates;
use Illuminate\Support\Facades\Gate;

View file

@ -3,6 +3,7 @@
namespace App\Models;
use App\Lib\Helpers\FileStorageHelper;
use App\Lib\Helpers\ToolboxHelper;
use App\Traits\Models\ActivityLog;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
@ -32,9 +33,12 @@ class NewsBreeding extends Model
* @var array<int, string>
*/
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<string, string>
*/
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
*

View file

@ -0,0 +1,97 @@
<?php
namespace App\Policies;
use App\Enums\Users\RoleEnum;
use App\Models\User;
use App\Policies\Rules\UserStaticRules;
class NewsBreedingPolicy
{
/**
* Determine whether the user can view any models.
*
* @return boolean
*/
public function viewAny(): bool
{
return UserStaticRules::everyOne();
}
/**
* Determine whether the user can view the model.
*
* @param \App\Models\User $authUser
* @return boolean
*/
public function view(User $authUser): bool
{
return UserStaticRules::atLeastRole($authUser, RoleEnum::admin);
}
/**
* Determine whether the user can create a new model.
*
* @param \App\Models\User $authUser
* @return boolean
*/
public function create(User $authUser): bool
{
return UserStaticRules::atLeastRole($authUser, RoleEnum::admin);
}
/**
* Determine whether the user can update the model.
*
* @param \App\Models\User $authUser
* @return boolean
*/
public function update(User $authUser): bool
{
return UserStaticRules::atLeastRole($authUser, RoleEnum::admin);
}
/**
* Determine whether the user can delete the model.
*
* @param \App\Models\User $authUser
* @return boolean
*/
public function delete(User $authUser): bool
{
return UserStaticRules::atLeastRole($authUser, RoleEnum::admin);
}
/**
* Determine whether the user can duplicate the model.
*
* @param \App\Models\User $authUser
* @return boolean
*/
public function duplicate(User $authUser): bool
{
return UserStaticRules::atLeastRole($authUser, RoleEnum::admin);
}
/**
* Determine whether the user can publish/unpublish the model.
*
* @param \App\Models\User $authUser
* @return boolean
*/
public function changePublished(User $authUser): bool
{
return UserStaticRules::atLeastRole($authUser, RoleEnum::admin);
}
/**
* Determine whether the user can change the order of the model.
*
* @param \App\Models\User $authUser
* @return boolean
*/
public function changeOrder(User $authUser): bool
{
return UserStaticRules::atLeastRole($authUser, RoleEnum::admin);
}
}

View file

@ -11,6 +11,7 @@ use App\Models\Claim;
use App\Models\ConsumptionMoment;
use App\Models\MainSlide;
use App\Models\Meat;
use App\Models\NewsBreeding;
use App\Models\NewsCorporate;
use App\Models\Offer;
use App\Models\Product;
@ -84,6 +85,7 @@ class RouteServiceProvider extends ServiceProvider
Route::model('offer', Offer::class);
Route::model('news_corporate', NewsCorporate::class);
Route::model('testimonial', Testimonial::class);
Route::model('news_breeding', NewsBreeding::class);
}
/**

View file

@ -106,6 +106,14 @@ class ViewServiceProvider extends ServiceProvider
->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;

View file

@ -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.');

View file

@ -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();
}

View file

@ -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']);