Agregar campos informativos en formularios de Sonata Admin

No es sorpresa la gran popularidad que tiene el bundle Sonata Admin, siendo como dicen ellos «El generador de admistradores faltante en Symfony2» (énfasis mío).

Al generar los adminstradores de nuestras entidades es común necesitar desplegar junto con los campos a editar, campos informativos del objeto (fecha de creación, último acceso, autor, etc.). Se puede fácilmente agregar los campos y hacerlos no editables y/o desactivarlos, pero visualmente el resultado no es realmente bueno.

// simple but ugly
->add('created', null, array(
    'read_only' => true,
    'disabled' => true,
    'widget' => 'single_text',
    'format' => 'EEE, MMM FF yyyy HH:mm'
))

Podemos utilizar CSS para mejorar considerablemente la imagen, sin embargo esto no funciona si queremos desplegar estructuras más complejas.

Tratándose de Symfony2 y SonataAdmin es posible adaptar los administradores generados para nuestro bundle en la medida que necesitemos sin impactar en otras áreas. En esta ocasión lo aprovecharemos para crear etiquetas informativas insertables como campos dentro de los formularios.

label in cat

Probado con:
Symfony 2.4
Sonata Admin Bundle 2.2

Dependiendo de los objetivos, lo que hacer es:
Crear la clase para el nuevo tipo de despliegue
Crear un transformador para hacer desplegable el objeto
Configurar las clases de administración con el objeto y el nuevo tipo
Crear los bloques en Twig con el marcado para el despliegue

Lo siguiente mostrará como desplegar un objecto DateTime, pero la guía es fácilmente adaptable para otros tipos, como números, hipervínculos, entidades, colecciones o cualquier cosa.

Crear la clase para el nuevo tipo de despliegue

Primero necesitamos crear una clase para el nuevo tipo

/* Acme/DemoBundle/Form/StaticTextType.php */

Para usar el nuevo tipo podemos incluir la clase e instanciarla en el administrador

/* Acme/DemoBundle/Admin/EntityAdmin.php */
add('id', new StaticTextType())
...

o crear crear un servicio y registrarlo como nuevo tipo de formulario. Esto también permite hacer uso de él a través de su etiqueta.

# Acme/DemoBundle/Resources/config/services.yml
services:
    ...
    acme_demo.form.type.info:
        class: Acme\DemoBundle\Form\StaticTextType
        tags:
            - { name: form.type, alias: acme_static_text }
/* Acme/DemoBundle/Admin/EntityAdmin.php */
    ...
    ->add('id', 'acme_static_text')

Para desplegar información básica como números o cadenas no necesitamos crear el transformador pues nuestro tipo hereda las características del tipo 'text'.

Crear un transformador para hacer desplegable el objeto

Para desplegar la fecha y hora necesitamos un transformador que genere la cadena que represente a la instancia DateTime.

/* Acme/DemoBundle/Form/DataTransformer/DateTimeToStringTransformer.php */
format = $format;
    }
    
    /**
     * Transforms a datetime object to a string.
     *
     * @param  DateTime|null $date
     * @return string
     */
    public function transform($dateTime)
    {
        return null === $dateTime
	    ? return ''
	    : $dateTime->format($this->format);
    }

    /**
     * Transforms a string date to a DateTime object.
     *
     * @param  string $dateTime
     *
     * @return DateTime|null
     *
     * @throws TransformationFailedException if cannot transform to DateTime.
     */
    public function reverseTransform($dateTime)
    {
        if (empty($dateTime)) {
            return null;
        }

        $dt = DateTime::createFromFormat($this->format, $dateTime);
        if (null === $dt) {
            throw new TransformationFailedException(sprintf(
                "Unable to transform '%s' to a DateTime object",
                $dateTime
            ));
        }

        return $dt;
    }
}

Configurar las clases de administración con el objeto y el nuevo tipo

Para mostrar los datos de los campos de manera que no sean procesados por el formulario, podemos agregar el campo configurándolo como no mapeado y el dato manualmente agregado o configurarlo como desactivado.

add('id', 'acme_static_text', array(
        'required' => false
    ))

    // correct display, ignored on save
    ->add('id', 'acme_static_text', array(
        'required' => false,
        'disabled' => true,
    ))

    $entity = $this->getSubject();
    ->add('id', 'acme_static_text', array(
        'required' => false,
        'mapped' => false,
        'data' => $entity->getId(),
    ))

Tip: Como el método $this->getSubject() de la clase de administración regresa la entidad que se está gestionando, tambíen se puede aprovechar para desplegar diferentes campos dependiendo de si se está creando una nueva instancia o editando una existente.

Nota: Al utilizar subadminstradores getSubject() regresa siempre la primera entidad.

Para usar el transformador al estructurar nuestro formulario en el administrador no agregamos directamente nuestro objeto con el tipo, sino que creamos un objeto FormBuilder al que le agregaremos una instancia del transformador.

/* Acme/DemoBundle/Admin/EntityAdmin.php */
add(
            $formMapper->create('created', 'acme_static_text', array(
                // data_class set to null to avoid object type errors
                'data_class' => null,
                'required' => false,
                'disablex' => true,
            ))
            ->addViewTransformer(new DateTimeToStringTransformer('D, M d Y H:i:s'))
        )

Algunos otros ejemplos de configuración:

        // 1-to-N collection would be something like this
        ->add('pages', 'acme_static_text_collection', array(
            'label' => false,
            'type' => 'entity',
                'options' => array(
                'class' => 'AcmeDemoBundle:Foo',
            ),
        ))

        // An entity would look like
        ->add(
            $formMapper->create('author', 'info', array(
                'data_class' => null,
                'required' => false,
                'mapped' => false,
                'data' => $subject->getAuthor(),
                'property_path' => null
            ))
            ->addViewTransformer(new UserToStringTransformer())
        )

Crear los bloques en Twig con el marcado para el despliegue

Finalmente vamos a construir los widgets con el HTML para mostrar la información. Como nuestro tipo se llama acme_static_text el bloque lo debemos crear con el nombre acme_static_text_widget.

{# Acme/DemoBundle/Resources/views/Form/widgets.html.twig #}
{% block acme_static_text_widget %}
    {% spaceless %}
        
{{ value }}
{% endspaceless %} {% endblock %}

Ahora debemos agregar nuestra plantilla con widgets en el administrador para que pueda ser usada. Una manera sencilla es sobrecargando el método getFormTheme() de nuestra clase de administración.

/* Acme/DemoBundle/Admin/EntityAdmin.php */

Y con esto terminamos. Si bien la implementación no es tan rápida, no es compleja y es valiosa porque muestra una manera para personalizar cualquier campo en los administradores de nuestros bundles.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *