beefeater / crud-event-bundle
Event-based CRUDL bundle with configurable filtering, pagination, and sorting for Symfony applications
Installs: 286
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
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 deserialized
- validate()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]=value
- filter[field][operator]=value
Supported operators:
| Operator | Description | 
|---|---|
| eq | equals (default) | 
| like | substring match | 
| gte | greater or equal | 
| lte | less or equal | 
| gt | greater than | 
| lt | less than | 
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
π¦ 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.before_persist
- crud_event.create.before_persist
- {resource}.create.after_persist
- crud_event.create.after_persist
Update
- {resource}.update.before_persist
- crud_event.update.before_persist
- {resource}.update.after_persist
- crud_event.update.after_persist
Patch
- {resource}.patch.before_persist
- crud_event.patch.before_persist
- {resource}.patch.after_persist
- crud_event.patch.after_persist
Delete
- {resource}.delete.before_remove
- crud_event.delete.before_remove
- {resource}.delete.after_remove
- crud_event.delete.after_remove
List
- {resource}.list.list_settings
- crud_event.list.filter_build
π 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.