Zapisywanie osadzonych formularzy w Symfony działa inaczej niż intuicyjnie zakładałem. Co ciekawsze, już raz się z tym kiedyś zetknąłem, ale było to na tyle dawno, że zapomniałem rozwiązania.
Załóżmy, że w zwykłym formularzu dziedziczącym z klasy sfForm
osadzam formularz oparty na modelu z bazy danych czyli dziedziczący z sfFormDoctrine
.
class OnePageCheckoutForm extends sfForm
{
public function configure()
{
$this->widgetSchema['same_address'] = new sfWidgetFormInputCheckbox();
$this->validatorSchema['same_address'] = new sfValidatorBoolean();
$this->embedForm('customer', new CustomerForm());
$this->embedForm('shipment_address', new AddressForm());
$this->getWidgetSchema()->setNameFormat('checkout_form[%s]');
}
}
W kontrolerze chcę zapisać dane z osadzonych formularzy, ale oczywiście klasa OnePageCheckoutForm
nie ma metody save()
. W pierwszym odruchu napisałem mniej więcej coś takiego:
public function executeCheckout(sfWebRequest $request)
{
if (!$this->getUser()->hasCart())
{
return $this->redirect('@cart');
}
$this->cart = $this->getUser()->getCart();
$this->form = new OnePageCheckoutForm();
if ($request->getMethod() == 'POST')
{
$this->form->bind($request->getParameter($this->form->getName()), $request->getFiles($this->form->getName()));
if ($this->form->isValid())
{
$values = $this->form->getValues();
$this->form->getEmbeddedForm('customer')->save();
$this->form->getEmbeddedForm('shipment_address')->save();
$this->cart->setCustomerId($this->form->getEmbeddedForm('customer')->getObject()->getId());
$this->cart->save();
$this->redirect('@cart_payment_process');
}
}
}
Ku memu zaskoczeniu zobaczyłem wyjątek, z którego wynikało, że zapis formularza customer
się nie powiódł, bo formularz nie zawiera danych. Dla pewności wrzuciłem w kod kilka var_dumpów:
// wynik: dane dla całego formularza łącznie z osadzonymi
var_dump($this->form->getValues());
// wynik: false
var_dump($this->form->getEmbeddedForm('customer')->isBound());
// wynik: pusta tablica
var_dump($this->form->getEmbeddedForm('customer')->getObject()->toArray());
Jak się okazało, formularz klasy sfForm
nie aktualizuje obiektów w osadzonych formularzach, trzeba to wykonać samemu:
$this->form->getEmbeddedForm('customer')
->updateObject($this->form->getValue('customer'));
Można to zrobić w kontrolerze, ja ze względów konwencjonalno-estetycznych dopisałem do klasy OnePageCheckoutForm
metody updateEmbeddedForms()
i saveEmbeddedForms()
, które są z grubsza kopią tak samo nazwanych metod z klasy sfFormObject
.