Introduces a SceneAttachment model for registering attachments.

This commit is contained in:
Vassyli
2021-01-27 19:53:58 +01:00
committed by Basilius Sauter
parent 47693f6127
commit 2a3a3e7bc2
5 changed files with 289 additions and 9 deletions
+91 -6
View File
@@ -7,17 +7,26 @@ use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\Id;
use Doctrine\ORM\Mapping\JoinTable;
use Doctrine\ORM\Mapping\ManyToMany;
use Doctrine\ORM\Mapping\OneToMany;
use Doctrine\ORM\Mapping\Table;
use Doctrine\ORM\Mapping\Column;
use JetBrains\PhpStorm\Deprecated;
use LotGD\Core\Exceptions\ArgumentException;
use LotGD\Core\Exceptions\AttributeMissingException;
use LotGD\Core\Exceptions\UnexpectedArrayKeyException;
use LotGD\Core\Exceptions\WrongTypeException;
use LotGD\Core\Tools\Model\Creator;
use LotGD\Core\Tools\Model\Deletor;
use LotGD\Core\Tools\Model\PropertyManager;
use LotGD\Core\Tools\Model\Saveable;
use LotGD\Core\Tools\Model\SceneBasics;
use Ramsey\Uuid\Uuid;
use function array_merge;
use function count;
/**
* A scene is a location within the game, such as the Village or the Tavern. Designed
* to be a kind of "template" for generating the specific location information for
@@ -27,7 +36,7 @@ use Ramsey\Uuid\Uuid;
*/
class Scene implements CreateableInterface, SceneConnectable
{
use Creator;
use Saveable;
use Deletor;
use SceneBasics;
use PropertyManager;
@@ -65,6 +74,20 @@ class Scene implements CreateableInterface, SceneConnectable
*/
private ?Collection $properties;
/**
* @ManyToMany(targetEntity="SceneAttachment", inversedBy="scenes", cascade={"persist"})
* @JoinTable(
* name="scenes_x_scene_attachments",
* joinColumns={
* @JoinColumn(name="scene_id", referencedColumnName="id")
* },
* inverseJoinColumns={
* @JoinColumn(name="attachment_id", referencedColumnName="class")
* }
* )
*/
private ?Collection $attachments;
// required for PropertyManager to now which class the properties belong to.
private string $propertyClass = SceneProperty::class;
@@ -80,15 +103,45 @@ class Scene implements CreateableInterface, SceneConnectable
private ?Collection $connectedScenes = null;
/**
* Constructor for a scene.
* Creates and returns an entity instance and fills values.
* @param array $arguments The values the instance should get
* @return CreateableInterface The created Entity
* @throws WrongTypeException|UnexpectedArrayKeyException
* @throws AttributeMissingException
*/
public function __construct()
#[Deprecated("Use constructor directly.")]
public static function create(array $arguments): CreateableInterface
{
if (isset(self::$fillable) === false) {
throw new AttributeMissingException('self::$fillable is not defined.');
}
if (\is_array(self::$fillable) === false) {
throw new WrongTypeException('self::$fillable needs to be an array.');
}
$entity = new self($arguments["title"], $arguments["description"], $arguments["template"]);
return $entity;
}
/**
* Constructor for a scene.
* @param string $title
* @param string $description
* @param SceneTemplate|null $template
*/
public function __construct(string $title, string $description, ?SceneTemplate $template = null)
{
$this->id = Uuid::uuid4()->toString();
$this->setTitle($title);
$this->setDescription($description);
$this->setTemplate($template);
$this->connectionGroups = new ArrayCollection();
$this->outgoingConnections = new ArrayCollection();
$this->incomingConnections = new ArrayCollection();
$this->attachments = new ArrayCollection();
}
public function __toString(): string
@@ -143,7 +196,7 @@ class Scene implements CreateableInterface, SceneConnectable
*/
public function hasConnectionGroup(string $name): bool
{
return \count($this->filterConnectionGroupCollectionByName($name)) === 1;
return count($this->filterConnectionGroupCollectionByName($name)) === 1;
}
/**
@@ -259,6 +312,11 @@ class Scene implements CreateableInterface, SceneConnectable
return false;
}
/**
* Returns a connection to another scene if it exists. Returns null if it does not exist.
* @param Scene $scene
* @return SceneConnection|null
*/
public function getConnectionTo(self $scene): ?SceneConnection
{
foreach ($this->outgoingConnections as $outgoingConnection) {
@@ -283,7 +341,7 @@ class Scene implements CreateableInterface, SceneConnectable
public function getConnections(): Collection
{
return new ArrayCollection(
\array_merge(
array_merge(
$this->outgoingConnections->toArray(),
$this->incomingConnections->toArray()
)
@@ -363,4 +421,31 @@ class Scene implements CreateableInterface, SceneConnectable
return $connection;
}
/**
* @return Collection
*/
public function getAttachments(): Collection
{
return $this->attachments;
}
/**
* @param SceneAttachment $sceneAttachment
* @return bool
*/
public function hasAttachment(SceneAttachment $sceneAttachment): bool
{
return $this->attachments->contains($sceneAttachment);
}
/**
* @param SceneAttachment $attachmentClass
*/
public function addAttachment(SceneAttachment $attachmentClass): void
{
if (!$this->hasAttachment($attachmentClass)) {
$this->attachments->add($attachmentClass);
}
}
}
+77
View File
@@ -0,0 +1,77 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Models;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\Id;
use Doctrine\ORM\Mapping\ManyToMany;
use Doctrine\ORM\Mapping\Table;
use LotGD\Core\Attachment;
use LotGD\Core\Exceptions\ArgumentException;
/**
* A SceneAttachment is a registered Attachment class to keep track of
* (a) generally all available attachments, and
* (b) which scene contains which attachment.
* @Entity
* @Table(name="scene_attachments")
*/
class SceneAttachment
{
/**
* @Id
* @Column(type="string", length=36, unique=True, name="class", options={"fixed"=true})
*/
protected string $class;
/** @Column(type="string", length=255) */
protected string $title;
/** @ManyToMany(targetEntity="Scene", mappedBy="attachments") */
private ?Collection $scenes;
/**
* SceneAttachment constructor.
* @param string $class A class inheriting from Attachment.
* @param string $title
* @throws ArgumentException if $class does not implement Attachment
*/
public function __construct(string $class, string $title) {
if (!is_subclass_of($class, Attachment::class)) {
throw new ArgumentException("The class '{$class}' must inherit from " . Attachment::class);
}
$this->class = $class;
$this->title = $title;
$this->scenes = new ArrayCollection();
}
/**
* @return string
*/
public function getClass(): string
{
return $this->class;
}
/**
* @return string
*/
public function getTitle(): string
{
return $this->title;
}
/**
* @return Collection
*/
public function getScenes(): Collection
{
return $this->scenes;
}
}
+2 -2
View File
@@ -18,9 +18,9 @@ trait Creator
/**
* Creates and returns an entity instance and fills values.
* @param array $arguments The values the instance should get
* @throws AttributeMissingException
* @throws WrongTypeException
* @return CreateableInterface The created Entity
* @throws WrongTypeException|UnexpectedArrayKeyException
* @throws AttributeMissingException
*/
public static function create(array $arguments): CreateableInterface
{
+1 -1
View File
@@ -17,7 +17,7 @@ trait SceneBasics
private string $title = "{No scene set}";
/** @Column(type="text") */
private string $description = "{No scene set}";
/** @Column(type="string", length=255) */
/**
* @ManyToOne(targetEntity="SceneTemplate", fetch="EAGER")
* @JoinColumn(name="template", referencedColumnName="class", nullable=true)
+118
View File
@@ -0,0 +1,118 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Tests\Models;
use LotGD\Core\Attachment;
use LotGD\Core\Exceptions\ArgumentException;
use LotGD\Core\Models\{Scene, SceneAttachment, SceneConnection, SceneConnectionGroup, SceneTemplate};
use LotGD\Core\Tests\CoreModelTestCase;
use LotGD\Core\Tests\SceneTemplates\NewSceneSceneTemplate;
class TestAttachment extends Attachment
{
}
class InvalidTestAttachment
{
}
/**
* Tests for creating scenes and moving them around.
*/
class SceneAttachmentTest extends CoreModelTestCase
{
/** @var string default data set */
protected $dataset = "scene";
public function testSceneAttachmentCreationWithValidParameters()
{
$sceneAttachment = new SceneAttachment(TestAttachment::class, "Test Attachment");
$this->assertInstanceOf(SceneAttachment::class, $sceneAttachment);
}
public function testIfSceneAttachmentCreationFailsIfAttachmentIsNotSubclassOfAttachment()
{
$this->expectException(ArgumentException::class);
$sceneAttachment = new SceneAttachment(InvalidTestAttachment::class, "Invalid Test Attachment");
}
public function testIfValidSceneAttachmentCanBePersistedAndGottenBackFromTheDatabase()
{
$em = $this->getEntityManager();
$sceneAttachment = new SceneAttachment(TestAttachment::class, "Test Attachment");
// persist
$em->persist($sceneAttachment);
$em->flush();
$em->clear();
// retrieve
$retrievedSceneAttachment = $em->getRepository(SceneAttachment::class)->find(TestAttachment::class);
$this->assertInstanceOf(SceneAttachment::class, $retrievedSceneAttachment);
// Delete again
$em->remove($retrievedSceneAttachment);
$em->flush();
}
public function testIfSceneGettersReturnGivenValuesProperly()
{
$sceneAttachment = new SceneAttachment(TestAttachment::class, "Test Attachment");
$this->assertSame($sceneAttachment->getClass(), TestAttachment::class);
$this->assertSame($sceneAttachment->getTitle(), "Test Attachment");
}
public function testIfPersistingASceneAlsoPersistsASceneAttachment()
{
$em = $this->getEntityManager();
$scene = new Scene("Test scene", "A test scene");
$sceneAttachment = new SceneAttachment(TestAttachment::class, "Test Attachment");
$scene->addAttachment($sceneAttachment);
$sceneId = $scene->getId();
// persist
$em->persist($scene);
$em->flush();
$em->clear();
// retrieve
$retrievedSceneAttachment = $em->getRepository(SceneAttachment::class)->find(TestAttachment::class);
// assert
$this->assertInstanceOf(SceneAttachment::class, $retrievedSceneAttachment);
$this->assertCount(1, $retrievedSceneAttachment->getScenes());
// remove scene
$scene = $retrievedSceneAttachment->getScenes()[0];
$em->remove($scene);
$em->flush();
$em->clear();
// retrieve
$retrievedSceneAttachment = $em->getRepository(SceneAttachment::class)->find(TestAttachment::class);
// assert
$this->assertInstanceOf(SceneAttachment::class, $retrievedSceneAttachment);
$this->assertCount(0, $retrievedSceneAttachment->getScenes());
// remove attachment
$em->remove($retrievedSceneAttachment);
$em->flush();
$em->clear();
// retrieve
$retrievedSceneAttachment = $em->getRepository(SceneAttachment::class)->find(TestAttachment::class);
// assert
$this->assertNull($retrievedSceneAttachment);
}
}