333 lines
13 KiB
PHP
Executable file
333 lines
13 KiB
PHP
Executable file
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Controller\Admin;
|
|
|
|
use App\Controller\i18nTrait;
|
|
use App\EasyAdmin\Field\FieldTrait;
|
|
use App\EasyAdmin\Filter\EnumFilter;
|
|
use App\EasyAdmin\Filter\UuidFilter;
|
|
use App\EasyAdmin\Form\Type\ProductStatusType;
|
|
use App\EasyAdmin\Form\Type\ProductVisibilityType;
|
|
use App\Entity\Product;
|
|
use App\Enum\Product\ProductStatus;
|
|
use App\Enum\Product\ProductType;
|
|
use App\Enum\Product\ProductVisibility;
|
|
use App\Flysystem\EasyAdminHelper;
|
|
use App\Flysystem\MediaManager;
|
|
use App\Form\Type\Product\AbstractProductFormType;
|
|
use App\Helper\CsvExporter;
|
|
use App\Repository\CategoryRepository;
|
|
use App\Repository\GroupRepository;
|
|
use App\Repository\ProductRepository;
|
|
use Doctrine\ORM\QueryBuilder;
|
|
use EasyCorp\Bundle\EasyAdminBundle\Collection\FieldCollection;
|
|
use EasyCorp\Bundle\EasyAdminBundle\Collection\FilterCollection;
|
|
use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
|
|
use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;
|
|
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
|
|
use EasyCorp\Bundle\EasyAdminBundle\Config\Filters;
|
|
use EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext;
|
|
use EasyCorp\Bundle\EasyAdminBundle\Contracts\Field\FieldInterface;
|
|
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
|
|
use EasyCorp\Bundle\EasyAdminBundle\Dto\CrudDto;
|
|
use EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto;
|
|
use EasyCorp\Bundle\EasyAdminBundle\Dto\SearchDto;
|
|
use EasyCorp\Bundle\EasyAdminBundle\Factory\FilterFactory;
|
|
use EasyCorp\Bundle\EasyAdminBundle\Field\AssociationField;
|
|
use EasyCorp\Bundle\EasyAdminBundle\Field\ChoiceField;
|
|
use EasyCorp\Bundle\EasyAdminBundle\Field\CollectionField;
|
|
use EasyCorp\Bundle\EasyAdminBundle\Field\CurrencyField;
|
|
use EasyCorp\Bundle\EasyAdminBundle\Field\DateTimeField;
|
|
use EasyCorp\Bundle\EasyAdminBundle\Field\IdField;
|
|
use EasyCorp\Bundle\EasyAdminBundle\Field\ImageField;
|
|
use EasyCorp\Bundle\EasyAdminBundle\Field\MoneyField;
|
|
use EasyCorp\Bundle\EasyAdminBundle\Field\TextareaField;
|
|
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
|
|
use EasyCorp\Bundle\EasyAdminBundle\Router\AdminUrlGenerator;
|
|
use League\Flysystem\FilesystemOperator;
|
|
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
|
use Symfony\Component\Form\Extension\Core\Type\EnumType;
|
|
use Symfony\Component\HttpFoundation\RedirectResponse;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
use Symfony\Component\String\Slugger\SluggerInterface;
|
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
|
|
|
abstract class AbstractProductCrudController extends AbstractCrudController implements AdminSecuredCrudControllerInterface
|
|
{
|
|
use FieldTrait;
|
|
use i18nTrait;
|
|
|
|
abstract public function getProductType(): ProductType;
|
|
|
|
abstract public function getEntityLabelInPlural(): string;
|
|
|
|
abstract public function getEntityLabelInSingular(): string;
|
|
|
|
public function __construct(
|
|
private readonly AdminUrlGenerator $adminUrlGenerator,
|
|
private readonly ProductRepository $productRepository,
|
|
private readonly CategoryRepository $categoryRepository,
|
|
private readonly FilesystemOperator $productStorage,
|
|
private readonly EasyAdminHelper $easyAdminHelper,
|
|
private readonly MediaManager $mediaManager,
|
|
#[Autowire('%product_base_path%')]
|
|
private readonly string $productBasePath,
|
|
private readonly CsvExporter $csvExporter,
|
|
private readonly TranslatorInterface $translator,
|
|
private readonly FilterFactory $filterFactory,
|
|
private readonly SluggerInterface $slugger,
|
|
protected readonly GroupRepository $groupRepository,
|
|
) {
|
|
}
|
|
|
|
public function configureCrud(Crud $crud): Crud
|
|
{
|
|
return $crud
|
|
->setEntityLabelInPlural($this->getEntityLabelInPlural())
|
|
->setEntityLabelInSingular($this->getEntityLabelInSingular())
|
|
->setSearchFields(['name', 'description'])
|
|
->setDefaultSort(['createdAt' => 'DESC'])
|
|
->setFormThemes([
|
|
'@EasyAdmin/crud/form_theme.html.twig',
|
|
'easy_admin/crud/form_theme.html.twig',
|
|
])
|
|
->setFormOptions([
|
|
'validation_groups' => [AbstractProductFormType::class],
|
|
])
|
|
;
|
|
}
|
|
|
|
public function configureFilters(Filters $filters): Filters
|
|
{
|
|
return $filters
|
|
->add(UuidFilter::new('id'))
|
|
->add(EnumFilter::new('status', ProductStatusType::class))
|
|
->add(EnumFilter::new('visibility', ProductVisibilityType::class))
|
|
->add('category')
|
|
->add('owner')
|
|
->add('name')
|
|
->add('description')
|
|
;
|
|
}
|
|
|
|
public function configureActions(Actions $actions): Actions
|
|
{
|
|
$onBreak = Action::new('onBreak', 'action.onBreak')
|
|
->linkToCrudAction('changeStatus')
|
|
->displayIf(static function (Product $product) {
|
|
return $product->isActive();
|
|
});
|
|
|
|
$activate = Action::new('activate', 'action.activate')
|
|
->linkToCrudAction('changeStatus')
|
|
->displayIf(static function (Product $product) {
|
|
return $product->isPaused();
|
|
});
|
|
|
|
$availability = Action::new('availability', 'action.availability')
|
|
->linkToCrudAction('linkToProductAvailabilityPage');
|
|
|
|
$exportAction = Action::new('export')
|
|
->linkToUrl(function () {
|
|
/** @var AdminContext $context */
|
|
$context = $this->getContext();
|
|
|
|
return $this->adminUrlGenerator->setAll($context->getRequest()->query->all())
|
|
->setEntityId(null)
|
|
->setAction('export')
|
|
->generateUrl();
|
|
})
|
|
->addCssClass('btn btn-success')
|
|
->setIcon('fa fa-download')
|
|
->createAsGlobalAction()
|
|
;
|
|
|
|
return $actions
|
|
->add(Crud::PAGE_INDEX, Action::DETAIL)
|
|
->add(Crud::PAGE_EDIT, Action::DETAIL)
|
|
->add(Crud::PAGE_EDIT, Action::INDEX)
|
|
->add(Crud::PAGE_INDEX, $onBreak)
|
|
->add(Crud::PAGE_INDEX, $activate)
|
|
->add(Crud::PAGE_DETAIL, $availability)
|
|
->add(Crud::PAGE_INDEX, $exportAction);
|
|
}
|
|
|
|
private function redirectToObjectCrudPage(): RedirectResponse
|
|
{
|
|
$this->adminUrlGenerator->setController(ObjectCrudController::class)->setAction('index')->removeReferrer()->setEntityId(null);
|
|
|
|
return $this->redirect($this->adminUrlGenerator->generateUrl());
|
|
}
|
|
|
|
public function linkToProductAvailabilityPage(): Response
|
|
{
|
|
return $this->render('/admin/product/availability_product.html.twig');
|
|
}
|
|
|
|
public function changeStatus(AdminContext $context): Response
|
|
{
|
|
/** @var Product $product */
|
|
$product = $context->getEntity()->getInstance();
|
|
if ($product->isPaused()) {
|
|
$product->setStatus(ProductStatus::ACTIVE);
|
|
} else {
|
|
$product->setStatus(ProductStatus::PAUSED);
|
|
}
|
|
$this->productRepository->save($product, true);
|
|
|
|
return $this->redirectToObjectCrudPage();
|
|
}
|
|
|
|
/**
|
|
* For now, we export exactly what we see in the list to avoid security problems.
|
|
*/
|
|
public function export(AdminContext $context): Response
|
|
{
|
|
$fields = FieldCollection::new($this->configureFields(Crud::PAGE_INDEX));
|
|
/** @var CrudDto $crud Crud is defined here */
|
|
$crud = $context->getCrud();
|
|
$filters = $this->filterFactory->create($crud->getFiltersConfig(), $fields, $context->getEntity());
|
|
/** @var SearchDto $search */
|
|
$search = $context->getSearch();
|
|
$queryBuilder = $this->createIndexQueryBuilder($search, $context->getEntity(), $fields, $filters);
|
|
$fileName = $this->slugger->slug($this->translator->trans($this->getEntityLabelInPlural(), [], DashboardController::DOMAIN));
|
|
|
|
return $this->csvExporter->createResponseFromQueryBuilder($queryBuilder, $fields, $fileName.'.csv');
|
|
}
|
|
|
|
public static function getEntityFqcn(): string
|
|
{
|
|
return Product::class;
|
|
}
|
|
|
|
public function createEntity(string $entityFqcn): Product
|
|
{
|
|
/** @var Product $product */
|
|
$product = new $entityFqcn();
|
|
$product->setType($this->getProductType());
|
|
|
|
return $product;
|
|
}
|
|
|
|
/**
|
|
* Only display a given product type.
|
|
*/
|
|
public function createIndexQueryBuilder(SearchDto $searchDto, EntityDto $entityDto, FieldCollection $fields, FilterCollection $filters): QueryBuilder
|
|
{
|
|
$qb = parent::createIndexQueryBuilder($searchDto, $entityDto, $fields, $filters);
|
|
$alias = $qb->getRootAliases()[0] ?? null;
|
|
$qb->andWhere($alias.'.type = :type')
|
|
->setParameter('type', $this->getProductType());
|
|
|
|
return $qb;
|
|
}
|
|
|
|
/**
|
|
* Return all possible product fields.
|
|
*
|
|
* @return array<FieldInterface>
|
|
*/
|
|
public function getFields(string $pageName): array
|
|
{
|
|
$idField = IdField::new('id')
|
|
->setLabel('id')
|
|
->hideOnForm();
|
|
|
|
$typeField = ChoiceField::new('type')
|
|
->setFormType(EnumType::class)
|
|
->setFormTypeOption('class', ProductType::class)
|
|
->setChoices(ProductType::getAsArray());
|
|
|
|
$statusField = ChoiceField::new('status')
|
|
->setFormType(EnumType::class)
|
|
->setFormTypeOption('class', ProductStatus::class)
|
|
->setChoices(ProductStatus::getAsArray());
|
|
|
|
$visibilityField = ChoiceField::new('visibility')
|
|
->setFormType(EnumType::class)
|
|
->setFormTypeOption('class', ProductVisibility::class)
|
|
->setChoices(ProductVisibility::getAsArray());
|
|
$groupsField = AssociationField::new('groups')->onlyOnForms();
|
|
$groupsFieldList = CollectionField::new('groups')->hideOnForm();
|
|
|
|
$ownerField = AssociationField::new('owner')
|
|
->setFormTypeOption('attr', [
|
|
'data-controller' => 'admin-parentgroup',
|
|
'data-admin-parentgroup-target' => 'ownerField',
|
|
])
|
|
->addWebpackEncoreEntries('admin');
|
|
$categoryField = AssociationField::new('category')
|
|
->setQueryBuilder(function (QueryBuilder $queryBuilder) {
|
|
return $this->categoryRepository->addTypeFilter($queryBuilder, $this->getProductType());
|
|
})
|
|
;
|
|
|
|
$nameField = TextField::new('name');
|
|
$descriptionField = TextareaField::new('description');
|
|
|
|
// objects
|
|
$ageField = TextField::new('age');
|
|
$depositField = MoneyField::new('deposit')
|
|
->setCurrencyPropertyPath('currency')
|
|
->setStoredAsCents()
|
|
->setNumDecimals(0)
|
|
;
|
|
$currencyField = CurrencyField::new('currency');
|
|
|
|
// services
|
|
$durationField = TextField::new('duration');
|
|
|
|
$createdAt = DateTimeField::new('createdAt');
|
|
$updatedAt = DateTimeField::new('updatedAt');
|
|
|
|
$imageField = ImageField::new('images')
|
|
->setBasePath($this->productBasePath) // correctly set the formatted value available in the template
|
|
->setUploadDir('public'.$this->productBasePath)
|
|
->setUploadedFileNamePattern('[uuid].[extension]')
|
|
->setFormTypeOption('upload_new', $this->easyAdminHelper->getUploadNewCallback($this->productStorage))
|
|
->setFormTypeOption('upload_delete', $this->easyAdminHelper->getUploadDeleteCallback($this->productStorage))
|
|
->setFormTypeOption('required', false)
|
|
->setFormTypeOption('multiple', true)
|
|
->setFormTypeOption('constraints', $this->mediaManager->getImageArrayConstraints())
|
|
->setTemplatePath('easy_admin/field/flysystem_images.html.twig')
|
|
->setCustomOption('first_image_only', true)
|
|
->setHelp($this->mediaManager->getHelpMessage())
|
|
->setSortable(false)
|
|
;
|
|
|
|
$addressField = TextField::new('address');
|
|
|
|
if ($pageName === Crud::PAGE_DETAIL) {
|
|
/** @var Product $product */
|
|
$product = $this->getContext()?->getEntity()->getInstance();
|
|
|
|
$addressField = TextField::new('address')->setValue($product->getOwner()->getAddress()?->getDisplayName());
|
|
}
|
|
|
|
$preferredLoanDuration = TextField::new('preferredLoanDuration');
|
|
|
|
return compact(
|
|
'idField',
|
|
'typeField',
|
|
'statusField',
|
|
'visibilityField',
|
|
'groupsField',
|
|
'groupsFieldList',
|
|
'ownerField',
|
|
'categoryField',
|
|
'nameField',
|
|
'descriptionField',
|
|
'ageField',
|
|
'durationField',
|
|
'depositField',
|
|
'currencyField',
|
|
'createdAt',
|
|
'updatedAt',
|
|
'imageField',
|
|
'addressField',
|
|
'preferredLoanDuration',
|
|
);
|
|
}
|
|
}
|