Permalink
Browse files

Fixed bug. UniqueEntity identifier value should not be persisted in c…

…ache.
  • Loading branch information...
Vitaliy Demidov
Vitaliy Demidov committed Aug 6, 2012
1 parent 932aa07 commit 72eab3d85f0c0ce5f4deb8a2980626005dbce8c5
@@ -72,6 +72,7 @@ public function uniqueEntityAction()
$value = $request->request->get('value');
$target = $request->request->get('target');
$ignore = $request->request->get('ignore');
+ $ignore = empty($ignore) ? null : json_decode($ignore, true);
$a = new \stdClass();
try {
if (!empty($target) && !empty($entity) && $this->hasUniqueEntityConstraint($entity, $target)) {
@@ -32,6 +32,7 @@ public function load(array $configs, ContainerBuilder $container)
$container->setParameter('apy_js_form_validation.validation_bundle', $config['validation_bundle']);
$container->setParameter('apy_js_form_validation.javascript_framework', $config['javascript_framework']);
$container->setParameter('apy_js_form_validation.warmer_routes', $config['warmer_routes']);
+ $container->setParameter('apy_js_form_validation.identifier_field', $config['identifier_field']);
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.xml');
@@ -60,6 +61,7 @@ private function getConfigTree()
->canBeUnset()
->prototype('scalar')->end()
->end()
+ ->scalarNode('identifier_field')->defaultValue('jsfv_identifier')->end()
->end()
->end()
->buildTree();
@@ -0,0 +1,90 @@
+<?php
+
+/**
+ * This file is part of the JsFormValidationBundle.
+ *
+ * (c) Abhoryo <abhoryo@free.fr>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace APY\JsFormValidationBundle\EventListener;
+
+use APY\JsFormValidationBundle\Generator\FormValidationScriptGenerator;
+use APY\JsFormValidationBundle\Generator\PostProcessEvent;
+use Symfony\Component\Form\Event\DataEvent;
+use Symfony\Component\Form\FormFactoryInterface;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\Form\FormEvents;
+
+/**
+ * APY\JsFormValidationBundle\EventListener\AddIdentifierSubscriber
+ *
+ * @author Vitaliy Demidov <zend@i.ua>
+ * @since 05 Aug 2012
+ */
+class AddIdentifierSubscriber implements EventSubscriberInterface
+{
+
+ /**
+ * @var FormFactoryInterface
+ */
+ protected $factory;
+
+ /**
+ * @var FormValidationScriptGenerator
+ */
+ protected $jsfv;
+
+ /**
+ * Constructor
+ * @param FormFactoryInterface $factory
+ */
+ public function __construct(FormFactoryInterface $factory, FormValidationScriptGenerator $jsfv)
+ {
+ $this->factory = $factory;
+ $this->jsfv = $jsfv;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function getSubscribedEvents()
+ {
+ return array(FormEvents::PRE_SET_DATA => 'preSetData');
+ }
+
+ /**
+ * Insert entity identifier hidden field into form.
+ *
+ * This field is required in order to UniqueEntity constraint worked
+ * properly on client side with update operation.
+ *
+ * @param DataEvent $event
+ */
+ public function preSetData(DataEvent $event)
+ {
+ $data = $event->getData();
+ $form = $event->getForm();
+
+ // This if statement let's us skip right over the null condition.
+ if (null === $data || !is_object($data)) {
+ return;
+ }
+
+ if ($form->getConfig()->getOption('compound') && $this->jsfv->hasUniqueEntityConstraint(get_class($data))) {
+ //When entity is updated UniqueEntity constraint should ignore
+ //entity with the same primary key id
+ $identifierField = $this->jsfv->getParameter('identifier_field');
+ $form->add($this->factory->createNamed(
+ $identifierField,
+ 'hidden',
+ json_encode($this->jsfv->getEntityIdentifierValue($data)),
+ array(
+ 'property_path' => false,
+ )
+ ));
+ }
+ }
+}
@@ -1,6 +1,6 @@
<?php
-/*
+/**
* This file is part of the JsFormValidationBundle.
*
* (c) Abhoryo <abhoryo@free.fr>
@@ -11,12 +11,40 @@
namespace APY\JsFormValidationBundle\Form\Extension;
+use APY\JsFormValidationBundle\Generator\FormValidationScriptGenerator;
+use APY\JsFormValidationBundle\EventListener\AddIdentifierSubscriber;
+use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
class FormTypeExtension extends AbstractTypeExtension
{
+ /**
+ * @var FormValidationScriptGenerator
+ */
+ protected $jsfv;
+
+ /**
+ * Sets javascript form validation script generator service.
+ *
+ * @param FormValidationScriptGenerator $jsfv
+ */
+ public function setJsfv(FormValidationScriptGenerator $jsfv)
+ {
+ $this->jsfv = $jsfv;
+ }
+
+ /**
+ * Gets javascript form validation script generator service.
+ *
+ * @return FormValidationScriptGenerator Returns javascript form validation script generator service
+ */
+ public function getJsfv()
+ {
+ return $this->jsfv;
+ }
+
/**
* {@inheritdoc}
*
@@ -27,6 +55,17 @@ public function getExtendedType()
return 'form';
}
+ /**
+ * {@inheritdoc}
+ *
+ * @see Symfony\Component\Form.AbstractTypeExtension::buildForm()
+ */
+ public function buildForm(FormBuilderInterface $builder, array $options)
+ {
+ $subscriber = new AddIdentifierSubscriber($builder->getFormFactory(), $this->getJsfv());
+ $builder->addEventSubscriber($subscriber);
+ }
+
/**
* {@inheritdoc}
*
@@ -22,12 +22,7 @@
use Doctrine\Common\Annotations\AnnotationReader;
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Mapping\Loader\AnnotationLoader;
-use Assetic\AssetWriter;
-use Assetic\AssetManager;
-use Assetic\Factory\AssetFactory;
-use Assetic\Factory\LazyAssetManager;
use Assetic\Filter\Yui\JsCompressorFilter;
-use Assetic\FilterManager;
use Assetic\Asset\AssetCollection;
class FormValidationScriptGenerator
@@ -59,6 +54,16 @@ public function __construct(ContainerInterface $container)
$this->em = $container->get('doctrine')->getEntityManager();
}
+ /**
+ * Gets Entity Manager
+ *
+ * @return \Doctrine\ORM\EntityManager Returns Entity Manager
+ */
+ public function getEntityManager()
+ {
+ return $this->em;
+ }
+
/**
* Retrieves validation bundle name.
*
@@ -72,7 +77,19 @@ public function __construct(ContainerInterface $container)
*/
public function getValidationBundle()
{
- return $this->container->getParameter('apy_js_form_validation.validation_bundle');
+ return $this->getParameter('validation_bundle');
+ }
+
+ /**
+ * Gets parameter value from apy_js_form_validation namespace
+ *
+ * @param string $parameter Parameter name
+ * @return mixed|null Returns parameter value, or NULL if it does not exist
+ */
+ public function getParameter($parameter)
+ {
+ return $this->container->hasParameter('apy_js_form_validation.' . $parameter) ?
+ $this->container->getParameter('apy_js_form_validation.' . $parameter) : null;
}
/**
@@ -102,10 +119,68 @@ public function getClassMetadata ($entityName)
}
/**
- * Generates
- * @param FormView $formView
- * @param unknown_type $overwrite
- * @throws \RuntimeException
+ * Checks, whether the entity has UiqueEntity Constraint
+ *
+ * @param string $entityName Entity Name
+ * @return boolean Returns TRUE if desired entity has UniqueEntity constraint
+ */
+ public function hasUniqueEntityConstraint ($entityName)
+ {
+ $ret = false;
+ $metadata = $this->getClassMetadata($entityName);
+ if (!empty($metadata->constraints)) {
+ foreach ($metadata->constraints as $constraint) {
+ if (preg_match("/\\\\UniqueEntity$/", get_class($constraint))) {
+ $ret = true;
+ break;
+ }
+ }
+ }
+ return $ret;
+ }
+
+ /**
+ * Gets entity identifier value.
+ *
+ * When entity is updated UniqueEntity constraint should ignore
+ * entity with the same primary key id
+ *
+ * @param object $entity Entity object which is applied to the form
+ * @return array|null Returns array that contains values of the fields that form
+ * primary key of entity
+ * @throws \RuntimeException
+ */
+ public function getEntityIdentifierValue ($entity)
+ {
+ if (empty($entity) || !is_object($entity)) {
+ throw new \RuntimeException("Invalid parameter. Entity should be provided.");
+ }
+ $entityMetadata = $this->getEntityManager()->getClassMetadata(get_class($entity));
+ $identifier = $entityMetadata->getIdentifier();
+ $ignore = array();
+ if (!empty($identifier)) {
+ foreach ($identifier as $prop) {
+ $propGetter = 'get' . ucfirst($prop);
+ if (method_exists($entity, $propGetter)) {
+ $ignore[] = $entity->$propGetter();
+ } elseif (isset($entity->$prop)) {
+ $ignore[] = $entity->$prop;
+ } else {
+ throw new \RuntimeException('Could not access value for the field: ' . $prop);
+ }
+ }
+ }
+ if (empty($ignore)) $ignore = null;
+
+ return $ignore;
+ }
+
+ /**
+ * Generates client-side javascript that validates form
+ *
+ * @param FormView $formView
+ * @param boolean $overwrite
+ * @throws \RuntimeException
*/
public function generate(FormView $formView, $overwrite = false)
{
@@ -233,27 +308,10 @@ public function generate(FormView $formView, $overwrite = false)
//We need to know entity class for the field which is applied by UniqueEntity constraint
if ($constraintName == 'UniqueEntity' && !empty($formField->parent)) {
$entity = $formField->parent->get('value');
- $entityMetadata = $this->em->getClassMetadata(get_class($entity));
- $identifier = $entityMetadata->getIdentifier();
- //When entity is updated UniqueEntity constraint should ignore
- //entity with the same primary key id
- $ignore = array();
- if (!empty($identifier)) {
- foreach ($identifier as $prop) {
- $propGetter = 'get' . ucfirst($prop);
- if (method_exists($entity, $propGetter)) {
- $ignore[] = $entity->$propGetter();
- } elseif (isset($entity->$prop)) {
- $ignore[] = $entity->$prop;
- } else {
- throw new \RuntimeException('Could not access value for the field: ' . $prop);
- }
- }
- }
- if (empty($ignore)) $ignore = null;
$constraintParameters += array(
'entity:' . json_encode(get_class($entity)),
- 'ignore:' . json_encode($ignore),
+ 'identifier_field_id:'.
+ json_encode($formView->children[$this->getParameter('identifier_field')]->get('id')),
);
}
foreach ($constraintProperties as $variable => $value) {
@@ -32,6 +32,9 @@
<service id="form.type_extension.validation_groups" class="%form.type_extension.validation_groups.class%">
<tag name="form.type_extension" alias="form" />
+ <call method="setJsfv">
+ <argument type="service" id="jsfv.generator" />
+ </call>
</service>
<service id="form.type_extension.repeated_field_parameters" class="%form.type_extension.repeated_field_parameters.class%">
@@ -12,6 +12,7 @@ apy_js_form_validation:
validation_bundle: APYJsFormValidationBundle
script_directory: bundles/jsformvalidation/js/
warmer_routes: [route1,route2]
+ identifier_field: jsfv_identifier
```
* `enabled` is optional (Default: `true`). Set to `false` disable all javascript form validations if you use the Twig function.
@@ -38,3 +39,7 @@ Here is a framework template of a the common script for validation.
* `script_directory` is optional (Default: `bundles/jsformvalidation/js/`). Define where scripts will be generated.
* `warmer_routes` is optional (Default: `~`). See [Assets warmer](assets_warmer.md)
+
+* `identifier_field` is optional (Default: `jsfv_identifier`).
+Defines the name of the hidden field which serves to convey the primary identifier value for UniqueEntity constraint request.
+This value will be ignored when entity is updated.
@@ -20,7 +20,7 @@ function UniqueEntity(field, params)
'entity': params.entity,
'target': params.fields,
'value': value,
- 'ignore': params.ignore ? params.ignore : ''
+ 'ignore': params.identifier_field_id ? jsfv.id(params.identifier_field_id).value : ''
},
success: function(data, textStatus) {
{% block check_uniquity_handler %}

0 comments on commit 72eab3d

Please sign in to comment.