I believe there is currently no support for circular references in
data fixtures. I have created a patch for sfPropel15Plugin that allows
this, and I think it can be easily adapted for Doctrine or Propel 1.4.

My solution allows you to update an already defined and saved object,
and it provides an onDelete -> setNull syntax to "unravel" the
circular reference when removing the data.

I have done some research for existing solutions, but I only found
unanswered questions (eg. [
http://groups.google.com/group/symfony-users/browse_thread/thread/f1bc6a2e811c545d/24e673ca8b14296c
], [ http://forum.symfony-project.org/index.php/m/95747/ ]), so I
think there is a need for this.

Current limitations:
 - The update of an object must happen in a different file, since
sfYaml overwrites the first group in the file with the second, but not
across files.
 - loadMany2Many has not been changed (yet), so I don't know whether
that syntax also works.

Example usage:

schema.yml:
Parent:
  parent_id
  name
  preferred_child_id (default null)
Child:
  child_id
  name
  parent_id


The preferred_child_id is the circular reference: we cannot create it
before one Child exists, but we cannot create a Child without a
Parent.


fixtures/parents.yml:
Parent:
  John:
    name: John

fixtures/children.yml:
Child:
  Alfred:
    name: Alfred
    parent_id: John
  Beatrice:
    name: Beatrice
    parent_id: John

Parent:
  John:
    preferred_child_id: Alfred
  _onDelete:
    setNull: preferred_child_id


All comments are welcome.

Greetings,

Jan Fabry


Index: plugins/sfPropel15Plugin/lib/addon/sfPropelData.class.php
===================================================================
--- plugins/sfPropel15Plugin/lib/addon/sfPropelData.class.php
(revision 29402)
+++ plugins/sfPropel15Plugin/lib/addon/sfPropelData.class.php   (working
copy)
@@ -132,9 +132,20 @@
         {
           throw new InvalidArgumentException(sprintf('Unknown class
"%s".', $class));
         }
+
+        // Skip entries starting with an underscore
+        if (strncmp($key, '_', 1) === 0) {
+          continue;
+        }
+
+        $referenceKey =
Propel::importClass(constant(constant($class.'::PEER').'::CLASS_DEFAULT')).'_'.
$key;
+
+        if (array_key_exists($referenceKey, $this-
>object_references)) {
+          $obj = $this->object_references[$referenceKey];
+        } else {
+          $obj = new $class();
+        }

-        $obj = new $class();
-
         if (!$obj instanceof BaseObject)
         {
           throw new RuntimeException(sprintf('The class "%s" is not a
Propel class. This probably means there is already a class named "%s"
somewhere in symfony or in your project.', $class, $class));
@@ -197,7 +208,7 @@
         // save the object for future reference
         if (method_exists($obj, 'getPrimaryKey'))
         {
-          $this-
>object_references[Propel::importClass(constant(constant($class.'::PEER').'::CLASS_DEFAULT')).'_'.
$key] = $obj;
+          $this->object_references[$referenceKey] = $obj;
         }
       }
     }
@@ -288,10 +299,28 @@
           throw new InvalidArgumentException(sprintf('Unknown class
"%sPeer".', $class));
         }

-        $this->log(sprintf('deleting data from %s', $class));
-        call_user_func(array(constant($class.'::PEER'),
'doDeleteAll'), $this->con);
+
+        if (array_key_exists('_onDelete', $data[$class])) {
+          $this->log(sprintf('executing onDelete sequence of %s',
$class));
+          if (array_key_exists('setNull', $data[$class]
['_onDelete'])) {
+            $nullableFields = $data[$class]['_onDelete']['setNull'];
+            if (!is_array($nullableFields)) {
+              $nullableFields = array($nullableFields);
+            }
+            $updateCriteria = new Criteria();
+            $tableName =
constant(constant($class.'::PEER').'::TABLE_NAME');
+            foreach ($nullableFields as $field) {
+              // Camelize?
+              $updateCriteria->add($tableName . '.' . $field, null);
+            }
+            call_user_func(array(constant($class.'::PEER'),
'doUpdate'), $updateCriteria, $this->con);
+          }
+        } else {
+          $this->log(sprintf('deleting data from %s', $class));
+          call_user_func(array(constant($class.'::PEER'),
'doDeleteAll'), $this->con);
+          $this->deletedClasses[] = $class;
+        }

-        $this->deletedClasses[] = $class;
       }
     }
   }

-- 
If you want to report a vulnerability issue on symfony, please send it to 
security at symfony-project.com

You received this message because you are subscribed to the Google
Groups "symfony users" group.
To post to this group, send email to symfony-users@googlegroups.com
To unsubscribe from this group, send email to
symfony-users+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/symfony-users?hl=en

Reply via email to