beefeater/crud-event-bundle

Event-based CRUDL bundle with configurable filtering, pagination, and sorting for Symfony applications

v1.5.0 2025-07-30 17:06 UTC

This package is auto-updated.

Last update: 2025-07-30 17:09:24 UTC


README

Build Status

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 List operations
  • 🧩 before and after events for persist, update, delete, patch, and list
  • βš™οΈ 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; includes ConstraintViolationListInterface for 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:

  1. Install the Symfony Monolog Bundle if you haven’t already:
composer require symfony/monolog-bundle
  1. Configure a dedicated logging channel and handler for crud_event in your config/packages/monolog.yaml file. For example, in the dev environment:
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"]
  1. You can similarly add configurations for when@test just change log file path %kernel.logs_dir%/crud_event_test.log. For the when@prod environment, it's recommended to keep the default logging setup using the fingers_crossed handler

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.