¿Utilizar parámetros para establecer valores por defecto?

Buenos días. Vamos directos al lío. Tengo el siguiente código:

//AppBundle\Form\BookingType
namespace AppBundle\Form;
 
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
 
/**
 * Defines the form used to start the Booking Process.
 */
class BookingType extends AbstractType
{
    /**
     * @var string
     */
    private $idtokenprovider;
 
    /**
     * @var string
     */
    private $hsri;
 
    /**
     * BookingType constructor.
     *
     * @param string $idtokenprovider
     * @param string $hsri
     */
    public function __construct($idtokenprovider, $hsri)
    {
        $this->idtokenprovider = $idtokenprovider;
        $this->hsri = $hsri;
    }
 
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('idtokenprovider', HiddenType::class, ['data' => $options['idtokenprovider']])
            ->add('lang', HiddenType::class, ['data' => $options['lang']])
            ->add('hsri', HiddenType::class, ['data' => $options['hsri']])
            ->add('step', HiddenType::class, ['data' => $options['step']])
            ->add('checkin', DateType::class, [
                'label' => 'booking.form.check_in',
                'widget' => 'single_text',
                'format' => 'dd/MM/yyyy',
            ])
            ->add('nights', ChoiceType::class, [
                'label' => 'booking.form.nights',
                'choices' => array_combine(range(1, 20), range(1, 20)),
                'choice_translation_domain' => false,
            ])
            ->add('clientCode', TextType::class, [
                'label' => 'booking.form.promotional_code',
                'required' => false,
            ]);
    }
 
    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'method' => 'GET',
            'csrf_protection' => false,
            'idtokenprovider' => $this->idtokenprovider,
            'hsri' => $this->idtokenprovider,
            'lang' => 'es',
            'step' => 1
        ]);
    }
 
    public function getBlockPrefix()
    {
        return '';
    }
}

Y en el controlador creo el formulario tal que así:

//AppBundle\Controller\BookingController.php
$form = $this->createForm(BookingType::class, null, [
    'lang' => $request->getLocale(),
]);
 
$form->handleRequest($request);

Como veis, paso null en el segundo parámetro que sería la información del formulario. Me parecía mucho mejor opción que estar pasando un array con los distintos valores para los campos ocultos en cada instancia del formulario.

¿Es correcta esta forma de trabajar?

Muchas gracias

Respuestas

#1

Yo diría que es la forma correcta de hacerlo. En este artículo de la documentación de Symfony se muestra cómo pasar opciones de configuración a los formularios. Lo único es que el constructor no sería necesario y puedes usar el método ->setRequired() para hacer que una opción de configuración sea obligatoria.

#2

Hola, tu objetivo final no es persistir lo que se manaje en el form, pues si es así no creo que con esta vía puedas hacerlo, pues normalmente donde pasas null deberías pasar una entidad que luego con el handleRequest del form hace un bind de los parámetros del objeto y la declaración de cada atributo del formulario, ejecutando las validaciones(Asserts de la entidad) entre otras cosas.

En mi opinión si quieres que tu objeto tenga parámetros por defecto manéjalo desde el constructor de tu entidad:

class Booking
{
    /**
     * Constructor
     */
    public function __construct()
    {
       $this->step = 1;
       $this->status = self::STATUS_NEW;
       $this->finished = false;
       $this->checkin = \Datetime();
    } 
}

y luego pasa una nueva instancia de booking al formulario, al crearse el objeto vendrá con todos los valores por defecto con los que inicialices la entidad.

$booking = new Booking();   // quizás uses algún servicio para crear el objeto
$form = $this->createForm(BookingType::class, $booking, [
         'lang' => $request->getLocale(),  // en este caso si tiene sentido pasarlo por defecto
        ]);
 
$form->handleRequest($request);

Al renderizar el form en la plantilla debería mostrar los parámetros por defecto que inicializaste en el objeto.

Esto para el caso de variables que no dependen de otro "servicio" pero en tal caso, podrías bien inyectar los services necesarios para obtener los valores que necesitas o si es que quieres que todos tus objetos booking se inicialicen con estos valores(uses o no el formulario) por ejemplo si quieres crear un objeto booking y setearlo a otra entidad relacionada con los valores por defecto deberías tener un manager que use esos "servicios" que buscan los valores y se lo seteen a tu objeto booking, haciendo esto, podrías tanto pasarle el objeto al form como setearlo a otra entidad o usarlo en otro lugar y al instanciarlo tendria siempre tus valores por defecto.

Este es solo mi punto de vista de como yo lo haría, quizás te dejen mejores propuestas por acá.

Saludos.

#3

Hola a los 2 y gracias por vuestras respuestas.

@javiereguiluz tengo el constructor por que creo el formulario como un servicio:

form.booking_type:
      class: AppBundle\Form\BookingType
      arguments: ['%booking_idtokenprovider%', '%booking_hsri%']
      tags:
          - { name: form.type }

De hecho incluso estoy pensando en inyectar el requestStack para coger el valor del getLocale() del currentRequest() con lo cual, me olvido en el 90% de los casos donde voy a usar el formulario con los valores por defecto, y en el 10% tendría que hacer

$form = $this->createForm(BookingType::class, null, [
    'idtokenprovider' => 'otrotoken',
    'hsri' => 'otrohsri',
    'lang' => 'en',
]);

Lo que no sabría es si es mejor esto o lo siguiente

$form = $this->createForm(BookingType::class, [
    'idtokenprovider' => 'otrotoken',
    'hsri' => 'otrohsri',
    'lang' => 'en',
]);

@RoberRielo No voy a persistir los datos a un modelo (la reserva no se va a la base de datos, el formulario simplemente se utiliza para iniciar el proceso de compra que maneja un motor externo)

#4

En tal caso que vas a hacer cuando se haga submit del form vas a leer del Request todos los parámetros que envió el formulario? porque creo que aunque no persistas la información deberías trabajar con un objeto "Model" para luego extraer la información y procesarla orientada a objetos. De cierta manera le vas a dar al controlador la responsabilidad de buscar la información que necesitas pasarle al form($idtokenprovider y $hsri).

#5

No voy a trabajar con el formulario. La clase BookingType es simplemente para crear los campos que necesito enviar los parámetros a otra página externa a mi proyecto que recibe los parámetros por GET.

Nunca hago un $form->handleRequest($request) por que los datos que se envían, por que ni se envían a mi propia página..

Aun así, ya solucioné mis dudas.

Gracias