diff --git a/src/Bootstrap.php b/src/Bootstrap.php index 608ea8b..8a051db 100644 --- a/src/Bootstrap.php +++ b/src/Bootstrap.php @@ -3,7 +3,9 @@ declare(strict_types=1); namespace LotGD\Core; +use Doctrine\Common\Annotations\AnnotationRegistry; use Doctrine\Common\EventManager as DoctrineEventManager; +use Doctrine\Common\Util\Debug; use Doctrine\ORM\Events as DoctrineEvents; use Doctrine\ORM\ { EntityManager, @@ -30,7 +32,8 @@ class Bootstrap { private $logger; private $game; - private $libraryConfigurationManager = []; + /** @var LibraryConfigurationManager */ + private $libraryConfigurationManager; private $annotationDirectories = []; /** @@ -75,6 +78,9 @@ class Bootstrap $dem = $entityManager->getEventManager(); $dem->addEventListener([DoctrineEvents::postLoad], new EntityPostLoadEventListener($this->game)); + // Run model extender + $this->extendModels(); + return $this->game; } @@ -203,4 +209,19 @@ class Bootstrap } } } + + public function extendModels() + { + AnnotationRegistry::registerLoader("class_exists"); + + $modelExtender = new ModelExtender(); + + foreach ($this->libraryConfigurationManager->getConfigurations() as $config) { + $modelExtensions = $config->getSubKeyIfItExists(["modelExtensions"]); + + if ($modelExtensions) { + $modelExtender->addMore($modelExtensions); + } + } + } } diff --git a/src/Doctrine/Annotations/Extension.php b/src/Doctrine/Annotations/Extension.php new file mode 100644 index 0000000..6f66548 --- /dev/null +++ b/src/Doctrine/Annotations/Extension.php @@ -0,0 +1,51 @@ +modelClass = $attributes["of"]; + + if (!class_exists($this->modelClass)) { + throw new ArgumentException("The class given in of must be a valid class."); + } + + if (!in_array(ExtendableModelInterface::class, class_implements($this->modelClass))) { + throw new ArgumentException("The class given in of must implement the ExtendableModelInterface."); + } + } + + /** + * Returns the model class name. + * @return string + */ + public function getModelClass(): string + { + return $this->modelClass; + } +} \ No newline at end of file diff --git a/src/Doctrine/Annotations/ExtensionMethod.php b/src/Doctrine/Annotations/ExtensionMethod.php new file mode 100644 index 0000000..98267b1 --- /dev/null +++ b/src/Doctrine/Annotations/ExtensionMethod.php @@ -0,0 +1,50 @@ +methodName = $attributes["as"]; + + if (!is_string($this->methodName)) { + throw new ArgumentException("Property 'as' must be a string."); + } + + if (strlen($this->methodName) == 0) { + throw new ArgumentException("Property 'as' must not be an empty string."); + } + } + + /** + * Returns the method name. + * @return string + */ + public function getMethodName(): string + { + return $this->methodName; + } +} \ No newline at end of file diff --git a/src/GameAwareInterface.php b/src/GameAwareInterface.php index 901b3c9..ba7118a 100644 --- a/src/GameAwareInterface.php +++ b/src/GameAwareInterface.php @@ -10,4 +10,5 @@ namespace LotGD\Core; interface GameAwareInterface { public function setGame(Game $g); + public function getGame(): Game; } \ No newline at end of file diff --git a/src/LibraryConfiguration.php b/src/LibraryConfiguration.php index 5b3abac..7ff32ed 100644 --- a/src/LibraryConfiguration.php +++ b/src/LibraryConfiguration.php @@ -126,7 +126,7 @@ class LibraryConfiguration * @param array $arguments * @return type */ - protected function getSubKeyIfItExists(array $arguments) + public function getSubKeyIfItExists(array $arguments) { $parent = $this->rawConfig; @@ -145,7 +145,7 @@ class LibraryConfiguration * Tries to iterate an array element given by the arguments * @param scalar $argument1,... array keys, by increasing depth */ - protected function iterateKey(...$arguments) + public function iterateKey(...$arguments) { $result = $this->getSubKeyIfItExists($arguments); diff --git a/src/LibraryConfigurationManager.php b/src/LibraryConfigurationManager.php index 701fafc..2469f6a 100644 --- a/src/LibraryConfigurationManager.php +++ b/src/LibraryConfigurationManager.php @@ -52,7 +52,7 @@ class LibraryConfigurationManager /** * Return an array of the library configurations. - * @return array + * @return LibraryConfiguration[] */ public function getConfigurations(): array { return $this->configurations; diff --git a/src/ModelExtender.php b/src/ModelExtender.php new file mode 100644 index 0000000..35a1f34 --- /dev/null +++ b/src/ModelExtender.php @@ -0,0 +1,80 @@ +reader = new AnnotationReader(); + } + + /** + * @param string[] $classes + */ + public function addMore(array $classes): void { + foreach($classes as $class) { + $this->add($class); + } + } + + /** + * @param string $class + * @throws ArgumentException if the given class is not properly annotated. + */ + public function add(string $class): void + { + $reflectionClass = new ReflectionClass($class); + /** @var Extension $extensionAnnotation */ + $extensionAnnotation = $this->reader->getClassAnnotation($reflectionClass, Extension::class); + + if ($extensionAnnotation === null) { + throw new ArgumentException(sprintf("Class %s must have the class Annotation %s", $class, Extension::class)); + } + + $modelClass = $extensionAnnotation->getModelClass(); + + if (empty(self::$classes[$modelClass])) { + self::$classes[$modelClass] = []; + } + + // Run through static methods + $reflectionMethods = $reflectionClass->getMethods(); + + foreach ($reflectionMethods as $method) { + if ($method->isStatic() === false) { + throw new ArgumentException(sprintf("Method %s must be static.", $method->getName())); + } + + /** @var ExtensionMethod $extensionMethodAnnotation */ + $extensionMethodAnnotation = $this->reader->getMethodAnnotation($method, ExtensionMethod::class); + $methodName = $method->getName(); + + self::$classes[$modelClass][$extensionMethodAnnotation->getMethodName()] = [$class, $methodName]; + } + } + + public static function get(string $modelClassName, string $methodName): ?callable + { + if (empty(self::$classes[$modelClassName])) { + return null; + } + + if (empty(self::$classes[$modelClassName][$methodName])) { + return null; + } + + return self::$classes[$modelClassName][$methodName]; + } +} \ No newline at end of file diff --git a/src/Models/ExtendableModelInterface.php b/src/Models/ExtendableModelInterface.php new file mode 100644 index 0000000..2fea0b8 --- /dev/null +++ b/src/Models/ExtendableModelInterface.php @@ -0,0 +1,10 @@ +game = $game; } - private function getGame(): Game { + public function getGame(): Game { return $this->game; } } \ No newline at end of file diff --git a/tests/BootstrapTest.php b/tests/BootstrapTest.php index 6ec1274..d2017d8 100644 --- a/tests/BootstrapTest.php +++ b/tests/BootstrapTest.php @@ -150,5 +150,6 @@ class BootstrapTest extends \PHPUnit_Framework_TestCase $user = $game->getEntityManager()->getRepository(UserEntity::class)->find($id); $this->assertSame($game, $user->returnGame()); + $this->assertSame([$user->getName()], $user->getNameAsArray()); } } diff --git a/tests/FakeModule/Models/UserEntity.php b/tests/FakeModule/Models/UserEntity.php index 891fdf6..03f688a 100644 --- a/tests/FakeModule/Models/UserEntity.php +++ b/tests/FakeModule/Models/UserEntity.php @@ -7,15 +7,18 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Table; use LotGD\Core\Game; use LotGD\Core\GameAwareInterface; +use LotGD\Core\Models\ExtendableModelInterface; +use LotGD\Core\Tools\Model\ExtendableModel; use LotGD\Core\Tools\Model\GameAware; /** * @Entity * @Table(name="Users") */ -class UserEntity implements GameAwareInterface +class UserEntity implements GameAwareInterface, ExtendableModelInterface { use GameAware; + use ExtendableModel; /** @Id @Column(type="integer") @GeneratedValue */ private $id; diff --git a/tests/FakeModule/Models/UserTestExtension.php b/tests/FakeModule/Models/UserTestExtension.php new file mode 100644 index 0000000..d0aeabe --- /dev/null +++ b/tests/FakeModule/Models/UserTestExtension.php @@ -0,0 +1,32 @@ +getGame(); + + if ($g !== null) { + return [$user->getName()]; + } else { + return []; + } + } +} \ No newline at end of file diff --git a/tests/FakeModule/lotgd.yml b/tests/FakeModule/lotgd.yml index 3bcbc21..ff9fda5 100644 --- a/tests/FakeModule/lotgd.yml +++ b/tests/FakeModule/lotgd.yml @@ -1,3 +1,5 @@ entityNamespace: "LotGD\\Core\\Tests\\FakeModule\\Models\\" subscriptionPatterns: - "e/lotgd/core/tests/event" +modelExtensions: + - "LotGD\\Core\\Tests\\FakeModule\\Models\\UserTestExtension" \ No newline at end of file