mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
documentation for CRUD
This commit is contained in:
parent
d49c2ea36e
commit
79b9f0d274
509
source/development/crud.rst
Normal file
509
source/development/crud.rst
Normal file
@ -0,0 +1,509 @@
|
||||
.. Copyright (C) 2014 Champs Libres Cooperative SCRLFS
|
||||
Permission is granted to copy, distribute and/or modify this document
|
||||
under the terms of the GNU Free Documentation License, Version 1.3
|
||||
or any later version published by the Free Software Foundation;
|
||||
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
|
||||
A copy of the license is included in the section entitled "GNU
|
||||
Free Documentation License".
|
||||
|
||||
.. _crud:
|
||||
|
||||
CRUD
|
||||
####
|
||||
|
||||
Chill provide an API to create a basic CRUD.
|
||||
|
||||
One can follow those steps to create a CRUD for one entity:
|
||||
|
||||
1. create your model and your form ;
|
||||
2. configure the crud ;
|
||||
3. customize the templates if required ;
|
||||
4. customize some steps of the controller if required ;
|
||||
|
||||
|
||||
An example with the ``ClosingMotive`` (PersonBundle) in the admin part of Chill:
|
||||
|
||||
|
||||
Create your model
|
||||
*****************
|
||||
|
||||
Create your model on the usual way (in this example, ORM informations are stored in yaml file):
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
namespace Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
|
||||
/**
|
||||
* ClosingMotive give an explanation why we closed the Accompanying period
|
||||
*/
|
||||
class ClosingMotive
|
||||
{
|
||||
/**
|
||||
* @var integer
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
private $active = true;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var self
|
||||
*/
|
||||
private $parent = null;
|
||||
|
||||
/**
|
||||
* child Accompanying periods
|
||||
*
|
||||
* @var Collection
|
||||
*/
|
||||
private $children;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var float
|
||||
*/
|
||||
private $ordering = 0.0;
|
||||
|
||||
|
||||
// getters and setters come here
|
||||
|
||||
}
|
||||
|
||||
The form:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
namespace Chill\PersonBundle\Form;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Chill\PersonBundle\Form\Type\ClosingMotivePickerType;
|
||||
use Chill\MainBundle\Form\Type\TranslatableStringFormType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\NumberType;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*/
|
||||
class ClosingMotiveType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder
|
||||
->add('name', TranslatableStringFormType::class, [
|
||||
'label' => 'Nom'
|
||||
])
|
||||
->add('active', CheckboxType::class, [
|
||||
'label' => 'Actif ?',
|
||||
'required' => false
|
||||
])
|
||||
->add('ordering', NumberType::class, [
|
||||
'label' => 'Ordre d\'apparition',
|
||||
'required' => true,
|
||||
'scale' => 5
|
||||
])
|
||||
->add('parent', ClosingMotivePickerType::class, [
|
||||
'label' => 'Parent',
|
||||
'required' => false,
|
||||
'placeholder' => 'closing_motive.any parent',
|
||||
'multiple' => false,
|
||||
'only_leaf' => false
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver
|
||||
->setDefault('class', \Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive::class)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
Configure the crud
|
||||
******************
|
||||
|
||||
The crud is configured using the key ``crud`` under ``chill_main``
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
chill_main:
|
||||
cruds:
|
||||
-
|
||||
# the class which is concerned by the CRUD
|
||||
class: '\Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive::class'
|
||||
# give a name for the crud. This will be used internally
|
||||
name: closing_motive
|
||||
# add a base path for the
|
||||
base_path: /admin/closing-motive
|
||||
# this is the form class
|
||||
form_class: 'Chill\PersonBundle\Form\ClosingMotiveType::class'
|
||||
# you can override the controller to configure some parts
|
||||
# if you do not configure anything here, the default CRUDController will be used
|
||||
controller: 'Chill\PersonBundle\Controller\AdminClosingMotiveController::class'
|
||||
# this is a list of action you can configure
|
||||
# by default, the actions `index`, `view`, `new` and `edit` are automatically create
|
||||
# you can add more actions or configure some details about them
|
||||
actions:
|
||||
index:
|
||||
# the default template for index is very poor,
|
||||
# you will need to override it
|
||||
template: '@ChillPerson/ClosingMotive/index.html.twig'
|
||||
# the role required for this role
|
||||
role: ROLE_ADMIN
|
||||
new:
|
||||
role: ROLE_ADMIN
|
||||
# by default, the template will only show the form
|
||||
# you can override it
|
||||
template: '@ChillPerson/ClosingMotive/new.html.twig'
|
||||
edit:
|
||||
role: ROLE_ADMIN
|
||||
template: '@ChillPerson/ClosingMotive/edit.html.twig'
|
||||
|
||||
To leave the bundle auto-configure the ``chill_main`` bundle, you can `prepend the configuration of the ChillMain Bundle <https://symfony.com/doc/current/bundles/prepend_extension.html>`_:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
namespace Chill\PersonBundle\DependencyInjection;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
|
||||
|
||||
class ChillPersonExtension extends Extension implements PrependExtensionInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function load(array $configs, ContainerBuilder $container)
|
||||
{
|
||||
// skipped here
|
||||
}
|
||||
|
||||
|
||||
public function prepend(ContainerBuilder $container)
|
||||
{
|
||||
$this->prependCruds($container);
|
||||
}
|
||||
|
||||
protected function prependCruds(ContainerBuilder $container)
|
||||
{
|
||||
$container->prependExtensionConfig('chill_main', [
|
||||
'cruds' => [
|
||||
[
|
||||
'class' => \Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive::class,
|
||||
'name' => 'closing_motive',
|
||||
'base_path' => '/admin/closing-motive',
|
||||
'form_class' => \Chill\PersonBundle\Form\ClosingMotiveType::class,
|
||||
'controller' => \Chill\PersonBundle\Controller\AdminClosingMotiveController::class,
|
||||
'actions' => [
|
||||
'index' => [
|
||||
'template' => '@ChillPerson/ClosingMotive/index.html.twig',
|
||||
'role' => 'ROLE_ADMIN'
|
||||
],
|
||||
'new' => [
|
||||
'role' => 'ROLE_ADMIN',
|
||||
'template' => '@ChillPerson/ClosingMotive/new.html.twig',
|
||||
],
|
||||
'edit' => [
|
||||
'role' => 'ROLE_ADMIN',
|
||||
'template' => '@ChillPerson/ClosingMotive/edit.html.twig',
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Customize templates
|
||||
*******************
|
||||
|
||||
The current template are quite basic. You can override and extends them.
|
||||
|
||||
For a better inclusion, you can embed them instead of extending them.
|
||||
|
||||
For index. Note that we extend here the `admin` layout, not the default one:
|
||||
|
||||
.. code-block:: html+jinja
|
||||
|
||||
{% extends '@ChillMain/Admin/layout.html.twig' %}
|
||||
|
||||
{% block admin_content %}
|
||||
{% embed '@ChillMain/CRUD/_index.html.twig' %}
|
||||
{# we customize the table headers #}
|
||||
{% block table_entities_thead_tr %}
|
||||
<th>{{ 'Ordering'|trans }}</th>
|
||||
<th>{{ 'Label'|trans }}</th>
|
||||
<th>{{ 'Active'|trans }}</th>
|
||||
<th> </th>
|
||||
{% endblock %}
|
||||
|
||||
{% block table_entities_tbody %}
|
||||
{# we customize the content of the table #}
|
||||
{% for entity in entities %}
|
||||
<tr>
|
||||
<td>{{ entity.ordering }}</td>
|
||||
<td>{{ entity|chill_entity_render_box }}</td>
|
||||
<td>{{ entity.active }}</td>
|
||||
<td>
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<a href="{{ chill_path_add_return_path('chill_crud_closing_motive_edit', { 'id': entity.id }) }}" class="sc-button bt-edit"></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ chill_path_add_return_path('chill_crud_closing_motive_new', { 'parent_id': entity.id } ) }}" class="sc-button bt-new">{{ 'closing_motive.new child'|trans }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
{% endembed %}
|
||||
{% endblock %}
|
||||
|
||||
For edit template:
|
||||
|
||||
.. code-block:: html+jinja
|
||||
|
||||
{% extends '@ChillMain/Admin/layout.html.twig' %}
|
||||
|
||||
{% block title %}
|
||||
{% include('@ChillMain/CRUD/_edit_title.html.twig') %}
|
||||
{% endblock %}
|
||||
|
||||
{% block admin_content %}
|
||||
{% as we are in the admin layout, we override the admin content with the CRUD content %}
|
||||
{% embed '@ChillMain/CRUD/_edit_content.html.twig' %}
|
||||
{# we do not have "view" page. We empty the corresponding block #}
|
||||
{% block content_form_actions_view %}{% endblock %}
|
||||
{% endembed %}
|
||||
{% endblock %}
|
||||
|
||||
For new template:
|
||||
|
||||
.. code-block:: html+jinja
|
||||
|
||||
{% extends '@ChillMain/Admin/layout.html.twig' %}
|
||||
|
||||
{% block title %}
|
||||
{% include('@ChillMain/CRUD/_new_title.html.twig') %}
|
||||
{% endblock %}
|
||||
|
||||
{% block admin_content %}
|
||||
{% embed '@ChillMain/CRUD/_new_content.html.twig' %}
|
||||
{% block content_form_actions_save_and_show %}{% endblock %}
|
||||
{% endembed %}
|
||||
{% endblock %}
|
||||
|
||||
Customize some steps in the controller
|
||||
**************************************
|
||||
|
||||
Some steps may be customized by overriding the default controller and some methods. Here, we will override the way the entity is created, and the ordering of the "index" page:
|
||||
|
||||
* we will associate a parent ClosingMotive to the element if a parameter `parent_id` is found ;
|
||||
* we will order the ClosingMotive by the ``ordering`` property
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
namespace Chill\PersonBundle\Controller;
|
||||
|
||||
use Chill\MainBundle\CRUD\Controller\CRUDController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Chill\MainBundle\Pagination\PaginatorInterface;
|
||||
|
||||
/**
|
||||
* Controller for closing motives
|
||||
*
|
||||
*/
|
||||
class AdminClosingMotiveController extends CRUDController
|
||||
{
|
||||
protected function createEntity($action, Request $request): object
|
||||
{
|
||||
// we first create an entity "the usual way"
|
||||
$entity = parent::createEntity($action, $request);
|
||||
|
||||
if ($request->query->has('parent_id')) {
|
||||
// if we find the parent_id parameter, we add the corresponding
|
||||
// parent to the newly created entity
|
||||
$parentId = $request->query->getInt('parent_id');
|
||||
|
||||
$parent = $this->getDoctrine()->getManager()
|
||||
->getRepository($this->getEntityClass())
|
||||
->find($parentId);
|
||||
|
||||
if (NULL === $parent) {
|
||||
throw $this->createNotFoundException('parent id not found');
|
||||
}
|
||||
|
||||
$entity->setParent($parent);
|
||||
}
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
||||
protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator)
|
||||
{
|
||||
// by default, the query is an instance of QueryBuilder
|
||||
/** @var \Doctrine\ORM\QueryBuilder $query */
|
||||
return $query->orderBy('e.ordering', 'ASC');
|
||||
}
|
||||
}
|
||||
|
||||
How-to and questions
|
||||
********************
|
||||
|
||||
Which role is required for each action ?
|
||||
========================================
|
||||
|
||||
By default, each action will use:
|
||||
|
||||
1. the role defined under the action key ;
|
||||
2. the base role as upper, with the action name appended:
|
||||
|
||||
Example: if the base role is ``CHILL_BUNDLE_ENTITY``, the role will become:
|
||||
|
||||
* ``CHILL_BUNDLE_ENTITY_VIEW`` for the ``view`` action ;
|
||||
* ``CHILL_BUNDLE_ENTITY_INDEX`` for the ``index`` action.
|
||||
|
||||
The entity will be passed to the role:
|
||||
|
||||
* for the ``view`` and ``edit`` action: the entity fetched from database
|
||||
* for the ``new`` action: the entity which is created (you can override default values using
|
||||
* for index action (or if you re-use the ``indexAction`` method: ``null``
|
||||
|
||||
How to add some route and actions ?
|
||||
===================================
|
||||
|
||||
Add them under the action key:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
chill_main:
|
||||
cruds:
|
||||
-
|
||||
# snipped
|
||||
actions:
|
||||
myaction: ~
|
||||
|
||||
The method `myactionAction` will be called by the parameter.
|
||||
|
||||
Inside this action, you can eventually call another internal method:
|
||||
|
||||
* ``indexAction`` for a list of items ;
|
||||
* ``viewAction`` for a view
|
||||
* ``editFormAction`` for an edition
|
||||
* ``createFormAction`` for a creation
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
namespace CSConnectes\SPBundle\Controller;
|
||||
|
||||
use Chill\PersonBundle\CRUD\Controller\OneToOneEntityPersonCRUDController;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
|
||||
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use CSConnectes\SPBundle\Form\CSPersonPersonalSituationType;
|
||||
use CSConnectes\SPBundle\Form\CSPersonDispositifsType;
|
||||
use Symfony\Component\Security\Core\Role\Role;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class CSPersonController extends OneToOneEntityPersonCRUDController
|
||||
{
|
||||
public function personalSituationEdit(Request $request, $id)
|
||||
{
|
||||
return $this->formEditAction(
|
||||
'ps_situation_edit',
|
||||
$request,
|
||||
$id,
|
||||
CSPersonPersonalSituationType::class
|
||||
);
|
||||
}
|
||||
|
||||
public function personalSituationView(Request $request, $id): Response
|
||||
{
|
||||
return $this->viewAction('ps_situation_view', $request, $id);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
How to create a CRUD for entities associated to persons
|
||||
=======================================================
|
||||
|
||||
The bundle person provide some controller and template you can override, instead of the ones present in the mainbundle:
|
||||
|
||||
* :code:`Chill\PersonBundle\CRUD\Controller\EntityPersonCRUDController` for entities linked with a one-to-may association to :code:`Person` class ;
|
||||
* :code:`Chill\PersonBundle\CRUD\Controller\OneToOneEntityPersonCRUDController` for entities linked with a one-to-one association to :code:`Person` class.
|
||||
|
||||
There are also template defined under ``@ChillPerson/CRUD/`` namespace.
|
||||
|
||||
Those controller assume that:
|
||||
|
||||
* the entity provide the method :code:`getPerson` and :code:`setPerson` ;
|
||||
* the `index`'s id path will be the id of the person, and the ids in `view` and `edit` path will be the id of the entity ;
|
||||
|
||||
This bundle also use by default the templates inside ``@ChillPerson/CRUD/``.
|
||||
|
||||
|
||||
Reference
|
||||
*********
|
||||
|
||||
Configuration reference
|
||||
=======================
|
||||
|
||||
|
||||
.. code-block:: txt
|
||||
|
||||
chill_main:
|
||||
cruds:
|
||||
|
||||
# Prototype
|
||||
-
|
||||
class: ~ # Required
|
||||
controller: Chill\MainBundle\CRUD\Controller\CRUDController
|
||||
name: ~ # Required
|
||||
base_path: ~ # Required
|
||||
base_role: null
|
||||
form_class: null
|
||||
actions:
|
||||
|
||||
# Prototype
|
||||
name:
|
||||
|
||||
# the method name to call in the route. Will be set to the action name if left empty.
|
||||
controller_action: null # Example: 'action'
|
||||
|
||||
# the path that will be **appended** after the base path. Do not forget to add arguments for the method. Will be set to the action name, including an `{id}` parameter if left empty.
|
||||
path: null # Example: /{id}/my-action
|
||||
|
||||
# the requirements for the route. Will be set to `[ 'id' => '\d+' ]` if left empty.
|
||||
requirements: []
|
||||
|
||||
# the role that will be required for this action. Override option `base_role`
|
||||
role: null
|
||||
|
||||
# the template to render the view
|
||||
template: null
|
||||
|
||||
Twig default block
|
||||
==================
|
||||
|
||||
This part should be documented.
|
||||
|
@ -16,6 +16,7 @@ As Chill rely on the `symfony <http://symfony.com>`_ framework, reading the fram
|
||||
|
||||
Install Chill for development <installation.rst>
|
||||
Instructions to create a new bundle <create-a-new-bundle.rst>
|
||||
CRUD (Create - Update - Delete) for one entity <crud.rst>
|
||||
Routing <routing.rst>
|
||||
Menus <menus.rst>
|
||||
Forms <forms.rst>
|
||||
|
Loading…
x
Reference in New Issue
Block a user