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
.