Varsayalım ki elimizdeki Country ve League arasında one-to-many ilişki var. Basit bir şekilde her bir entity için birer tane formtype yaratıp, veritabanına bilgi girişi yapabiliriz ama bazen bu tarz işlemleri tek bir formtype kullanarak yapmakta isteyebiliriz. İşte bu örnek bize bu işlemi gösteriyor.


Country entity


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;
use Symfony\Component\Validator\Constraints as Assert;

/**
* @ORM\Table(name="country")
* @UniqueEntity(fields="code", message="Code is already taken.")
* @UniqueEntity(fields="name", message="Name is already taken.")
*/
class Country
{
/**
* @ORM\Id
* @ORM\Column(type="smallint")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;

/**
* @ORM\Column(type="string", length=2, unique=true)
* @Assert\NotBlank(message="Code is required.")
*/
protected $code;

/**
* @ORM\Column(name="name", type="string", length=100, unique=true)
* @Assert\NotBlank(message="Name is required.")
*/
protected $name;

/**
* @ORM\OneToMany(
* targetEntity="League",
* mappedBy="country",
* cascade={"persist", "remove"}
* )
*/
protected $league;
}

League entity


Eğer $country özelliğine @Assert\NotBlank(message="Country is required.") kontrolünü ekleseydik, submit yaptığımız form her zaman geçersiz olurdu çünkü, daha yeni yaratıyor olduğumuz için Country bu aşamada mevcut değil.


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;
use Symfony\Component\Validator\Constraints as Assert;

/**
* @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)
* @Assert\NotBlank(message="Name is required.")
*/
protected $name;

/**
* @ORM\ManyToOne(
* targetEntity="Country",
* inversedBy="league"
* )
* @ORM\JoinColumn(
* name="country_id",
* referencedColumnName="id",
* onDelete="CASCADE",
* nullable=false
* )
*/
protected $country;
}

Country FormType


namespace Football\FrontendBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class CountryType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options = [])
{
$builder
->setMethod($options['method'])
->setAction($options['action'])
->add('code', 'text', ['required' => false, 'error_bubbling' => true])
->add('name', 'text', ['required' => false, 'error_bubbling' => true]);
}

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

public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(
['data_class' => 'Football\FrontendBundle\Entity\Country']
);
}
}

League FormType


namespace Football\FrontendBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class LeagueType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options = [])
{
$builder
->setMethod($options['method'])
->setAction($options['action'])
->add('name', 'text', ['required' => false, 'error_bubbling' => true]);
}

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

public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(
['data_class' => 'Football\FrontendBundle\Entity\League']
);
}
}


CountryLeague FormType


namespace Football\FrontendBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class CountryLeagueType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options = [])
{
$builder
->setMethod($options['method'])
->setAction($options['action'])
->add('country', new CountryType())
->add('league', new LeagueType());
}

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

public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(
['data_class' => null]
);
}
}

Controller


/**
* @Route("/countryleague")
*/
class CountyLeagueController extends Controller
{
const ROUTER_PREFIX = 'football_frontend_countyleague_';

/**
* Create page.
*
* @Route("/create")
* @Method({"GET"})
*
* @return Response
*/
public function createAction()
{
$form = $this->getForm(
'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 CountryLeagueException
*/
public function createProcessAction(Request $request)
{
if ($request->getMethod() != 'POST') {
throw new CountryLeagueException('CountryLeague create: only POST method is allowed.');
}

$form = $this->getForm(
'POST',
$this->generateUrl(self::ROUTER_PREFIX . 'create')
);
$form->handleRequest($request);

if (!$form->isSubmitted()) {
throw new CountryLeagueException('CountryLeague create: form is not submitted.');
}

if ($form->isValid() !== true) {
return $this->getFormView('create', ['form' => $form->createView()]);
}

try {
$data = $form->getData();

$country = new Country();
$country->setCode($data['country']->getCode());
$country->setName($data['country']->getName());

$league = new League();
$league->setName($data['league']->getName());
$league->setCountry($country);

$em = $this->getDoctrine()->getManager();
$em->persist($country);
$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 CountryLeagueException($message);
}

return $this->redirect($this->generateUrl(self::ROUTER_PREFIX . 'create'));
}

/**
* Creates form object.
*
* @param string $method
* @param string $action
*
* @return Form
*/
private function getForm($method, $action)
{
return $this->createForm(
new CountryLeagueType(),
null,
[
'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:CountryLeague:%s.html.twig', $template),
$parameters
);
}
}

Twig şablonu


{% extends 'FootballFrontendBundle:CountryLeague:index.html.twig' %}

{% block body %}
{% spaceless %}
{{ parent() }}
COUNTRY LEAGUE - Create
<hr />
{{ form_start(form) }}
{% if form_errors(form) != '' %}

{{ form_errors(form) }}

{% endif %}

<p>COUNTRY CODE: {{ form_widget(form.country.code) }}</p>
<p>COUNTRY NAME: {{ form_widget(form.country.name) }}</p>
<p>LEAGUE NAME: {{ form_widget(form.league.name) }}</p>

{{ form_widget(form._token) }}

<p><button name="button">Submit</button></p>
{{ form_end(form, {'render_rest': false}) }}
{% endspaceless %}
{% endblock %}