Created
July 6, 2011 21:22
-
-
Save n3b/1068357 to your computer and use it in GitHub Desktop.
symfony2 dynamic validator constraints
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace n3b\Bundle\Shop\Form; | |
use Symfony\Component\Form\AbstractType; | |
use Symfony\Component\Form\FormBuilder; | |
use Symfony\Component\Form\FormEvents; | |
class CheckoutFullType extends AbstractType | |
{ | |
public function buildForm(FormBuilder $builder, array $options) | |
{ | |
$builder->add('deliver', 'checkbox') | |
->add('user_save', 'checkbox') | |
->add('checkout', new CheckoutType()); | |
$builder->addEventSubscriber(new EventSubscriber\PreBindDataSubscriber()); | |
$builder->addValidator(new Validator\CheckoutDeliveryValidator()); | |
} | |
public function getDefaultOptions(array $options) | |
{ | |
return array( | |
'validation_groups' => array('pass_through'), | |
'data_class' => 'n3b\Bundle\Shop\Entity\Customer', | |
); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace n3b\Bundle\Shop\Entity; | |
use Doctrine\ORM\Mapping as ORM; | |
use Symfony\Component\Validator\Constraints as Assert; | |
use Doctrine\Common\Collections\ArrayCollection; | |
use n3b\Bundle\Shop\Model\Customer as BaseCustomer; | |
/** | |
* @ORM\Entity | |
*/ | |
class Customer extends BaseCustomer | |
{ | |
/** | |
* @ORM\Id | |
* @ORM\Column(type="integer") | |
* @ORM\GeneratedValue(strategy="AUTO") | |
*/ | |
protected $id; | |
/** | |
* @ORM\Column(unique="true", nullable="true") | |
* @Assert\NotBlank(groups={"registration"}) | |
* @Assert\Blank(groups={"pass_through"}) | |
*/ | |
protected $login; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Symfony\Component\Form; | |
use Symfony\Component\Form\Event\DataEvent; | |
use Symfony\Component\Form\Event\FilterDataEvent; | |
use Symfony\Component\Form\Exception\FormException; | |
use Symfony\Component\Form\Exception\UnexpectedTypeException; | |
use Symfony\Component\Form\Exception\TransformationFailedException; | |
use Symfony\Component\HttpFoundation\Request; | |
use Symfony\Component\EventDispatcher\EventDispatcherInterface; | |
class Form implements \IteratorAggregate, FormInterface | |
{ | |
... | |
//этого метода нет в дефолтной комплектации | |
public function setAttribute($name, $value) | |
{ | |
$this->attributes[$name] = $value; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace n3b\Bundle\Shop\Form\EventSubscriber; | |
use Symfony\Component\Form\FormEvents; | |
use Symfony\Component\Form\Event\DataEvent; | |
use Symfony\Component\EventDispatcher\EventSubscriberInterface; | |
class PreBindDataSubscriber implements EventSubscriberInterface | |
{ | |
public function onPreBindData(DataEvent $event) | |
{ | |
$data = $event->getData(); | |
if(isset($data['user_save'])) | |
$event->getForm()->get('checkout')->get('customer')->setAttribute('validation_groups', array('registration')); | |
} | |
static public function getSubscribedEvents() | |
{ | |
return array(FormEvents::PRE_BIND => 'onPreBindData'); | |
} | |
} |
То, что родительская форма возвращает null ($event->getForm()->getData()
) вроде как правильно, судя по этому коду:
$form = $this->services['ff']->create(new CheckoutFullType());
$form->get('checkout')->setData($checkout);
Т.к. setData()
вызывается непосредственно для элемента checkout.
А в объекте $checkout
для формы $event->getForm()->get('checkout')->get('customer')
есть данные или null?
Спасибо за информацию о guesser'ах и FormFactory, не знал про эту фичу. В таком случае Вы правы, это баг - что не учитывается группа валидации при определении валидаторов.
Я не стал там ковыряться, решил инициализировать модель после байнда:
$form = $this->services['ff']->create(new CheckoutFullType());
if ($this->services['request']->getMethod() == 'POST') {
$form->bindRequest($this->services['request']);
$checkout = $form->get('checkout')->getData();
при таком раскладе в onPreBindData
все поголовно в нулях.
По guesser'ам ждем фиксов. Пока накидал цепочку инжектов опций, начиная от билдера и заканчивая непосредственно guesser'ами.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Странно, сейчас проверил -
$event->getForm()->get('checkout')->getData()
действительно возвращает пустой объект модели (да, именно этот объект привязывается до байнда), однако родительская форма$event->getForm()->getData()
отдает все жеnull
. Аналогично с дочерней формой$event->getForm()->get('checkout')->get('customer')
тоже возвращаетnull
. Видимо, связывание билдером модели с формой происходит позже.Насчет
required
связь есть. В FormFactory:Эти guesser'ы и задают атрибуты формам, которые в дальнейшем выводятся при генерации вида формы. А сами guesser'ы проверяют в аннотациях только соответствие классам. Таким образом,
@Assert\NotBlank(groups={"registration"})
будет проводить валидацию только для группregistration
, а вот атрибут формы будет, вне зависимости от группы,required
. Что с учетом html5 вообще не даст отправить форму.З.Ы. посмотрите класс Symfony\Component\Form\Extension\Validator\ValidatorTypeGuesser