Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Attribute/Crud.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
final public const FETCH_MANUAL = 'manual';

/**
* @var string auto|manual
* @var 'auto'|'manual'|null
* Check if fields should be fetched automatically or manually
*/
public ?string $fetchMode;
Expand Down
5 changes: 4 additions & 1 deletion Attribute/Field.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
readonly class Field
{

/**
* @param array<string, mixed> $options
*/
public function __construct(
public ?string $label = null,
public ?string $twigName = null,
Expand All @@ -20,4 +23,4 @@ public function __construct(
public mixed $payload = null
) {}

}
}
72 changes: 58 additions & 14 deletions Controller/Crud.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\QueryBuilder;
use Exception;
use JetBrains\PhpStorm\ExpectedValues;
use Knp\Component\Pager\PaginatorInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
Expand All @@ -38,15 +39,18 @@
use Symfony\Component\String\Slugger\SluggerInterface;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use function count;

/**
* Creates a Crud for a given entity managed by Doctrine.
* @template T
* @template T of object
*/
abstract class Crud extends AbstractController
{
private const ITEMS_PER_PAGE = 15;
protected EntityManagerInterface $em;

/** @var ClassMetadata<T> */
protected ClassMetadata $metadata;
protected FieldService $fieldService;
protected ?Request $request = null;
Expand All @@ -60,10 +64,10 @@ abstract class Crud extends AbstractController
/** @var EntityRepository<T> */
protected EntityRepository $repository;

/** @var Fields|Field[] */
/** @var Fields<T> */
protected Fields $fields;

/** @var Filters|Filter[] */
/** @var Filters<T> */
protected Filters $filters;

/** If the Crud is active and fully loaded */
Expand Down Expand Up @@ -177,6 +181,7 @@ public function getGlobalActions(): Actions
}

/**
* @param T $entity
* Actions that can be applied to a single existing entity, such as "Edit" or "Delete"
*/
public function getActions($entity): Actions
Expand Down Expand Up @@ -211,6 +216,9 @@ public function getActions($entity): Actions

/**
* All the actions available for a list of entities
*
* @param iterable<T> $entities
* @return Actions[]
*/
public function getActionsPerEntities(iterable $entities): array
{
Expand All @@ -224,6 +232,8 @@ public function getActionsPerEntities(iterable $entities): array

/**
* The batch actions available for a lit of entities
*
* @param iterable<T> $entities
*/
public function getBatchActions(iterable $entities): Actions
{
Expand Down Expand Up @@ -254,6 +264,7 @@ public function getListEntityActionsDisplayMode(): EntityActionsDisplayMode

/**
* Removes an entity
* @param T $entity
*/
public function deleteAction($entity): Response
{
Expand Down Expand Up @@ -284,7 +295,7 @@ public function deleteBatchAction(): Response
return $this->redirectToList();
}
$checked = $this->request->request->all('batch-actions');
$nbChecked = \count($checked);
$nbChecked = count($checked);
foreach ($checked as $k => $v) {
/** @var T $entity */
$entity = $this->repository->find($k);
Expand Down Expand Up @@ -323,13 +334,13 @@ public function toggleBooleanPostAction(Request $request, $entity): Response
if (!$this->isEditableBoolean($entity)) {
throw $this->createAccessDeniedException("Entity {$this->getEntity()} cannot be edited (boolean).");
}

$index = $request->request->get('index');
$value = $request->request->getBoolean('checked');
$propertyAccessor = PropertyAccess::createPropertyAccessor();
try {
$propertyAccessor->setValue($entity, $index, $value);
} catch (\Exception) {
} catch (Exception) {
throw $this->createAccessDeniedException("Entity {$this->getEntity()}'s property $index cannot be read or written");
}

Expand Down Expand Up @@ -600,7 +611,7 @@ public function exportAction(Request $request): Response
foreach ($fields as $field) {
try {
$value = $propertyAccessor->getValue($entity, $field->getIndex());
} catch (\Exception) {
} catch (Exception) {
$value = '';
}

Expand Down Expand Up @@ -652,6 +663,7 @@ protected function createNew(): object

/**
* Overrides a form type. By default, forms are created using a custom formBuilder.
* @param T $entity
*/
protected function overrideFormType($entity, bool $creation): ?string
{
Expand Down Expand Up @@ -703,6 +715,8 @@ public function getForm($entity, bool $creation): FormInterface

/**
* Returns all the entity's attributes that will be turned into Fields.
*
* @return string[]
*/
protected function getAllEntityFields(): array
{
Expand All @@ -725,9 +739,9 @@ protected function getAllEntityFields(): array
}
}
}
$methods = $this->metadata->getReflectionClass()?->getMethods(\ReflectionMethod::IS_PUBLIC);
$methods = $this->metadata->getReflectionClass()->getMethods(\ReflectionMethod::IS_PUBLIC);
foreach ($methods as $method) {
if (\count($method->getAttributes(\Arkounay\Bundle\QuickAdminGeneratorBundle\Attribute\Field::class)) === 1) {
if (count($method->getAttributes(\Arkounay\Bundle\QuickAdminGeneratorBundle\Attribute\Field::class)) === 1) {
$res[] = $method->getName();
}
}
Expand Down Expand Up @@ -759,6 +773,8 @@ protected function getFieldFetchMode(): string

/**
* Fields that will be used to display the list of entities.
*
* @return Fields<T>
*/
protected function getListingFields(): Fields
{
Expand All @@ -767,6 +783,8 @@ protected function getListingFields(): Fields

/**
* Fields that will be used to display the detail of an entity.
*
* @return Fields<T>
*/
protected function getViewFields(): Fields
{
Expand All @@ -775,6 +793,8 @@ protected function getViewFields(): Fields

/**
* Fields that will be used for the export of an entity.
*
* @return Fields<T>
*/
protected function getExportFields(): Fields
{
Expand All @@ -783,12 +803,17 @@ protected function getExportFields(): Fields

/**
* Fields that will be used to automatically generate the form in the create / edit actions.
*
* @return Fields<T>
*/
protected function getFormFields(): Fields
{
return clone $this->fields->filter(static fn(Field $field) => $field->isDisplayedInForm());
}

/**
* @return Filters<T>
*/
protected function getFilters(): Filters
{
return $this->filters;
Expand All @@ -812,6 +837,8 @@ public function load(Request $request): void

/**
* Creates a Fields object without any field by default
*
* @return Fields<T>
*/
protected function createFields(): Fields
{
Expand All @@ -820,6 +847,8 @@ protected function createFields(): Fields

/**
* Creates a Fields object with default fields
*
* @return Fields<T>
*/
protected function createFieldsFromMetadata(): Fields
{
Expand Down Expand Up @@ -858,6 +887,9 @@ protected function createFieldsFromMetadata(): Fields
return $fields;
}

/**
* @return Filters<T>
*/
final protected function createFilters(): Filters
{
return new Filters($this->metadata, $this->fieldService);
Expand Down Expand Up @@ -956,6 +988,7 @@ protected function createFilterForm(): FormBuilderInterface
/**
* All the actions that will generate Routes.
* Every functions that end with "Actions" will be considered as an Action and thus, a new route will be automatically created.
* @return array<string>
*/
public function getAllActions(): array
{
Expand All @@ -972,7 +1005,7 @@ public function getAllActions(): array

/**
* Finds the entity from an id.
* @return T
* @return T|null
*/
public function guessEntity()
{
Expand All @@ -992,11 +1025,12 @@ public function getDescription(): string
}

/**
* The default paginations options. Used to add a default sorting on the listing page.
* The default pagination options. Used to add a default sorting on the listing page.
* @param Fields<T> $fields
* @return array<string, string>
*/
protected function getPaginationOptions(Fields $fields): array
{
/** @var Fields|Field[] $fields */
foreach ($fields as $field) {
if ($field->getDefaultSortDirection() !== null) {
return ['defaultSortFieldName' => 'e.' . $field->getIndex(), 'defaultSortDirection' => $field->getDefaultSortDirection()];
Expand All @@ -1017,7 +1051,7 @@ protected function getPaginationOptions(Fields $fields): array
/**
* True by default
* If true, the responsive mode will be simplified, there won't be a table but a simple list that will display entity's toString().
* This removes batch actions and fields informations.
* This removes batch actions and fields information.
* Return false to use a responsive table with more data instead.
*/
protected function simpleResponsiveMode(): bool
Expand Down Expand Up @@ -1052,6 +1086,9 @@ protected function backUrl(): string
return $this->generateUrl('qag.' . $this->getRoute(), $this->getListRouteParams());
}

/**
* @return array<string, mixed>
*/
protected function getListRouteParams(): array
{
$params = array_merge($this->request->query->all(), $this->request->get('referer', []));
Expand All @@ -1061,11 +1098,12 @@ protected function getListRouteParams(): array

/**
* Checks if there are actions to display in the page, so the last column can be removed if there are not.
* @param array<int, Actions<int|string>> $actionEntities
*/
protected function hasActions(array $actionEntities): bool
{
foreach ($actionEntities as $actions) {
if (\count($actions) > 0) {
if (count($actions) > 0) {
return true;
}
}
Expand All @@ -1085,6 +1123,7 @@ protected function hasQuickListQueryBuilderSecurity(): bool

/**
* Used to check if the entity is a part of the getListQueryBuilder
* @param T $entity
*/
protected function entityIsInList($entity): bool
{
Expand All @@ -1097,6 +1136,8 @@ protected function entityIsInList($entity): bool

/**
* Allows params overriding before twig rendering
* @param array<string, mixed> $params
* @return array<string, mixed>
*/
protected function retrieveParams(string $action, array $params): array
{
Expand All @@ -1107,6 +1148,9 @@ protected function retrieveParams(string $action, array $params): array
], $params);
}

/**
* @return array{0: bool, 1: string|null, 2: \Symfony\Component\Form\FormInterface|null, 3: int}
*/
protected function applySearchAndFiltersQueryBuilder(Request $request, QueryBuilder $queryBuilder): array
{
$isSearchable = $this->isSearchable();
Expand Down
Loading