agence-adeliom/easy-common-bundle
Symfony bundle providing common utilities for EasyAdmin: reusable Doctrine entity traits (ID, slug, timestamps, soft delete, publish/status) and a PHP 8 enum polyfill/helper with static constructors, validation, and typed enum parameters.
Installation:
composer require agence-adeliom/easy-common-bundle
Ensure your config/bundles.php includes:
return [
// ...
AgenceAdeliom\EasyCommonBundle\EasyCommonBundle::class => ['all' => true],
];
First Use Case:
Add a trait to an EasyAdmin entity (e.g., src/Entity/Post.php):
use AgenceAdeliom\EasyCommonBundle\Entity\Traits\EntityIdTrait;
use AgenceAdeliom\EasyCommonBundle\Entity\Traits\EntityTimestampableTrait;
class Post extends BaseEntity
{
use EntityIdTrait;
use EntityTimestampableTrait;
}
Verify Database Migration:
Run php bin/console make:migration to generate fields for id, created_at, updated_at (if using TimestampableTrait).
EntityIdTrait, EntityTimestampableTrait).// BaseEntity.php (abstract)
abstract class BaseEntity
{
use EntityIdTrait;
use EntityTimestampableTrait;
}
Extend BaseEntity for all models to enforce consistency.published, draft).use AgenceAdeliom\EasyCommonBundle\Enum\Enum;
class PostStatus extends Enum
{
public const DRAFT = 'draft';
public const PUBLISHED = 'published';
}
Use in EntityStatusTrait:
use EntityStatusTrait;
class Post extends BaseEntity
{
use EntityStatusTrait;
protected string $statusField = 'status';
protected string $statusEnum = PostStatus::class;
}
use EntitySoftDeletableTrait;
class Post extends BaseEntity
{
use EntitySoftDeletableTrait;
protected string $deletedAtField = 'deleted_at';
}
Configure EasyAdmin CRUD:
->setEntityClass(Post::class)
->setDefaultSort(['id' => 'DESC'])
->ignoreSoftDeleted()
name fields.use EntityNameSlugTrait;
class Post extends BaseEntity
{
use EntityNameSlugTrait;
protected string $nameField = 'title';
protected string $slugField = 'slug';
}
Add to EasyAdmin CRUD:
->setFormOptions([
'validation_groups' => function (Post $post) {
return ['Default', 'slug'];
},
])
Filtering Soft-Deleted Entities:
->setDefaultSort(['deleted_at' => 'ASC'])
->setFilterOptions([
'deleted_at' => Filter\BooleanFilter::new()->setLabel('Deleted'),
])
Enum Dropdown in Forms:
->setFormOptions([
'fields' => [
'status' => Form\ChoiceType::new()
->setChoices(PostStatus::toArray())
->setPlaceholder('Select status'),
],
])
use Symfony\Component\Validator\Constraints as Assert;
class Post extends BaseEntity
{
#[Assert\Unique(entityManager: 'doctrine', message: 'Slug already exists')]
protected ?string $slug = null;
}
PHP Version Mismatch:
3.x with PHP < 8.2 or Symfony < 6.4.composer.json:
"require": {
"agence-adeliom/easy-common-bundle": "^2.x"
}
Trait Conflicts:
getId() in EntityIdTrait vs. custom id property).#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private ?int $id = null;
Enum Polyfill:
use AgenceAdeliom\EasyCommonBundle\Enum\Enum; if using native enums.Soft Deletes in Queries:
ignoreSoftDeleted() in CRUD or manually scope:
$posts = $entityManager->getRepository(Post::class)
->findBy([], [], null, null, ['deleted_at' => 'ASC']);
Missing Fields in Migration:
php bin/console doctrine:schema:validate
Enum Values Not Showing:
statusEnum:
protected string $statusEnum = PostStatus::class; // Must match namespace
Customizing Slug Generation:
generateSlug() in EntityNameSlugTrait:
public function generateSlug(): string
{
return strtolower(parent::generateSlug()) . '-custom';
}
Adding Custom Statuses:
EntityStatusTrait:
use EntityStatusTrait;
class Post extends BaseEntity
{
use EntityStatusTrait;
public function getStatusOptions(): array
{
return [
'draft' => 'Draft',
'published' => 'Published',
'archived' => 'Archived', // Custom
];
}
}
Timestamp Precision:
EntityTimestampableTrait to use DateTimeImmutable:
#[ORM\Column(type: 'datetime_immutable')]
private ?\DateTimeImmutable $updatedAt = null;
EasyAdmin Version Compatibility:
setFilterOptions vs. setFilters).Doctrine Event Listeners:
EntityTimestampableTrait, ensure prePersist/preUpdate listeners are not duplicated in your entity.Slug Collisions:
generateSlug():
public function generateSlug(): string
{
$slug = strtolower($this->name);
$originalSlug = $slug;
$count = 1;
while ($this->repository->findOneBy(['slug' => $slug])) {
$slug = "{$originalSlug}-{$count}";
$count++;
}
return $slug;
}
How can I help you explore Laravel packages today?