mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
523 lines
17 KiB
ReStructuredText
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> </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.
|
|
|