26/05/2014 - DOCTRINE, SYMFONY, TWIG
In this example we're going to see how to define form validation annotations in model classes rather than entities or formtypes. Unfortunately uniqueConstraints
and UniqueEntity
constraint errors cannot be handled in model validations so it is up to you to handle them manually in controller like shown below.
Assume that this is the child entity of one-to-many relationship and Country is the parent.
namespace Football\FrontendBundle\Entity;
use DateTime;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* @ORM\Entity
* @ORM\Table(
* name="league",
* uniqueConstraints={@ORM\UniqueConstraint(columns={"name", "country_id"})}
* )
* @UniqueEntity(
* fields={"name","country"},
* message="League for given country already exists in database."
* )
*/
class League
{
/**
* @ORM\Id
* @ORM\Column(type="smallint")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @ORM\Column(name="name", type="string", length=100)
*/
protected $name;
/**
* @ORM\ManyToOne(
* targetEntity="Country",
* inversedBy="league"
* )
* @ORM\JoinColumn(
* name="country_id",
* referencedColumnName="id",
* onDelete="CASCADE",
* nullable=false
* )
*/
protected $country;
}
namespace Football\FrontendBundle\Model;
use Symfony\Component\Validator\Constraints as Assert;
class LeagueModel
{
/**
* @var string
*
* @Assert\NotBlank(message="Name is required.")
* @Assert\Length(
* max=100,
* maxMessage="Name cannot be longer than {{ limit }} characters."
* )
*/
protected $name;
/**
* @var string
*
* @Assert\NotBlank(message="Country is required.")
*/
protected $country;
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
return $this;
}
public function getCountry()
{
return $this->country;
}
public function setCountry($country)
{
$this->country = $country;
return $this;
}
}
namespace Football\FrontendBundle\Form\Type;
use Doctrine\ORM\EntityRepository;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class LeagueType extends AbstractType
{
private $country;
public function __construct()
{
$this->country = [
'error_bubbling' => true,
'class' => 'FootballFrontendBundle:Country',
'property' => 'name',
'multiple' => false,
'expanded' => false,
'required' => false,
'empty_value' => '',
'query_builder' => function (EntityRepository $repo)
{
return $repo->createQueryBuilder('c')->orderBy('c.name', 'ASC');
}
];
}
public function buildForm(FormBuilderInterface $builder, array $options = [])
{
$builder
->setMethod($options['method'])
->setAction($options['action'])
->add('name', 'text', ['error_bubbling' => true])
->add('country', 'entity', $this->country);
}
public function getName()
{
return 'league';
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(
['data_class' => 'Football\FrontendBundle\Model\LeagueModel']
);
}
}
/**
* @Route("/league")
*/
class LeagueController extends Controller
{
const ROUTER_PREFIX = 'football_frontend_league_';
/**
* Create page.
*
* @Route("/create")
* @Method({"GET"})
*
* @return Response
*/
public function createAction()
{
$form = $this->getForm(
new LeagueModel(),
'POST',
$this->generateUrl(self::ROUTER_PREFIX . 'create')
);
return $this->getFormView('create', ['form' => $form->createView()]);
}
/**
* Creates processing.
*
* @param Request $request
*
* @Route("/create")
* @Method({"POST"})
*
* @return RedirectResponse|Response
* @throws LeagueException
*/
public function createProcessAction(Request $request)
{
if ($request->getMethod() != 'POST') {
throw new LeagueException('League create: only POST method is allowed.');
}
$form = $this->getForm(
new LeagueModel(),
'POST',
$this->generateUrl(self::ROUTER_PREFIX . 'create')
);
$form->handleRequest($request);
if (!$form->isSubmitted()) {
throw new LeagueException('League create: form is not submitted.');
}
if ($form->isValid() !== true) {
return $this->getFormView('create', ['form' => $form->createView()]);
}
try {
$data = $form->getData();
$league = new League();
$league->setName($data->getName());
$league->setCountry($data->getCountry());
$em = $this->getDoctrine()->getManager();
$em->persist($league);
$em->flush();
} catch (DBALException $e) {
$message = sprintf('DBALException [%s]: %s', $e->getCode(), $e->getMessage());
} catch (ORMException $e) {
$message = sprintf('ORMException [%s]: %s', $e->getCode(), $e->getMessage());
} catch (Exception $e) {
$message = sprintf('Exception [%s]: %s', $e->getCode(), $e->getMessage());
}
if (isset($message)) {
throw new LeagueException($message);
}
return $this->redirect($this->generateUrl(self::ROUTER_PREFIX . 'list'));
}
/**
* Creates form object.
*
* @param LeagueModel $league
* @param string $method
* @param string $action
*
* @return Form
*/
private function getForm(LeagueModel $league, $method, $action)
{
return $this->createForm(
new LeagueType(),
$league,
[
'method' => $method,
'action' => $action
]
);
}
/**
* Creates webform.
*
* @param string $template
* @param array $parameters
*
* @return Response
*/
private function getFormView($template, array $parameters = [])
{
return $this->render(
sprintf('FootballFrontendBundle:League:%s.html.twig', $template),
$parameters
);
}
}
{% extends 'FootballFrontendBundle:League:index.html.twig' %}
{% block body %}
{% spaceless %}
{{ parent() }}
LEAGUE - Create
<hr />
{{ form_start(form, {attr: {novalidate: 'novalidate'}}) }}
{% if form_errors(form) != '' %}
{{ form_errors(form) }}
{% endif %}
<p>NAME: {{ form_widget(form.name) }}</p>
<p>COUNTRY: {{ form_widget(form.country) }}</p>
<p><button name="button">Submit</button></p>
{{ form_end(form) }}
{% endspaceless %}
{% endblock %}