Build support for multiple parents (graph of scenes instead of tree)
This commit is contained in:
@@ -1,12 +0,0 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace LotGD\Core\Exceptions;
|
||||
|
||||
/**
|
||||
* Exception if a specific, required argument is missing
|
||||
*/
|
||||
class NoParentSetException extends CoreException
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace LotGD\Core\Exceptions;
|
||||
|
||||
/**
|
||||
* Exception if a specific, required argument is missing
|
||||
*/
|
||||
class WrongParentException extends CoreException
|
||||
{
|
||||
|
||||
}
|
||||
+50
-46
@@ -9,8 +9,6 @@ use Doctrine\ORM\Mapping\Entity;
|
||||
use Doctrine\ORM\Mapping\Table;
|
||||
use Doctrine\ORM\Mapping\Column;
|
||||
|
||||
use LotGD\Core\Exceptions\NoParentSetException;
|
||||
use LotGD\Core\Exceptions\WrongParentException;
|
||||
use LotGD\Core\Tools\Model\Creator;
|
||||
use LotGD\Core\Tools\Model\Deletor;
|
||||
use LotGD\Core\Tools\Model\SceneBasics;
|
||||
@@ -30,13 +28,16 @@ class Scene implements CreateableInterface
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ManyToOne(targetEntity="Scene", cascade={"persist"})
|
||||
* @JoinColumn(name="parent", referencedColumnName="id", nullable=true)
|
||||
* @ManyToMany(targetEntity="Scene", mappedBy="children", cascade={"persist"})
|
||||
*/
|
||||
private $parent = null;
|
||||
private $parents = null;
|
||||
|
||||
/**
|
||||
* @OneToMany(targetEntity="Scene", mappedBy="parent", cascade={"persist", "remove"})
|
||||
* @ManyToMany(targetEntity="Scene", inversedBy="parents", cascade={"persist", "remove"})
|
||||
* @JoinTable(name="paths",
|
||||
* joinColumns={@JoinColumn(name="scene_id", referencedColumnName="id")},
|
||||
* inverseJoinColumns={@JoinColumn(name="child_scene_id", referencedColumnName="id")}
|
||||
* )
|
||||
*/
|
||||
private $children = [];
|
||||
|
||||
@@ -46,7 +47,7 @@ class Scene implements CreateableInterface
|
||||
private static $fillable = [
|
||||
"title",
|
||||
"description",
|
||||
"parent",
|
||||
"parents",
|
||||
"template"
|
||||
];
|
||||
|
||||
@@ -56,6 +57,7 @@ class Scene implements CreateableInterface
|
||||
public function __construct()
|
||||
{
|
||||
$this->children = new ArrayCollection();
|
||||
$this->parents = new ArrayCollection();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,46 +70,60 @@ class Scene implements CreateableInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets or removes the parent of this scene.
|
||||
* @param \LotGD\Core\Models\Scene $parent The new parent or NULL
|
||||
* Set the parents to the given Collection.
|
||||
* @param Collection $parents
|
||||
*/
|
||||
public function setParent(Scene $parent = null)
|
||||
public function setParents(Collection $parents)
|
||||
{
|
||||
// Get old parent and remove $this from it
|
||||
if ($this->parent !== null) {
|
||||
$oldParent = $this->parent;
|
||||
$oldParent->removeChild($this);
|
||||
$this->parent = null;
|
||||
// Super slow, but presumably these are short collections :)
|
||||
// We should probably move to a set collection at some point.
|
||||
$oldParents = $this->parents;
|
||||
$additions = $parents->filter(function($element) use ($oldParents) {
|
||||
return !$oldParents->contains($element);
|
||||
});
|
||||
$removals = $this->parents->filter(function($element) use ($parents) {
|
||||
return !$parents->contains($element);
|
||||
});
|
||||
|
||||
foreach ($additions as $a) {
|
||||
$this->addParent($a);
|
||||
}
|
||||
foreach ($removals as $r) {
|
||||
$this->removeParent($r);
|
||||
}
|
||||
|
||||
// New parent is not null
|
||||
if ($parent !== null) {
|
||||
$this->parent = $parent;
|
||||
$this->parents = $parents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a parent to this scene.
|
||||
* @param \LotGD\Core\Models\Scene $parent
|
||||
*/
|
||||
public function addParent(Scene $parent)
|
||||
{
|
||||
if (!$this->parents->contains($parent)) {
|
||||
$this->parents->add($parent);
|
||||
$parent->addChild($this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent of this scene
|
||||
* @return \LotGD\Core\Models\Scene
|
||||
* @throws \LotGD\Core\Exceptions\NoParentSetException
|
||||
* Removes a parent from this scene.
|
||||
* @param Scene $parent
|
||||
*/
|
||||
public function getParent(): Scene
|
||||
public function removeParent(Scene $parent)
|
||||
{
|
||||
if ($this->parent === null) {
|
||||
throw new NoParentSetException("This child does not have a parent set to return. Check with hasParent first.");
|
||||
}
|
||||
|
||||
return $this->parent;
|
||||
$this->parents->removeElement($parent);
|
||||
$parent->removeChild($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this entity has a parent
|
||||
* @return bool
|
||||
* Returns all the possible parents of this scene.
|
||||
* @return Collection
|
||||
*/
|
||||
public function hasParent(): bool
|
||||
public function getParents(): Collection
|
||||
{
|
||||
return !(empty($this->parent));
|
||||
return $this->parents;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,35 +135,23 @@ class Scene implements CreateableInterface
|
||||
return $this->children;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the number of children registered for this entitiy is > 0
|
||||
* @return bool
|
||||
*/
|
||||
public function hasChildren(): bool
|
||||
{
|
||||
return count($this->children) > 0 ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a child for this entity.
|
||||
* @param \LotGD\Core\Models\Scene $child
|
||||
*/
|
||||
protected function addChild(Scene $child)
|
||||
{
|
||||
$this->children->add($child);
|
||||
if (!$this->children->contains($child)) {
|
||||
$this->children->add($child);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a child from this entity.
|
||||
* @param \LotGD\Core\Models\Scene $child
|
||||
* @throws WrongParentException
|
||||
*/
|
||||
protected function removeChild(Scene $child)
|
||||
{
|
||||
if ($child->getParent() !== $this) {
|
||||
throw new WrongParentException("This Scene is not the parent of the given child.");
|
||||
}
|
||||
|
||||
$this->children->removeElement($child);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace LotGD\Core\Tests\Models;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
|
||||
use LotGD\Core\Models\Scene;
|
||||
use LotGD\Core\Tests\CoreModelTestCase;
|
||||
|
||||
@@ -29,9 +31,9 @@ class SceneModelTest extends CoreModelTestCase
|
||||
|
||||
$this->assertEquals("The Forest", $scene->getTitle());
|
||||
$this->assertEquals("This is a very dangerous and dark forest", $scene->getDescription());
|
||||
$this->assertInstanceOf(Scene::class, $scene->getParent());
|
||||
$this->assertEquals(true, $scene->hasParent());
|
||||
$this->assertEquals(false, $scene->hasChildren());
|
||||
$this->assertInstanceOf(Scene::class, $scene->getParents()[0]);
|
||||
$this->assertCount(1, $scene->getParents());
|
||||
$this->assertCount(0, $scene->getChildren());
|
||||
|
||||
$em->flush();
|
||||
}
|
||||
@@ -46,7 +48,7 @@ class SceneModelTest extends CoreModelTestCase
|
||||
$parentScene = $em->getRepository(Scene::class)->find(1);
|
||||
$childScene = $em->getRepository(Scene::class)->find(2);
|
||||
|
||||
$this->assertEquals($parentScene, $childScene->getParent());
|
||||
$this->assertContains($parentScene, $childScene->getParents());
|
||||
$this->assertContains($childScene, $parentScene->getChildren());
|
||||
|
||||
$em->flush();
|
||||
@@ -63,31 +65,31 @@ class SceneModelTest extends CoreModelTestCase
|
||||
$parentScene2 = $em->getRepository(Scene::class)->find(4);
|
||||
|
||||
$orphanScene = $em->getRepository(scene::class)->find(5);
|
||||
$this->assertEquals(false, $orphanScene->hasParent());
|
||||
$this->assertEquals(false, $orphanScene->hasChildren());
|
||||
$this->assertCount(0, $orphanScene->getParents());
|
||||
$this->assertCount(0, $orphanScene->getChildren());
|
||||
|
||||
// Assign orphanScene to parentScene1 and check relationships
|
||||
$orphanScene->setParent($parentScene1);
|
||||
$orphanScene->addParent($parentScene1);
|
||||
|
||||
$this->assertEquals(true, $orphanScene->hasParent());
|
||||
$this->assertCount(1, $orphanScene->getParents());
|
||||
$this->assertCount(3, $parentScene1->getChildren());
|
||||
$this->assertEquals($parentScene1, $orphanScene->getParent());
|
||||
$this->assertContains($parentScene1, $orphanScene->getParents());
|
||||
$this->assertContains($orphanScene, $parentScene1->getChildren());
|
||||
|
||||
// Move the scene now to parentScene2 and check relationships
|
||||
$orphanScene->setParent($parentScene2);
|
||||
// Add the scene now to parentScene2 and check relationships
|
||||
$orphanScene->addParent($parentScene2);
|
||||
|
||||
$this->assertCount(2, $parentScene1->getChildren());
|
||||
$this->assertCount(3, $parentScene1->getChildren());
|
||||
$this->assertCount(1, $parentScene2->getChildren());
|
||||
$this->assertEquals($parentScene2, $orphanScene->getParent());
|
||||
$this->assertContains($parentScene2, $orphanScene->getParents());
|
||||
$this->assertContains($orphanScene, $parentScene2->getChildren());
|
||||
|
||||
// Make an orphan out of it again
|
||||
$orphanScene->setParent(null);
|
||||
$orphanScene->setParents(new ArrayCollection());
|
||||
|
||||
$this->assertCount(2, $parentScene1->getChildren());
|
||||
$this->assertCount(0, $parentScene2->getChildren());
|
||||
$this->assertEquals(false, $orphanScene->hasParent());
|
||||
$this->assertCount(0, $orphanScene->getParents());
|
||||
$this->assertNotContains($orphanScene, $parentScene1->getChildren());
|
||||
$this->assertNotContains($orphanScene, $parentScene2->getChildren());
|
||||
|
||||
|
||||
@@ -22,16 +22,20 @@ scenes:
|
||||
title: "The Village"
|
||||
description: "This is the village."
|
||||
template: "lotgd/tests/village"
|
||||
parent:
|
||||
-
|
||||
id: 2
|
||||
title: "The Forest"
|
||||
description: "This is a very dangerous and dark forest"
|
||||
template: "lotgd/tests/forest"
|
||||
parent: 1
|
||||
-
|
||||
id: 3
|
||||
title: "The Weaponry"
|
||||
description: "This is the place where you can buy awesome weapons"
|
||||
template: "lotgd/tests/weaponry"
|
||||
parent: 1
|
||||
paths:
|
||||
-
|
||||
scene_id: 1
|
||||
child_scene_id: 2
|
||||
-
|
||||
scene_id: 1
|
||||
child_scene_id: 3
|
||||
|
||||
@@ -26,16 +26,20 @@ scenes:
|
||||
title: "The Village"
|
||||
description: "This is the village."
|
||||
template: "lotgd/tests/village"
|
||||
parent:
|
||||
-
|
||||
id: 2
|
||||
title: "The Forest"
|
||||
description: "This is a very dangerous and dark forest"
|
||||
template: "lotgd/tests/forest"
|
||||
parent: 1
|
||||
-
|
||||
id: 3
|
||||
title: "The Weaponry"
|
||||
description: "This is the place where you can buy awesome weapons"
|
||||
template: "lotgd/tests/weaponry"
|
||||
parent: 1
|
||||
paths:
|
||||
-
|
||||
scene_id: 1
|
||||
child_scene_id: 2
|
||||
-
|
||||
scene_id: 1
|
||||
child_scene_id: 3
|
||||
|
||||
@@ -4,28 +4,30 @@ scenes:
|
||||
title: "The Village"
|
||||
description: "This is the village."
|
||||
template: "lotgd/tests/village"
|
||||
parent:
|
||||
-
|
||||
id: 2
|
||||
title: "The Forest"
|
||||
description: "This is a very dangerous and dark forest"
|
||||
template: "lotgd/tests/forest"
|
||||
parent: 1
|
||||
-
|
||||
id: 3
|
||||
title: "The Weaponry"
|
||||
description: "This is the place where you can buy awesome weapons"
|
||||
template: "lotgd/tests/weaponry"
|
||||
parent: 1
|
||||
-
|
||||
id: 4
|
||||
title: "Another Village"
|
||||
description: "This is another village"
|
||||
template: "lotgd/tests/village"
|
||||
parent:
|
||||
-
|
||||
id: 5
|
||||
title: "Orphan"
|
||||
description: "This is an orphan scene"
|
||||
template: "lotgd/tests/orphan"
|
||||
parent:
|
||||
paths:
|
||||
-
|
||||
scene_id: 1
|
||||
child_scene_id: 2
|
||||
-
|
||||
scene_id: 1
|
||||
child_scene_id: 3
|
||||
|
||||
Reference in New Issue
Block a user