diff --git a/lib/sfDoctrineDynamicFormRelations.class.php b/lib/sfDoctrineDynamicFormRelations.class.php index 041eeea..0f5d731 100644 --- a/lib/sfDoctrineDynamicFormRelations.class.php +++ b/lib/sfDoctrineDynamicFormRelations.class.php @@ -8,6 +8,7 @@ * @package sfDoctrineDynamicFormRelationsPlugin * @subpackage form * @author Kris Wallsmith + * @author Christian Schaefer */ class sfDoctrineDynamicFormRelations extends sfForm { @@ -78,6 +79,49 @@ public function filterValues(sfEvent $event, $values) { $form = $event->getSubject(); + $this->reEmbed($form, $values); + + $this->correctValidators($form); + + return $values; + } + + // protected + + /** + * Replacing validators with their up-to-date equivalent from an embedded form. + * + * @param sfForm $form Form instance on which to replace validators + * + * @return sfValidatorSchema + */ + protected function correctValidators($form) + { + foreach($form->getEmbeddedForms() as $field => $embed) + { + if($form->getValidator($field) instanceof sfValidatorSchema) + { + foreach($embed->getValidatorSchema()->getFields() as $name => $validator) + { + if(!$form->getValidator($field)->offsetExists($name)) + { + $embed->getValidatorSchema()->offsetUnset($name); + } + } + $form->setValidator($field, $this->correctValidators($embed)); + } + } + return $form->getValidatorSchema(); + } + + /** + * Re-embeds all dynamically embedded relations recursively to match up with the input values. + * + * @param sfForm $form A form + * @param array $values Tainted form values + */ + protected function reEmbed(sfForm $form, $values) + { if ($relations = $form->getOption('dynamic_relations')) { foreach (array_keys($relations) as $field) @@ -89,11 +133,23 @@ public function filterValues(sfEvent $event, $values) $form->getObject()->addListener(new sfDoctrineDynamicFormRelationsListener($form)); } - return $values; + // recursive re-embed down the line + foreach ($form->getEmbeddedForms() as $field => $embed) + { + if(array_key_exists($field, $values)) + { + $this->reEmbed($embed, $values[$field]); + } + else + { + // unsetting field when no value for it exists + // this happens on the embedded form - unfortunately its validator schema is not considered by its parent + // that's why it needs to be corrected afterwards. @see self::correctValidators() + $form->offsetUnset($field); + } + } } - // protected - /** * Embeds a dynamic relation in a form. * @@ -119,7 +175,7 @@ protected function embedDynamicRelation(sfForm $form, $relationName, $formClass // validate relation type if (Doctrine_Relation::MANY != $relation->getType()) { - throw new LogicException(sprintf('The %s "%s" relation is not a MANY relation.', get_class($form->getObject()), $relation->getName())); + throw new LogicException(sprintf('The %s "%s" relation is not a MANY relation.', get_class($form->getObject()), $relation->getAlias())); } // use the default form class @@ -176,6 +232,7 @@ protected function doEmbed(sfForm $form, $field, $values) else { $object = $config['relation']->getTable()->create(); + $object->fromArray($value); $form->getObject()->get($config['relation']->getAlias())->add($object); $child = $r->newInstanceArgs(array_merge(array($object), $config['arguments'])); diff --git a/lib/sfDoctrineDynamicFormRelationsListener.class.php b/lib/sfDoctrineDynamicFormRelationsListener.class.php index ee77273..8974d42 100644 --- a/lib/sfDoctrineDynamicFormRelationsListener.class.php +++ b/lib/sfDoctrineDynamicFormRelationsListener.class.php @@ -6,6 +6,7 @@ * @package sfDoctrineDynamicFormRelationsPlugin * @subpackage record * @author Kris Wallsmith + * @author Christian Schaefer */ class sfDoctrineDynamicFormRelationsListener extends Doctrine_Record_Listener { @@ -31,20 +32,40 @@ public function __construct(sfForm $form) * @see Doctrine_Record_Listener */ public function preSave(Doctrine_Event $event) + { + // this listener may have been added several times with a different $form instance + // but as listeners have a model rather than a record scope we need to filter if + // this current listener actually matches! + if($this->form->getObject()->id == $event->getInvoker()->id) + { + $this->doPreSave($event->getInvoker(), $this->form); + } + } + + protected function doPreSave(Doctrine_Record $record, sfForm $form) { // loop through relations - if ($relations = $this->form->getOption('dynamic_relations')) + if ($relations = $form->getOption('dynamic_relations')) { foreach ($relations as $field => $config) { + $collection = $record->get($config['relation']->getAlias()); + // collect form objects for comparison $search = array(); - foreach ($this->form->getEmbeddedForm($field)->getEmbeddedForms() as $embed) + try + { + foreach ($form->getEmbeddedForm($field)->getEmbeddedForms() as $i => $embed) + { + $search[] = $embed->getObject(); + } + } + catch(InvalidArgumentException $e) { - $search[] = $embed->getObject(); + // previously embedded form was removed at the end of form.filter_values as there were no values for it. + // @see sfDoctrineDynamicFormRelations::correctValidators() } - $collection = $event->getInvoker()->get($config['relation']->getAlias()); foreach ($collection as $i => $object) { if (false === $pos = array_search($object, $search, true))