Таким образом, я провел приблизительно 5 или 6 часов сегодня, борясь с формами Symfony2, и в пункте, где я хотел бы некоторый совет от других членов сообщества. Я попробовал более чем 3 различных метода, чтобы достигнуть того, что я после и не имел никакого успеха. Я прочитал докторов, googled все, спросил других, и я только немного более обеспечен чем тогда, когда я начал.
Мой вариант использования
Я строю систему, где можно заказать билеты. Но основная проблема состоит в том, как проектировать часть заказа системы.
- у Билета есть имя и даты начала и даты окончания, где это доступно (другой материал также, но позволяет, сохраняет пример простым.
- <сильному> Заказу можно было выбрать многочисленные Билеты и для каждого Билета, есть количество.
- Заказ есть Клиент . Эта часть прекрасна и работает денди !
После чтения вокруг и попытки разных вещей, я заключил, что, чтобы представлять Билет и количество Заказа, мне было нужно другое предприятие, от которого ЗаказБилет соответствует ЗаказItem https://github.com/beberlei/AcmePizzaBundle и Пицца - мой Билет.
- ЗаказБилет есть Билет и количество.
На моей странице заказа, где заказ создается, я хочу следующее:
- форма для Потребительских деталей - имя, электронная почта, адрес. Эта часть хорошо работает .
- форма для Билетов . Я хочу показанное название Билета в textbox или даже последовательности; не в избранной коробке (который является тем, что происходит теперь). Я хочу, чтобы количество было определено рядом с названием билета. Если нет никакого набора количества, это означает, что билет не отобран.
- Билеты должны быть фильтрованы , где они доступны в зависимости от сегодняшней даты - это достигается в другом месте (в администраторе бэкенда, где они создаются) при помощи таможенного метода хранилища на типе формы с закрытием конструктора запросов.
Мой бэкэнд
The Заказ/ЗаказБилет/Билет design is largely based on https://github.com/beberlei/AcmePizzaBundle
Билет
/**
* @ORM\Entity(repositoryClass="Foo\BackendBundle\Entity\БилетsRepository")
* @ORM\HasLifecycleCallbacks
* @ORM\Table(name="Билетs")
*/
class Билетs
{
//id fields and others
/**
* @Assert\NotBlank
* @ORM\Column(type="string", nullable=true)
*/
protected $name;
/**
* @ORM\Column(type="date", name="available_from", nullable=true)
*/
protected $availableFrom;
/**
* @ORM\Column(type="date", name="available_to", nullable=true)
*/
protected $availableTo;
}
ЗаказБилет
/**
* @ORM\Table()
* @ORM\Entity
*/
class ЗаказБилет
{
//id field
/**
* @ORM\Column(name="quantity", type="integer")
*/
private $quantity;
/**
* @ORM\ManyToOne(targetEntity="Билетs")
*/
protected $Билет;
/**
* @ORM\ManyToOne(targetEntity="Заказs", inversedBy="Билетs")
*/
protected $Заказ;
//getters and setters for quantity, Билет and Заказ
}
Заказ
/**
* @ORM\Entity
* @ORM\HasLifecycleCallbacks
* @ORM\Table(name="Заказs")
*/
class Заказs
{
//id field and other stuff
/**
* @ORM\OneToMany(targetEntity="ЗаказБилет", mappedBy="Заказ", cascade={"persist"})
**/
protected $Билетs;
/**
* @ORM\ManyToOne(targetEntity="Клиент", cascade={"persist"})
*/
protected $Клиент;
public function __construct()
{
$this->Билетs = new \Doctrine\Common\Collections\ArrayCollection();
}
//getters, setters, add for Билетs and Клиент
}
Клиент
/**
* @ORM\Table()
* @ORM\Entity
*/
class Клиент
{
//id, name, email, address fields
}
Это создает схему как так (стол, называющий различия, от авто поколения):
CREATE TABLE `Билетs` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`available_from` date DEFAULT NULL,
`available_to` date DEFAULT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `Клиент` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`email` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`address` longtext COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `ЗаказБилет` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`Билет_id` int(11) DEFAULT NULL,
`Заказ_id` int(11) DEFAULT NULL,
`quantity` int(11) NOT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `Заказs` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`Клиент_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
);
Формы
class КлиентType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('email')
->add('name')
->add('address')
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Foo\BackendBundle\Entity\Клиент'
));
}
public function getName()
{
return 'foo_backendbundle_Клиентtype';
}
}
class ЗаказБилетType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('quantity', 'integer')
->add('Билет')
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Foo\BackendBundle\Entity\ЗаказБилет'
));
}
public function getName()
{
return 'foo_backendbundle_ЗаказБилетtype';
}
}
class ЗаказsType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('Клиент', new КлиентType())
->add('Билетs', 'collection', array(
'type' => new ЗаказБилетType(),
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
))
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Foo\BackendBundle\Entity\Заказs',
));
}
public function getName()
{
return 'foo_backendbundle_Заказstype';
}
}
Форма
<form action="{{ path('index') }}" method="post" {{ form_enctype(form) }}>
Билетs
{{ form_errors(form) }}
<table>
<thead>
<tr>
<td>Билет</td>
<td>Quantity</td>
</thead>
<tbody>
{% for Билетrow in form.Билетs %}
<tr>
<td>{{ form_widget(Билетrow.Билет) }}</td>
<td>{{ form_widget(Билетrow.quantity) }}</td>
</tr>
{% endfor %}
</tbody>
</table>
Клиент
{% for Клиент in form.Клиент %}
{{ form_row(Клиент) }}
{% endfor %}
</form>
И наконец диспетчер
class DefaultController extends Controller
{
/**
* @Route("/", name="index")
* @Template()
*/
public function indexAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
//IMPORTANT - the Билетs are prefiltered for active Билетs, these have to be injected into the Заказ atm. In other places I use this method on the query builder
$Билетs = $em->getRepository('FooBackendBundle:Билетs')->findActive();
//check no Билетs
$Заказ = new Заказs();
//To prepopulate the Заказ with the available Билетs, we have to do it like this, due to it being a collection,
//rather than using the Формы query_builder like everywhere else
foreach($Билетs as $Билет) {
$ot = new ЗаказБилет();
$ot->setБилет($Билет);
$ot->setQuantity(0);
$ot->setЗаказ($Заказ);
$Заказ->addБилет($ot);
}
$form = $this->createForm(new ЗаказsType(), $Заказ);
if ($request->isMethod('POST')) {
$form->bind($request);
//IMPORTANT here I have to remove the previously added Билетs where the quantity is 0 - as they're not wanted in the Заказ. Is there a better way to do this?
//if the quantity of Билет is 0, do not add to Заказ
//note we use the validation callback in Заказ to check total quantity of ЗаказБилетs is > 0
$Заказ->removeБилетsWithNoQuantity();
if ($form->isValid()) {
$em->persist($Заказ);
$em->flush();
return $this->redirect($this->generateUrl('Заказ_show', array('id' => $Заказ->getId())));
}
}
return array('form' => $form->createView());
}
}
Резюме
This works and will save the Заказ correctly, but I'm not sure it is the correct way to do what I want, and it does not display as I want.
You can see below in the images how it looks and how the Заказ goes through. It is worth noting that in each of the Билет drop downs is the rest of the Билетs but which are not active.
The Заказ page:

The Заказ Резюме page after save:

The 3 Билетs displayed are the ones that have been filtered, and I only want these Билетs on Форма. I ONLY WANT TO SEE THE Билет NAME, NOT AN EDITABLE DROP DOWN.
The core problem is that they are presented as editable drop downs. I may just want a text string of the Билет name, or maybe even the Билет price in the future. I'm not sure how to achieve this. I know that the Билет field and relationship must be rendered somehow so that it can be bound in the controller. So basically I want to be able to use the Билет entity and it's fields on the same row as the quantity text box.
So let's step out of the crapstorm of Symfony2's Формы and put this in perspective - in the normal world, obviously I'd just retrieve the Билетs, then for each Билет, I'd print the Билет name, any other stuff I wanted, a hidden Билет id, then an input for the Билет quantity. Back into SF2 a little - I guess I need the Билет entity available whilst looping the ЗаказБилет collection.
Пожалуйста, помоги мне!