beefeater/crud-event-bundle

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

Installs: 409

Dependents: 0

Suggesters: 0

Security: 0

Stars: 1

Watchers: 0

Forks: 0

Open Issues: 0

Type:symfony-bundle

pkg:composer/beefeater/crud-event-bundle

1.9 2025-11-20 14:54 UTC

This package is auto-updated.

Last update: 2025-11-20 14:57:36 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) (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

πŸ“¦ 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_persist
  • crud_event.create.before_persist
  • {resource}.create.after_persist
  • crud_event.create.after_persist

Update

  • {resource}.update.on_request
  • {resource}.update.before_persist
  • crud_event.update.before_persist
  • {resource}.update.after_persist
  • crud_event.update.after_persist

Patch

  • {resource}.patch.on_request
  • {resource}.patch.before_persist
  • crud_event.patch.before_persist
  • {resource}.patch.after_persist
  • crud_event.patch.after_persist

Delete

  • {resource}.delete.on_request
  • {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

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; 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.