.. 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 `_: .. 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 %} {{ 'Ordering'|trans }} {{ 'Label'|trans }} {{ 'Active'|trans }}   {% endblock %} {% block table_entities_tbody %} {# we customize the content of the table #} {% for entity in entities %} {{ entity.ordering }} {{ entity|chill_entity_render_box }} {{ entity.active }} {% 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.