Skip to content
65 changes: 61 additions & 4 deletions lib/sfDoctrineDynamicFormRelations.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* @package sfDoctrineDynamicFormRelationsPlugin
* @subpackage form
* @author Kris Wallsmith <kris.wallsmith@symfony-project.com>
* @author Christian Schaefer <caefer@ical.ly>
*/
class sfDoctrineDynamicFormRelations extends sfForm
{
Expand Down Expand Up @@ -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)
Expand All @@ -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.
*
Expand All @@ -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
Expand Down Expand Up @@ -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']));
Expand Down
29 changes: 25 additions & 4 deletions lib/sfDoctrineDynamicFormRelationsListener.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* @package sfDoctrineDynamicFormRelationsPlugin
* @subpackage record
* @author Kris Wallsmith <kris.wallsmith@symfony-project.com>
* @author Christian Schaefer <caefer@ical.ly>
*/
class sfDoctrineDynamicFormRelationsListener extends Doctrine_Record_Listener
{
Expand All @@ -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))
Expand Down