ldc-boutique-cnc-sable/app/Http/Controllers/Bo/DiscountsController.php
Jérémy Munsch 42bdae01cc
feat: implement pagination using Pagination value object
- Updated DataTableService to accept a Pagination object for pagination parameters.
- Refactored paginateQuery method to utilize pageSize and pageNumber from Pagination.

refactor: replace ActionExecutor with Executor in controller traits

- Updated ChangesModelOrder, TogglesModelStar, and TogglesModelStatus traits to use Executor interface instead of ActionExecutor.

feat: add PictureMetadata and Slug value objects

- Introduced PictureMetadata for managing image metadata with validation.
- Added Slug value object for slug generation and validation.

feat: update breadcrumbs to support RangeDto

- Modified breadcrumbs-bo.php to accept RangeDto in the edit route.

test: add tests for RangesController

- Added tests for accessing, creating, updating, and deleting ranges.
- Included tests for toggling range status.

test: add integration tests for range actions

- Created tests for CreateRange, DeleteRange, and UpdateRange actions.
- Validated input handling and database interactions.

test: add unit tests for range input classes

- Implemented tests for CreateRangeInput and UpdateRangeInput to validate input constraints.

test: add unit tests for value objects

- Added tests for PictureMetadata and Slug to ensure proper validation and instantiation.

test: add unit tests for RangeDto

- Created tests to verify RangeDto mapping from Range model.

test: add unit tests for RangePolicy

- Implemented tests for RangePolicy to validate permissions based on user roles.
2026-10-27 09:58:34 +01:00

174 lines
5.6 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\Discounts\StoreDiscountRequest;
use App\Http\Requests\Bo\Discounts\UpdateDiscountRequest;
use App\Models\Discount;
use App\Models\Product;
use App\Services\DataTableService;
use App\Traits\Controllers\ChangesModelOrder;
use App\Traits\Controllers\TogglesModelStatus;
use Domain\Enums\DiscountType;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class DiscountsController extends Controller
{
use ChangesModelOrder;
use TogglesModelStatus;
public function index(Request $request, DataTableService $dataTable): View
{
$this->authorize('viewAny', Discount::class);
$query = Discount::query();
$dataTable->sortQuery(Discount::class, $query);
$pagination = $request->pagination();
$discounts = $dataTable->paginateQuery($query, $pagination);
return view('bo.discounts.index', compact('discounts'));
}
public function create(): View
{
$this->authorize('create', Discount::class);
$discountTypes = DiscountType::cases();
$existingProds = Product::query()
->select(['id', 'name'])
->orderBy('name')
->get();
$selectedProds = [];
return view('bo.discounts.create', compact(
'discountTypes',
'existingProds',
'selectedProds'
));
}
public function store(StoreDiscountRequest $request): RedirectResponse
{
$this->authorize('create', Discount::class);
return DB::transaction(function () use ($request) {
$discount = new Discount();
$discount->fill($request->only(
$discount->getFillable()
));
$discount->value = $request->getDiscountValue();
if ($discount->saveOrFail()) {
$this->syncAssociatedProducts(
$request->getAssociatedProductsIds(),
$discount
);
return to_route('bo.discounts.edit', $discount)
->with('success', trans('crud.messages.has_been_created', [
'model' => Str::ucfirst(trans_choice('models.classes.discounts', 1)),
]));
}
return to_route('bo.discounts.create')
->with('error', trans('crud.messages.cannot_be_created', [
'model' => Str::ucfirst(trans_choice('models.classes.discounts', 1)),
]));
});
}
public function edit(Discount $discount): View
{
$this->authorize('update', $discount);
$discountTypes = DiscountType::cases();
$selectedProds = $discount->products()
->select(['products.id', 'products.name'])
->get();
$existingProds = Product::query()
->select(['id', 'name'])
->whereNotIn('id', $selectedProds->pluck('id'))
->orderBy('name')
->get();
return view('bo.discounts.edit', compact(
'discount',
'discountTypes',
'existingProds',
'selectedProds',
));
}
public function update(UpdateDiscountRequest $request, Discount $discount): RedirectResponse
{
$this->authorize('update', $discount);
return DB::transaction(function () use ($request, $discount) {
$discount->fill($request->only(
$discount->getFillable()
));
$discount->value = $request->getDiscountValue();
$this->syncAssociatedProducts(
$request->getAssociatedProductsIds(),
$discount
);
if ($discount->saveOrFail()) {
return to_route('bo.discounts.edit', $discount)
->with('success', trans('crud.messages.has_been_updated', [
'model' => Str::ucfirst(trans_choice('models.classes.discounts', 1)),
]));
}
return to_route('bo.discounts.edit', $discount)
->with('error', trans('crud.messages.cannot_be_updated', [
'model' => Str::ucfirst(trans_choice('models.classes.discounts', 1)),
]));
});
}
public function destroy(Discount $discount): RedirectResponse
{
$this->authorize('delete', $discount);
return DB::transaction(function () use ($discount) {
if ($discount->deleteOrFail()) {
return to_route('bo.discounts.index')
->with('warning', trans('crud.messages.has_been_deleted', [
'model' => Str::ucfirst(trans_choice('models.classes.discounts', 1)),
]));
}
return to_route('bo.discounts.edit', $discount)
->with('error', trans('crud.messages.cannot_be_deleted', [
'model' => Str::ucfirst(trans_choice('models.classes.discounts', 1)),
]));
});
}
/** @param array<string> $productIdList */
private function syncAssociatedProducts(array $productIdList, Discount $discount): void
{
$discount->products()
->whereNotIn('id', $productIdList)
->update(['discount_id' => null]);
if (! empty($productIdList)) {
Product::query()
->whereIn('id', $productIdList)
->update(['discount_id' => $discount->id]);
}
}
}