523 lines
17 KiB
ReStructuredText

.. 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:
Auto-loading the routes
***********************
Ensure that those lines are present in your file `app/config/routing.yml`:
.. code-block:: yaml
chill_cruds:
resource: 'chill_main_crud_route_loader:load'
type: service
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>&nbsp;</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.