Parliamo del progetto
PHP symfony Sonata

Symfony 4 / Sonata: Creare un'interfaccia CRUD annidata (child 1->N) con l'ordine tramite drag'n drop

In generale vogliamo creare un'interfaccia CRUD, 1N, con la quale, quando il’si è sul’édition d’un élément, si aggiunge un pannello per gérer tutti i élémenti figli.Qui abbiamo una tabella...

Symfony 4 / Sonata: Creare un'interfaccia CRUD annidata (child 1->N) con l'ordine tramite drag'n drop

In generale vogliamo creare un'interfaccia CRUD, 1N, con la quale, quando il’si è sul’édition d’un élément, si aggiunge un pannello per gérer tutti i élémenti figli.

Qui abbiamo una tabella wtype, con una tabella wconf che contiene una série d’registrazione collegataés a un elemento wtype.

Sélection_081
Sélection_082

Proprio come per l’esempio di’impléimplementazione di sortable con trascinamento’ndrop ( disponibile qui ) useremo i seguenti componenti:
pixassociates/sortable-behavior-bundle e stof/doctrine-extensions-bundle
Dovrete quindi avere préalablement créé un'entitàés che contiene un legame 1N con un dueècon entitàé.

La manipolazione consiste unicamente à iniettare il dueiècon entitàés nella chiamata della dédichiarazione del servizio della première con l’argomento « aggiungiFiglio » e la réfépresenza del servizio genitore.

Sélection_083

Quando il’vogliamo fare référiferimento a un servizio si usa la chaînon utilizzareée per la dédichiarazione (che è libera), e si aggiunge « @ » per dire che c’è una référence.

#config/services.yaml
services:
     admin.wconf:
        class: App\Admin\WconfAdmin
        tags:
            - { name: sonata.admin, manager_type: orm, label: "Configuration des types" }
        public: true
        arguments: [~, App\Entity\Wconf, 'PixSortableBehaviorBundle:SortableAdmin']
        calls:
            - [setPositionService, ["@pix_sortable_behavior.position"]]
        
    admin.wtype:
        class: App\Admin\WtypeAdmin
        arguments: [~, App\Entity\Wtype, ~]
        calls:
            - [addChild, ["@admin.wconf"]] 
        tags:
            - { name: sonata.admin, manager_type: orm, label: "Types de contrats" }
        public: true
 

Nei 2 controller admin, sarà necessario aggiungere i référiferimenti alle seguenti librerie:

use Knp\Menu\ItemInterface as MenuItemInterface;
use Sonata\AdminBundle\Admin\AdminInterface;
use Sonata\AdminBundle\Route\RouteCollection;
use Pix\SortableBehaviorBundle\Services\PositionORMHandler as PositionHandler;

Poi in l’interfaccia Parent, si aggiungono i pulsanti.
Per l’url di l’interfaccia fili, bisogna dargli la référence del servizio per che’il créé le strade. Poiché il nostro servizio è « admin.wconf », la référence per la lista sarà quindi ‘admin.wconf.elenco’

    protected function configureSideMenu(MenuItemInterface $menu, $action, AdminInterface $childAdmin = null): void
    {
        if (!$childAdmin && !\in_array($action, ['edit'], true)) {
            return;
        }
               
        $admin = $this->isChild() ? $this->getParent() : $this;
        $id = $admin->getRequest()->get('id');
        $label=$this->hasSubject() && null !== $this->getSubject()->getLabel() ? $this->getSubject()->getLabel():null;
        
        $menu->addChild(
            'Configuration du contrat '.$label,
            $admin->generateMenuUrl('edit', ['id' => $id])
           
            );
        
        $menu->addChild( 'Configuration des interfaces '.$label, 
            [
                'uri' => $admin->generateUrl('admin.wconf.list', ['id' => $id]) 
            ]);
        
    }

Poiché ora la nostra interfaccia CRUD figlia è limitata all'ambito del nostro sélezione genitore, e che desideriamo gérer l’ordine in drag’n drop, elimineremo la possibilitàé di ordinare la lista.
Basta solo di’aggiungere l’argomento sortable=false (predefinito true), durante la définizione dei campi della lista.

    protected function configureListFields(ListMapper $listMapper)
    {
        $listMapper->add('_action', null, array(
            'actions' => array(
                'move' => array(
                    'template' => '@PixSortableBehavior/Default/_sort_drag_drop.html.twig',
                    'enable_top_bottom_buttons' => false, //optional
                ),
            ),
        ))
        ;
        
        $listMapper->addIdentifier('etape', null, ['label' => 'Etape','sortable'=>false]);
        $listMapper->addIdentifier('position', null, ['label' => 'position','sortable'=>false]);
        //$listMapper->addIdentifier('id', null, ['label' => 'id','sortable'=>false]);
        $listMapper->addIdentifier('label', null, ['label' => 'Label','sortable'=>false]);
        
        
        $listMapper->addIdentifier('synchro_field', null, ['label' => 'Destination du champs','sortable'=>false]);
        $listMapper->add('actif', null, ['editable' => true,'sortable'=>false]);
        
    }

Vogliamo anche conservare l’ordine per déoccorre, affinché l’l'utente ritrova sempre il mêil mio ordine à ogni ricarica.
Ci sono due manières.


La premiaère, consiste à sovrascrivere la configurazione per débisogna :

public function __construct( $code, $class, $baseControllerName ) {
        parent::__construct( $code, $class, $baseControllerName );

}
 protected $datagridValues = array(
        '_page' => 1,
        '_sort_by' => 'position',
        '_sort_order' => 'ASC',
    );

La seconda méthode, permette di fare una selezione più fine, per esempio su 2 campi sovraccaricando la richiestaêti usiée per la lista.

    public function createQuery($context = 'list')
    {
        $proxyQuery = parent::createQuery('list');
        $proxyQuery->addOrderBy($proxyQuery->getRootAlias().'.etape', 'ASC');
        $proxyQuery->addOrderBy($proxyQuery->getRootAlias().'.position', 'ASC');
    
        return $proxyQuery;
    }
    

E lì ci si rende conto che se si déattivo l’ordine delle colonne, il nostro ordine configuratoé nella nostra richiestaênon ti funziona più.
Quindi, quello che’andiamo a fare, c’è sovraccaricare il template solo per questa lista, e togliere il codice che gère le entêtè di colonne, ma conservare tutto il resto.

L'elenco si trova in vendor/sonata-project/admin-bundle/src/Resources/views/CRUD/base_list.html.twig e il nostro block interessatoé est « intestazione_tabella »

Sélection_088

Aggiungeremo il nostro template nel dédichiarazione del nostro servizio.
Per questo dobbiamo prendere la référence del template, dégrida in questa pagina : https://symfony.com/doc/master/bundles/SonataAdminBundle/reference/templates.html#global-templates
Dans il nostro caso c’è

Sélection_089

E aggiungeremo semplicemente una chiamata di tipo setTemplate con la nostra configurazione

    admin.wconf:
        class: App\Admin\WconfAdmin
        tags:
            - { name: sonata.admin, manager_type: orm, label: "Configuration des types" }
        public: true
        arguments: [~, App\Entity\Wconf, 'PixSortableBehaviorBundle:SortableAdmin']
        calls:
            - [setPositionService, ["@pix_sortable_behavior.position"]]
            - [ setTemplate, [list, "Admin/wconf-list.html.twig"]]

Abbiamo messo : « Admin/wconf-list.html.twig »
Ciò che significa che il sistèMi vai a cercare il file /templates/Admin/wconf-list.html.twig
Questo file deve étendere la vista iniziale della lista, e redéfinire il blocco che gère l’entêsolo a te. Vogliamo davvero conservare tutto il resto.
Dunque, noi démetti il nostro file dando la référence del modello maître, e rossoéfinisci poi il nostro blocco

#/templates/Admin/wconf-list.html.twig
{% extends '@SonataAdmin/CRUD/base_list.html.twig' %}

        {% block table_header %}
        
                            <thead>
                                <tr class="sonata-ba-list-field-header">
                                    {% for field_description in admin.list.elements %}
                                        {% if admin.hasRoute('batch') and field_description.getOption('code') == '_batch' and batchactions|length > 0 %}
                                            <th class="sonata-ba-list-field-header sonata-ba-list-field-header-batch">
                                              <input type="checkbox" id="list_batch_checkbox">
                                            </th>
                                        {% elseif field_description.getOption('code') == '_select' %}
                                            <th class="sonata-ba-list-field-header sonata-ba-list-field-header-select"></th>
                                        {% elseif field_description.name == '_action' and app.request.isXmlHttpRequest %}
                                            {# Action buttons disabled in ajax view! #}
                                        {% elseif field_description.getOption('ajax_hidden') == true and app.request.isXmlHttpRequest %}
                                            {# Disable fields with 'ajax_hidden' option set to true #}
                                        {% else %}
                                            {% set sortable = false %}
                                            {% apply spaceless %}
                                                <th class="sonata-ba-list-field-header-{{ field_description.type }}{% if sortable %} sonata-ba-list-field-header-order-{{ sort_by|lower }} {{ sort_active_class }}{% endif %}{% if field_description.options.header_class is defined %} {{ field_description.options.header_class }}{% endif %}"{% if field_description.options.header_style is defined %} style="{{ field_description.options.header_style }}"{% endif %}>
                                                    {% if field_description.getOption('label_icon') %}
                                                        <i class="sonata-ba-list-field-header-label-icon {{ field_description.getOption('label_icon') }}" aria-hidden="true"></i>
                                                    {% endif %}
                                                    {{ field_description.label|trans({}, field_description.translationDomain) }}
                                                   
                                                </th>
                                            {% endapply %}
                                        {% endif %}
                                    {% endfor %}
                                </tr>
                            </thead>
            {% endblock %}

Poi abbiamo la nostra entête senza i collegamenti per poter ordinare i contenuti.
Siamo sûr che l’utente non vaérer l’ordine in drag’un drop con una selezione mal configurataé e chiamarci ogni 5 minuti per dirci che ça non funziona.

Adattare l’esempio alla versione installata

Gli esempi Symfony e Sonata rimangono legati alle versioni indicate nell’articolo. Symfony 4.x e Symfony 6.2 non sono più supportati: per un progetto attuale, utilizzate un ramo mantenuto e la documentazione corrispondente esattamente alle vostre dipendenze.

php bin/console about
composer show symfony/framework-bundle sonata-project/admin-bundle

Prima di riprendere il codice, controllate le firme, i servizi, le rotte e i template interessati. Aggiungete poi un test funzionale che copra le autorizzazioni, il codice HTTP e il risultato visibile.

Riferimento : versioni di Symfony supportate.

Condividi questo articolo