Skip to content

Commit

Permalink
persistance: fixed & improved persisting of cyclic relationships (pos…
Browse files Browse the repository at this point in the history
…sible BC break)
  • Loading branch information
hrach committed Jan 23, 2016
1 parent 0e14e12 commit 0da8b64
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 3 deletions.
17 changes: 14 additions & 3 deletions src/Repository/PersistanceHelper.php
Expand Up @@ -12,6 +12,7 @@
use Nextras\Orm\Entity\IEntity;
use Nextras\Orm\Entity\Reflection\PropertyMetadata;
use Nextras\Orm\Entity\Reflection\PropertyRelationshipMetadata as Relationship;
use Nextras\Orm\InvalidStateException;
use Nextras\Orm\Model\IModel;


Expand Down Expand Up @@ -39,6 +40,7 @@ public static function getCascadeQueue(IEntity $entity, IModel $model, $withCasc
$queue[$entityHash] = $entity;
return;
}
$queue[$entityHash] = TRUE;

$keys = [[], []];
foreach ($entity->getMetadata()->getProperties() as $propertyMeta) {
Expand All @@ -52,16 +54,19 @@ public static function getCascadeQueue(IEntity $entity, IModel $model, $withCasc
}

foreach ($keys[0] as $propertyMeta) {
self::addRelationtionToQueue($entity, $propertyMeta, $model, $queue);
self::addRelationtionToQueue($entity, $propertyMeta, $model, TRUE, $queue);
}

unset($queue[$entityHash]); // reenqueue
$queue[$entityHash] = $entity;

foreach ($keys[1] as $propertyMeta) {
self::addRelationtionToQueue($entity, $propertyMeta, $model, $queue);
self::addRelationtionToQueue($entity, $propertyMeta, $model, FALSE, $queue);
}
}


protected static function addRelationtionToQueue(IEntity $entity, PropertyMetadata $propertyMeta, IModel $model, array & $queue)
protected static function addRelationtionToQueue(IEntity $entity, PropertyMetadata $propertyMeta, IModel $model, $checkCycles, array & $queue)
{
$isPersisted = $entity->isPersisted();
$rawValue = $entity->getRawProperty($propertyMeta->name);
Expand All @@ -75,6 +80,12 @@ protected static function addRelationtionToQueue(IEntity $entity, PropertyMetada
$value = $entity->getValue($propertyMeta->name);
if ($relType === Relationship::ONE_HAS_ONE || $relType === Relationship::MANY_HAS_ONE) {
if ($value !== NULL) {
if ($checkCycles && isset($queue[spl_object_hash($value)]) && $queue[spl_object_hash($value)] === TRUE && !$value->isPersisted()) {
$entityClass = get_class($entity);
throw new InvalidStateException(
"Persist cycle detected in $entityClass::\${$propertyMeta->name}. Use manual two phase persist."
);
}
self::getCascadeQueue($value, $model, TRUE, $queue);
}
} else {
Expand Down
74 changes: 74 additions & 0 deletions tests/cases/integration/Relationships/relationships.cyclic.phpt
@@ -0,0 +1,74 @@
<?php

/**
* @testCase
* @dataProvider ../../../sections.ini
*/

namespace NextrasTests\Orm\Integration\Relationships;

use Mockery;
use Nextras\Orm\InvalidStateException;
use NextrasTests\Orm\DataTestCase;
use NextrasTests\Orm\Photo;
use NextrasTests\Orm\PhotoAlbum;
use Tester\Assert;

$dic = require_once __DIR__ . '/../../../bootstrap.php';


class RelationshipCyclicTest extends DataTestCase
{
public function testCycleCheck()
{
$album = new PhotoAlbum();
$album->title = 'album 1';
$photo1 = new Photo();
$photo1->title = 'photo 1';
$photo1->album = $album;
$photo2 = new Photo();
$photo2->title = 'photo 2';
$photo2->album = $album;
$photo3 = new Photo();
$photo3->title = 'photo 3';
$photo3->album = $album;
$album->preview = $photo2;

Assert::throws(function () use ($album) {
$this->orm->persist($album);
}, InvalidStateException::class, 'Persist cycle detected in NextrasTests\Orm\Photo::$album. Use manual two phase persist.');

Assert::throws(function () use ($photo2) {
$this->orm->persist($photo2);
}, InvalidStateException::class, 'Persist cycle detected in NextrasTests\Orm\PhotoAlbum::$preview. Use manual two phase persist.');
}


public function testCycleManualPersist()
{
$album = new PhotoAlbum();
$album->title = 'album 1';
$photo1 = new Photo();
$photo1->title = 'photo 1';
$photo1->album = $album;
$photo2 = new Photo();
$photo2->title = 'photo 2';
$photo2->album = $album;
$photo3 = new Photo();
$photo3->title = 'photo 3';
$photo3->album = $album;

$this->orm->persist($album);
$album->preview = $photo2;
$this->orm->persist($album);

Assert::true($album->isPersisted());
Assert::true($photo1->isPersisted());
Assert::true($photo2->isPersisted());
Assert::true($photo3->isPersisted());
}
}


$test = new RelationshipCyclicTest($dic);
$test->run();

0 comments on commit 0da8b64

Please sign in to comment.