jmf / crud-engine-bundle
CRUD engine bundle for Symfony
Package info
github.com/jmfeurprier/crud-engine-bundle
Type:symfony-bundle
pkg:composer/jmf/crud-engine-bundle
Requires
- php: >=8.3
- doctrine/instantiator: ^2.0
- doctrine/orm: ^3.0
- jmf/template-rendering: ^1.0|^2.0
- symfony/form: ^7.0|^8.0
- symfony/framework-bundle: ^7.0|^8.0
- symfony/http-foundation: ^7.0|^8.0
- symfony/routing: ^7.0|^8.0
- symfony/security-bundle: ^7.0|^8.0
- twig/extra-bundle: ^3.0
- twig/string-extra: ^3.0
- webmozart/assert: ^1.0|^2.0
Requires (Dev)
- overtrue/phplint: ^9.7.1
- phpstan/phpstan: ^2.1.42
- phpstan/phpstan-strict-rules: ^2.0.10
- phpunit/phpunit: ^12.5.14|^13.0.5
- rector/rector: ^2.3.9
- squizlabs/php_codesniffer: ^4.0.1
- 7.4.x-dev
- 7.4.0
- 7.3.x-dev
- 7.3.0
- 7.2.x-dev
- 7.2.0
- 7.1.x-dev
- 7.1.0
- 7.0.x-dev
- 7.0.0
- 7.0.0-rc1
- 6.0.x-dev
- 6.0.3
- 6.0.2
- 6.0.1
- 6.0.0
- 5.1.x-dev
- 5.1.2
- 5.1.1
- 5.1.0
- 5.0.x-dev
- 5.0.1
- 5.0.0
- 5.0.0-rc1
- 4.3.x-dev
- 4.3.1
- 4.3.0
- 4.3.0-rc1
- 4.2.x-dev
- 4.2.0
- 4.2.0-rc1
- 4.1.x-dev
- 4.1.0
- 4.0.x-dev
- 4.0.0
- 3.0.x-dev
- 3.0.1
- 3.0.0
- 3.0.0-rc2
- 3.0.0-rc1
- 2.0.x-dev
- 2.0.0
- 1.0.x-dev
- 1.0.0
- 1.0.0-rc3
- 1.0.0-rc2
- 1.0.0-rc1
- dev-master
This package is auto-updated.
Last update: 2026-06-02 14:23:02 UTC
README
A Symfony bundle that automates CRUD (Create, Read, Update, Delete) controllers and routes for Doctrine entities based on configuration, eliminating repetitive boilerplate code.
Requirements
- PHP 8.3+
- Symfony 7.0 or 8.0
- Doctrine ORM 3.0+
Installation
composer require jmf/crud-engine-bundle
Register the bundle in config/bundles.php if not using Symfony Flex:
return [ // ... Jmf\CrudEngine\JmfCrudEngineBundle::class => ['all' => true], ];
Quick Start
1. Configure the bundle
Create config/packages/jmf_crud_engine.yaml:
jmf_crud_engine: entities: App\Entity\Article: actions: create: redirection: route: article.index delete: redirection: route: dashboard index: read: update: redirection: route: article.index
2. Load the routes
In config/routes.yaml:
jmf_crud_engine: resource: 'Jmf\CrudEngine\Routing\RouteLoader' type: service
3. Create templates
Create Twig templates for each action (e.g., templates/article/index.html.twig, templates/article/create.html.twig, etc.).
That's it — the bundle automatically registers routes and wires up controllers for all configured actions.
Actions
The bundle provides five actions, each mapped to a controller:
| Action | Default Route Path | HTTP Methods | Form | Description |
|---|---|---|---|---|
index |
/articles |
GET | No | Lists all entities |
read |
/articles/{id} |
GET | No | Displays a single entity |
create |
/articles/new |
GET, POST | Yes | Creates a new entity |
update |
/articles/{id}/edit |
GET, POST | Yes | Updates an existing entity |
delete |
/articles/{id}/delete |
GET, POST | No | Deletes an entity (POST confirms) |
Route paths and names are derived from the entity class name by default and can be overridden via configuration.
Configuration Reference
jmf_crud_engine: schema: # Default patterns for auto-discovering helper classes (Twig-style placeholders) helper: - "App\\Controller\\{{ EntityKey }}\\{{ ActionKey }}ActionHelper" - "App\\Controller\\{{ EntityKey }}{{ ActionKey }}ActionHelper" # Default patterns for auto-discovering form types formType: - "App\\Form\\{{ EntityKey }}\\{{ ActionKey }}Type" - "App\\Form\\{{ EntityKey }}{{ ActionKey }}Type" - "App\\Form\\{{ EntityKey }}Type" # Default view path pattern view: path: "{{ entity_key }}/{{ action_key }}.html.twig" entities: App\Entity\Aricle: # Roles required for all actions on this entity (optional) roles: - ROLE_ADMIN actions: index: ~ read: ~ create: # Override the form type (optional) formType: App\Form\Article\CreateType # Override the helper service (optional) helper: App\Controller\Article\CreateActionHelper # Redirect after a successful form submission (required for create/update/delete) redirection: route: article.index parameters: id: "{{ _entity.id }}" # Twig expression using the entity fragment: section # Optional URL fragment # Roles required for this action only (optional, overrides entity roles) roles: - ROLE_EDITOR # Override route configuration (optional) route: path: /blog/new parameters: {} requirements: id: '\d+' # Override template configuration (optional) view: path: article/create.html.twig variables: # Map template variable names to alternatives accepted in the template form: [articleForm, newArticleForm] update: redirection: route: article.index delete: redirection: route: dashboard
Template Placeholders
Configuration values and default patterns support the following placeholders:
| Placeholder | Example (Article) |
Description |
|---|---|---|
{{ EntityKey }} |
Article |
Entity class name |
{{ EntityKeys }} |
Articles |
Pluralized entity class name |
{{ entityKey }} |
article |
Camel-cased entity key |
{{ entity_key }} |
article |
Snake-cased entity key |
{{ ActionKey }} |
Create |
Action name (Pascal case) |
{{ actionKey }} |
create |
Action name (camel case) |
{{ action_key }} |
create |
Action name (snake case) |
Action Helpers
Action helpers allow you to customize behavior at specific lifecycle hooks without replacing the entire controller. Create a class implementing the appropriate interface and register it as a Symfony service.
If the class name matches a configured default pattern (e.g., App\Controller\Article\CreateActionHelper), it is picked up automatically. Otherwise, set helper explicitly in the action configuration.
Create / Update Helper
use Jmf\CrudEngine\Controller\Helpers\CreateActionHelperInterface; class ArticleCreateActionHelper implements CreateActionHelperInterface { /** * Instantiate the new entity (optional — skipping uses Doctrine Instantiator). */ public function createEntity(): object { return new Article(); } /** * Called before the entity is persisted. */ public function hookBeforePersist(object $entity, array $parameters): void { // e.g. set timestamps, assign an owner } /** * Persist the entity (optional — skipping uses default EntityManager::persist + flush). */ public function persist(object $entity): void { // custom persistence logic } /** * Called after the entity is persisted. */ public function hookAfterPersist(object $entity, array $parameters): void { // e.g. dispatch domain events } /** * Extra variables passed to the template. */ public function getViewVariables(object $entity, array $parameters): array { return []; } }
Index Helper
use Jmf\CrudEngine\Controller\Helpers\IndexActionHelperInterface; class ArticleIndexActionHelper implements IndexActionHelperInterface { public function getEntities(array $parameters): iterable { // return custom entity collection } public function hookBeforeRender(iterable $entities, array $parameters): void { } public function getViewVariables(iterable $entities, array $parameters): array { return []; } }
Read Helper
use Jmf\CrudEngine\Controller\Helpers\ReadActionHelperInterface; class ArticleReadActionHelper implements ReadActionHelperInterface { public function getViewVariables(object $entity, array $parameters): array { return []; } }
Delete Helper
use Jmf\CrudEngine\Controller\Helpers\DeleteActionHelperInterface; class ArticleDeleteActionHelper implements DeleteActionHelperInterface { public function hookBeforeRemove(object $entity, array $parameters): void { } public function remove(object $entity): void { } public function hookAfterRemove(object $entity, array $parameters): void { } public function onFailure(object $entity, array $parameters): \Symfony\Component\HttpFoundation\Response { } public function getViewVariables(object $entity, array $parameters): array { return []; } }
Templates
Templates receive the entity (or collection) as a variable. The variable name is derived from the entity key (e.g., article for App\Entity\Article, articles for the index action).
Forms are passed as form (a FormView instance).
Example: templates/article/index.html.twig
{% for article in articles %}
<h2>{{ article.title }}</h2>
{% endfor %}
Example: templates/article/create.html.twig
{{ form_start(form) }}
{{ form_widget(form) }}
<button type="submit">Create</button>
{{ form_end(form) }}
Security
Access can be restricted at entity level or per-action using roles:
jmf_crud_engine: entities: App\Entity\Article: roles: [ROLE_ADMIN] # applies to all actions actions: create: roles: [ROLE_EDITOR] # overrides entity-level roles for this action
License
MIT