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

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`:

   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):

```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:

```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``

```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 autoconfigure the ``chill_main`` bundle, you can `prepend the configuration of the ChillMain Bundle ](https://symfony.com/doc/current/bundles/prepend_extension.html):

```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 is 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:

```php
   {% 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:

```php
   {% 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:

   {% 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

```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:

```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:

```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:

* `Chill\PersonBundle\CRUD\Controller\EntityPersonCRUDController` for entities linked with a one-to-may association to `Person` class ;
* `Chill\PersonBundle\CRUD\Controller\OneToOneEntityPersonCRUDController` for entities linked with a one-to-one association to `Person` class.

There are also template defined under ``@ChillPerson/CRUD/`` namespace.

Those controllers assume that:

* the entity provide the method `getPerson` and `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 uses by default the templates inside ``@ChillPerson/CRUD/``.

###### Reference

## Configuration reference

```yaml
   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.
