Symfony 4 / Sonata: Crear una Administración Uno a Muchos (1N)

Vamos a crear una interfaz de administración 1N, con dos entidades. La primera, Uno, y la segunda, Muchos, y configurar un panel de administración para la tabla Uno, que puede afectar a varios elementos de la tabla Muchos. Para darle más interés, añadiremos algunos parámetros adicionales, como campos de marca de tiempo para fechas de sincronización con un SI, y campos primarios que no se llaman ID y no se autoincrementan.

Sélection_086

En nuestro caso, tenemos una sincronización con un SI que requiere un campo de marca de tiempo.
Solo crea un campo de tipo datetime
Agrega la anotación de opción {'default': 'CURRENT_TIMESTAMP'}
La siguiente anotación creará un campo Timestamps con el valor predeterminado establecido en CURRENT_TIMESTAMP.

* @ORM\Column(type="datetime", nullable=false, options={"default": "CURRENT_TIMESTAMP"})
* @ORM\Version


Para nombrar la tabla correctamente y crear índices, simplemente añade la siguiente anotación en la cabeza de la clase

use Doctrine\ORM\Mapping as ORM;
use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\Table;
use Doctrine\ORM\Mapping\Id;
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\GeneratedValue;
use Doctrine\ORM\Mapping\Index;

/**
* @ORM\Entity(repositoryClass="App\Repository\WcoconRepository")
* @Table(name="table_name",indexes={@Index(name="PRIMARY", columns={"champs1", "champs2"})})
*/


Para usar un campo diferente al predeterminado 'id', simplemente añade la siguiente anotación:

/**
* @ORM\Column(type="integer")
* @ORM\Id()
*/
private $numero_dossier;


Una vez realizada la relación, es necesario especificar el id que se utilizará en el mapeo. De lo contrario, habrá un error como:
Nombre de columna id referenciado para relación de App\Entity\Many hacia App\Entity\Uno no existe.

Así que en la entidad 'Muchos', necesitas añadir la relación con el id de la entidad 'Uno'

/**
* @ORM\ManyToOne(targetEntity="App\Entity\One", inversedBy="Ones")
* @ORM\JoinColumn(nullable=false, name="many_id", referencedColumnName="one_id")
*/
private $many_id;


Último punto importante; Para evitar que el objeto tenga un nombre como 'App\Entity\Uno:sdfsdfgsdgmlkpoufsdlkjfsdg' durante la edición, es mejor sobrescribir el método __toString(), para mostrar el nombre del objeto.
En la entidad, simplemente incluye un método:

public function __toString()
{
       return "Nom object ".$this->getName();
}


Una vez hecho esto, solo procede con la migración:

php bin/console make:migration
php bin/console doctrine:migrations:migrate


Si es necesario, para regenerar el repositorio a partir de las entidades disponibles simplemente lanza el siguiente comando:

php bin/console make:entity --regenerate

Las entidades no serán modificadas.

Sélection_092


Para probar las relaciones entre entidades, puedes ejecutar el siguiente comando:

php bin/console doctrine:schema:validate
Sélection_091


En esta etapa, todo lo que nos queda por hacer es configurar las interfaces de gestión en el admin, y podemos comenzar a desarrollar la autenticación de front-end.

El primer paso de la interfaz de administración es crear la clase en src/Admin/TableAdmin.php

namespace App\Admin;

use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Form\FormMapper;
use Symfony\Component\Form\Extension\Core\Type\TextType;

use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Sonata\AdminBundle\Form\Type\ModelAutocompleteType;
use Sonata\AdminBundle\Form\Type\ModelType;
use Sonata\AdminBundle\Form\Type\ModelListType;
use Sonata\Form\Type\CollectionType;

class OneAdmin extends AbstractAdmin
{
	protected function configureFormFields(FormMapper $formMapper)
	{

		$formMapper->tab('General');

		$formMapper->with('Parametres', ['class' => 'col-md-4']);

		$formMapper->add('name', TextType::class, ['required' => false, 'label'=>'Site internet','attr' => ['placeholder' => '']]);

		$formMapper->end()

		$formMapper->with('Parametres', ['class' => 'col-md-8']);

		$formMapper->add('nomduchampsArrayCollection', EntityType::class, [
		'class' => 'App\Entity\Many',
		'choice_label' => 'nom_du_champs_autre_table',
		'label' => 'champs liste',
		'multiple' => true,
		//'expanded' => true,
		]);

		$formMapper->end()
		$formMapper->end('General');

	}

	protected function configureDatagridFilters(DatagridMapper $datagridMapper)
	{
		$datagridMapper->add('name');

	}

	protected function configureListFields(ListMapper $listMapper)
	{
		$listMapper->addIdentifier('name', null, ['label' => 'Nom']);

	}
	public function prePersist($object)
	{
		$this->preUpdate($object);
	}

	public function preUpdate($object)
	{

		$mapping=$object->getManys()->getMapping();
		$currentManys=$this->createQuery()->getQueryBuilder()->getQuery()->getEntityManager()->getRepository($mapping["targetEntity"])
		->findBy([$mapping["mappedBy"]=>$this->id($object)]);
		$object->setMany($object->getManys(),$currentManys);

	}
	public function setMany($collections,$currentManys)
	{
		foreach ($currentCollections as $w) 
		{
			if(!$collections->contains($w)){
			$w->setCollectionID(null);
			}

		}
		$this->collections= new ArrayCollection();

		foreach ($collections as $collection) {
			$this->addCollections($collections);
		}

	}

}

En la entidad One.php, necesitamos añadir nuestro nuevo método para manejar modificaciones

    public function setMany($manys,$current)
    {
        foreach ($current as $w) {
            if(!$manys->contains($w)){
                $w->setManyField(null);
            }
           
        }
        $this->manys = new ArrayCollection();
        
        foreach ($manys as $m) {
            $this->addMany($m);
        }

    }

Luego registrar el servicio en config/services.yaml

    admin.MaTable:
        class: App\Admin\MaTableAdmin
        arguments: [~, App\Entity\MaTable, ~]
        tags:
            - { name: sonata.admin, manager_type: orm, label: "Table MaTable" }
        public: true