Formulários Symfony mais limpos

Formulários Symfony mais limpos

Formulários são componentes essenciais para o desenvolvimento de aplicativos da web Symfony. Mas se eu olhar para a forma típica do Symfony , vejo várias violações das Quatro Regras do Design Simples .

Problemas em formulários do symfony

  1. Alias ​​de formulário duplicadogetName e o alias definido nos serviços deve corresponder
  2. Acoplamento desnecessário ao Symfony – eu realmente preciso saber OptionsResolverInterface?
  3. Opcional$options
    1. analisador estático como PMD ou mesmo seu IDE pode relatar parâmetros não utilizados
    2. buildFormé menos legível quando o código combina formulário de construção e opções de leitura

Forma típica do Symfony

namespace AppBundleFormType;

use SymfonyComponentFormAbstractType;
use SymfonyComponentFormFormBuilderInterface;
use SymfonyComponentOptionsResolverOptionsResolverInterface;

class TaskType extends AbstractType
{
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver
->setDefaults(array(
'data_class' => 'AppBundleEntityTask'
));
}

public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder

->add('task')
->add('dueDate', null, array('widget' => 'single_text'))
->add('save', 'submit');
if (is_null($options['data']->getId())) {
$builder
->add('assignee');
}
}

public function getName()
{
return 'task';
}
}

Formulário registrado como serviço:

services:
acme_demo
.form.type.task:
class: AppBundleFormTypeTaskType
tags
:
- { name: form.type, alias: task }

Solução mais limpa

Eu sigo regras de design simples, ao invés das Melhores Práticas do Symfony, para manter a forma o mais legível e simples possível. Essa abordagem funcionou muito bem para mim, especialmente em aplicativos com muitos formulários. Não herde o Symfony AbstractType, mas CleanFormType(desculpe o nome fictício) que resolve os problemas anteriores:

  1. Alias ​​é definido em um lugar (em services.yml)
  2. O formulário é acoplado apenas a FormBuilderInterface
  3. As opções são verdadeiramente opcionais
namespace AppBundleFormType;

use SymfonyComponentFormFormBuilderInterface;

class TaskType extends CleanFormType
{
private $isNewTaskCreated;

public function getDefaults()
{
return array(
'data_class' => 'AppBundleEntityTask'
);
}

protected function loadOptions(array $options)
{
$this
->isNewTaskCreated = is_null($options['data']->getId());
}

protected function build(FormBuilderInterface $builder)
{
$builder

->add('task')
->add('dueDate', null, array('widget' => 'single_text'))
->add('save', 'submit');
if ($this->isNewTaskCreated) {
$builder
->add('assignee');
}
}
}
services:
acme_demo
.form.type.task:
class: AppBundleFormTypeTaskType
arguments
:
- "task"
tags
:
- { name: form.type, alias: task }

A definição do formulário é ainda mais simples sem padrões e opções :

class TaskType extends CleanFormType
{
protected function build(FormBuilderInterface $builder)
{
$builder

->add('task')
->add('dueDate', null, array('widget' => 'single_text'))
->add('save', 'submit');
}
}

Código fonte

O CleanFormType está disponível na essência . Você pode usá-lo e modificá-lo como quiser. Aqui está a aula:

namespace AppBundleFormType;

use SymfonyComponentFormAbstractType;
use SymfonyComponentFormFormBuilderInterface;
use SymfonyComponentOptionsResolverOptionsResolverInterface;

abstract class CleanFormType extends AbstractType
{
private $formName;

public function __construct($formName)
{
$this
->formName = $formName;
}

public function getName()
{
return $this->formName;
}

public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver
->setDefaults(
$this
->getDefaults()
);
}

public function buildForm(FormBuilderInterface $builder, array $options)
{
$this
->loadOptions($options);
$this
->build($builder);
}

protected function getDefaults()
{
return array();
}

/** @SuppressWarnings("unused") */
protected function loadOptions(array $options)
{
}

abstract protected function build(FormBuilderInterface $builder);
}