beefeater / crud-event-bundle
Event-based CRUDL bundle with configurable filtering, pagination, and sorting for Symfony applications
Installs: 533
Dependents: 0
Suggesters: 0
Security: 0
Stars: 1
Watchers: 0
Forks: 0
Open Issues: 0
Type:symfony-bundle
pkg:composer/beefeater/crud-event-bundle
Requires
- php: >=8.1
- doctrine/doctrine-bundle: ^2.10
- doctrine/orm: ^3.0
- doctrine/persistence: ^3.3
- symfony/dependency-injection: ^7.2
- symfony/event-dispatcher: ^7.2
- symfony/framework-bundle: ^7.2
- symfony/http-foundation: ^7.2
- symfony/http-kernel: ^7.2
- symfony/routing: ^7.2
- symfony/serializer: ^7.2
- symfony/uid: ^7.2
- symfony/validator: ^7.2
- symfony/yaml: ^7.2
Requires (Dev)
- phpunit/phpunit: ^12.2
- squizlabs/php_codesniffer: ^3.13
This package is auto-updated.
Last update: 2026-01-29 17:51:07 UTC
README
Beefeater CRUD Event Bundle is a powerful Symfony bundle for rapid REST API generation with built-in support for CRUD operations, events, pagination, sorting, and filtering.
π¦ Key Features
- π Auto-generation of CRUD routes (
Create,Read,Update,Delete,List,Patch) based on YAML configuration - π API versioning support (e.g.,
v1,v2) - π Pagination, sorting, and filtering for
Listoperations - π§©
beforeandafterevents forpersist,update,delete,patch, andlist - βοΈ Extensible via custom
EventListeners(e.g., for logging, notifications, etc.) - β Symfony Validator integration for request data validation
- π§ Built-in support for Doctrine ORM
- βοΈ Partial updates via
PATCH - π Route parameters support including nested resources and UUIDs
π§ Installation
Install the bundle via Composer:
composer require beefeater/crud-event-bundle
π Usage
Register Routes
Add the following to config/routes.yaml:
crud_api_v1: resource: '%kernel.project_dir%/config/crud_routes_v1.yaml' type: crud_routes
Example: crud_routes_v1.yaml
version: v1 resources: tournaments: entity: App\Entity\Tournament operations: [C, R, U, D, L, P] path: /tournaments categories: entity: App\Entity\Category operations: [C, R, U, D, L, P] path: /tournaments/{tournament}/categories params: tournament: App\Entity\Tournament
Routes generated for tournaments:
| Route Name | Method | Path |
|---|---|---|
| api_v1_tournaments_C | POST | /api/v1/tournaments |
| api_v1_tournaments_R | GET | /api/v1/tournaments/{id} |
| api_v1_tournaments_U | PUT | /api/v1/tournaments/{id} |
| api_v1_tournaments_D | DELETE | /api/v1/tournaments/{id} |
| api_v1_tournaments_L | GET | /api/v1/tournaments |
| api_v1_tournaments_P | PATCH | /api/v1/tournaments/{id} |
Routes generated for categories:
| Route Name | Method | Path |
|---|---|---|
| api_v1_categories_C | POST | /api/v1/tournaments/{tournament}/categories |
| api_v1_categories_R | GET | /api/v1/tournaments/{tournament}/categories/{id} |
| api_v1_categories_U | PUT | /api/v1/tournaments/{tournament}/categories/{id} |
| api_v1_categories_D | DELETE | /api/v1/tournaments/{tournament}/categories/{id} |
| api_v1_categories_L | GET | /api/v1/tournaments/{tournament}/categories |
| api_v1_categories_P | PATCH | /api/v1/tournaments/{tournament}/categories/{id} |
β οΈ If the
version:key is not specified in the configuration file (e.g.crud_routes_v1.yaml), the route paths will be built without any version prefix, for example:/api/categories/{id}.
π How It Works
- Routes are auto-generated from YAML config and handled by a central
CrudEventController - Symfony events are dispatched before and after each operation
- Incoming data is deserialized and validated using validation groups:
fromJson()accepts validation groups that control which fields are deserializedvalidate()runs validation based on those groups
- Validation groups can be set using PHP attributes/annotations:
#[Assert\NotBlank(groups: ['create'])] #[Groups(['create', 'update'])] private string $name;
This allows flexible validation rules depending on the operation.
π§© EventListeners
You can register event listeners to:
- Log actions
- Send notifications
- Modify or enrich data
- Handle errors
π Pagination, Sorting, Filtering
Pagination Parameters
| Parameter | Default |
|---|---|
page |
1 |
pageSize |
25 |
Example:
GET /api/v1/tournaments?page=2&pageSize=10
π Sorting
sort=+field1,-field2β ascending/descending- Example:
GET /api/v1/tournaments?sort=-age,+name
π§° Filtering
filter[field]=valuefilter[field][operator]=value
Supported operators:
| Operator | Description |
|---|---|
| eq | equals (default) (null safe) |
| like | substring match |
| gte | greater or equal |
| lte | less or equal |
| gt | greater than |
| lt | less than |
| neq | NOT equals (null safe) |
| in | value is in array (null safe) |
| nin | value is NOT in array (null safe) |
Boolean values supported: true, false, none |
Examples:
GET /api/v1/tournaments?filter[isActive]=true GET /api/v1/tournaments?filter[status][eq]=active GET /api/v1/tournaments?filter[rating][gte]=3&filter[rating][lte]=5 GET /api/v1/tournaments?filter[category][in][]=pro&filter[category][nin][]=amateur
π Quick Search
QuickSearch is an optional feature, enabled per resource via configuration and available only for the list operation.
It can be used independently or together with filter and adds LIKE %value% search conditions.
Configuration example:
version: v1 resources: categories: entity: App\Entity\Category operations: [C, R, U, D, L, P] path: /categories quick-search: [name, parent.name]
Behaviour
- Enabled explicitly per resource/controller
- Searches across all configured fields
- Fields are combined using
OR - Combined with
filterusingAND - Uses
LIKE %value%automatically
Example
GET /api/v1/categories?filter[isActive]=true&quickSearch=junior
Equivalent query logic:
WHERE
category.is_active = true
AND (
category.name LIKE '%junior%'
OR
parent.name LIKE '%junior%'
)
quickSearch supports both entity properties and related entity properties
(using dot notation: relation.property).
π¦ Nested Resources
If a parent ID (e.g., UUID) is present in the path (e.g., /api/v1/tournaments/{tournament}/categories), it is:
- Automatically resolved and injected
- Available for filtering
You can combine all parameters:
GET /api/v1/tournaments/{tournament}/categories?page=2&pageSize=10&sort=-age,+name&filter[rating][gte]=3&filter[rating][lte]=5
π’ Dispatched Events
Create
{resource}.create.on_request{resource}.create.before_persistcrud_event.create.before_persist{resource}.create.after_persistcrud_event.create.after_persist
Update
{resource}.update.on_request{resource}.update.before_persistcrud_event.update.before_persist{resource}.update.after_persistcrud_event.update.after_persist
Patch
{resource}.patch.on_request{resource}.patch.before_persistcrud_event.patch.before_persist{resource}.patch.after_persistcrud_event.patch.after_persist
Delete
{resource}.delete.on_request{resource}.delete.before_removecrud_event.delete.before_remove{resource}.delete.after_removecrud_event.delete.after_remove
List
{resource}.list.list_settingscrud_event.list.filter_build
Before deserialize
entity.before_deserialize
π Example Event Listener Registration
App\EventListener\TournamentCrudListener: tags: - { name: kernel.event_listener, event: 'tournaments.create.after_persist', method: onAfterPersist }
β Custom Exceptions
-
PayloadValidationException
Thrown when validation fails; includesConstraintViolationListInterfacefor detailed violation info. -
ResourceNotFoundException
Thrown when the requested resource is not found by ID.
You can register listeners to handle these exceptions globally.
π Logging
The Beefeater CRUD Event Bundle supports logging of key operations such as:
- Route creation
- Error logging
- Warning logging
How to Enable Logging in Your Project
To enable logging for this bundle, follow these steps:
- Install the Symfony Monolog Bundle if you havenβt already:
composer require symfony/monolog-bundle
- Configure a dedicated logging channel and handler for
crud_eventin yourconfig/packages/monolog.yamlfile. For example, in thedevenvironment:
monolog: channels: - crud_event # add this channel alongside your existing ones when@dev: monolog: handlers: crud_event: # add this handler alongside your existing ones type: stream path: "%kernel.logs_dir%/crud_event.log" level: debug channels: ["crud_event"]
- You can similarly add configurations for
when@testjust change log file path%kernel.logs_dir%/crud_event_test.log. For thewhen@prodenvironment, it's recommended to keep the default logging setup using thefingers_crossedhandler
All logs related to the Beefeater CRUD Event Bundle will be saved in:
var/log/crud_event.log
This setup allows you to conveniently monitor important bundle actions and errors separately from other Symfony logs.
π§© Controller Inheritance
CrudEventController exposes protected handle* methods for reuse in child controllers
You can define your own endpoints, run custom logic (e.g. SECURITY checks), and then call:
- parent::handleCreate(Request $request, string $resourceName, string $entityClass, ?string $version = null)
- parent::handleUpdate(Request $request, string $resourceName, string $entityClass, string $id, ?string $version = null)
- parent::handlePatch(Request $request, string $resourceName, string $entityClass, string $id, ?string $version = null)
- parent::handleDelete(Request $request, string $resourceName, string $entityClass, string $id, ?string $version = null)
- parent::handleList(Request $request, Page $page, Sort $sort, Filter $filter, string $resourceName)
β οΈ Note: When using handleList() in your child controllers, make sure to import the types from the bundle:
- use Beefeater\CrudEventBundle\Model\Page;
- use Beefeater\CrudEventBundle\Model\Sort;
- use Beefeater\CrudEventBundle\Model\Filter;
π Security
Security is optional and configured per resource and per operation.
version: v1 resources: tournaments: entity: App\Entity\Tournament operations: [C, R, U, D, L, P] path: /tournaments categories: entity: App\Entity\Category operations: [C, R, U, D, L, P] path: /tournaments/{tournament}/categories params: tournament: App\Entity\Tournament security: C: [ ROLE_USER ] U: [ ROLE_USER, ROLE_ADMIN ] D: [ ROLE_ADMIN ]
Supported operations:
Cβ CreateRβ ReadUβ UpdateDβ DeleteLβ ListPβ Patch
β οΈ If the
security:key is not specified in the configuration file (e.g.crud_routes_v1.yaml), security is ignored.β οΈ Security roles must be defined per operation using arrays for
C, R, U, D, L, P.
Missing an operation means public access for that operation.β οΈ Secured endpoints are marked with the
_securedsuffix in the route name, for example:
api_v1_categories_C_securedβ οΈ Multiple roles per operation are supported.
Access is granted if the user has at least one matching role.
The order of user roles does not matter.β οΈ The bundle fully respects Symfony role hierarchy (e.g.
ROLE_ADMINinheritsROLE_USER).β οΈ Security requires the
symfony/security-bundleto be installed.
If it is missing, security will be ignored.