From 03304650b2c0e85e246c0e754bbf5cf0ad9f9c71 Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Wed, 9 Jan 2019 22:45:26 +0100
Subject: [PATCH 01/42] Leave references and collections properties as is when
calling hydrate.
---
src/LightQL/Entities/Entity.php | 2 --
1 file changed, 2 deletions(-)
diff --git a/src/LightQL/Entities/Entity.php b/src/LightQL/Entities/Entity.php
index 94d6ebf..0e2bef4 100644
--- a/src/LightQL/Entities/Entity.php
+++ b/src/LightQL/Entities/Entity.php
@@ -154,8 +154,6 @@ public function hydrate(array $data)
} elseif (\is_null($this->{$property}) || $this->{$property} === null) {
$this->{$property} = $column->getDefault();
}
- } else {
- $this->{$property} = null;
}
}
}
From d1584032d8bd37820989b77081d01330208abfea Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Wed, 9 Jan 2019 23:03:40 +0100
Subject: [PATCH 02/42] Add a value validator
This is used to allow the validation of an enity value, before send it
to the database. This useful to avoid PHP objects to be sent from
entities to database, and to control if a value is allowed to be sent to
the database.
---
.../Annotations/ValidatorAnnotation.php | 105 ++++++++++++++++++
src/LightQL/Entities/EntityManager.php | 60 +++++++++-
src/LightQL/Entities/IValueValidator.php | 57 ++++++++++
.../Exceptions/ValueValidatorException.php | 54 +++++++++
src/LightQL/LightQL.php | 4 +
5 files changed, 274 insertions(+), 6 deletions(-)
create mode 100644 src/LightQL/Annotations/ValidatorAnnotation.php
create mode 100644 src/LightQL/Entities/IValueValidator.php
create mode 100644 src/LightQL/Exceptions/ValueValidatorException.php
diff --git a/src/LightQL/Annotations/ValidatorAnnotation.php b/src/LightQL/Annotations/ValidatorAnnotation.php
new file mode 100644
index 0000000..cfea748
--- /dev/null
+++ b/src/LightQL/Annotations/ValidatorAnnotation.php
@@ -0,0 +1,105 @@
+
+ * @copyright 2018 Aliens Group, Inc.
+ * @license MIT
+ * @version 1.0.0
+ * @link http://lightql.na2axl.tk
+ */
+
+namespace ElementaryFramework\LightQL\Annotations;
+
+use ElementaryFramework\Annotations\Annotation;
+use ElementaryFramework\Annotations\AnnotationFile;
+use ElementaryFramework\Annotations\IAnnotationFileAware;
+use ElementaryFramework\Annotations\Exceptions\AnnotationException;
+
+/**
+ * Validator Annotation
+ *
+ * Used to define the value validator of an entity.
+ *
+ * This annotation have to be associated with the @entity
+ * annotation to take effect.
+ *
+ * @usage('class' => true, 'inherited' => true)
+ *
+ * @category Annotations
+ * @package LightQL
+ * @author Nana Axel
+ * @link http://lightql.na2axl.tk/docs/api/LightQL/Annotations/ValidatorAnnotation
+ */
+class ValidatorAnnotation extends Annotation implements IAnnotationFileAware
+{
+ /**
+ * Specify the class name to use as the ID generator
+ * of the current entity.
+ *
+ * @var string
+ */
+ public $validator;
+
+ /**
+ * Annotation file.
+ *
+ * @var AnnotationFile
+ */
+ protected $file;
+
+ /**
+ * Initialize the annotation.
+ *
+ * @param array $properties The array of annotation properties
+ *
+ * @throws AnnotationException
+ *
+ * @return void
+ */
+ public function initAnnotation(array $properties)
+ {
+ $this->map($properties, array("validator"));
+
+ parent::initAnnotation($properties);
+
+ if (!isset($this->validator)) {
+ throw new AnnotationException(self::class . " must have a \"validator\" property");
+ }
+
+ $this->validator = $this->file->resolveType($this->validator);
+ }
+
+ /**
+ * Provides information about file, that contains this annotation.
+ *
+ * @param AnnotationFile $file Annotation file.
+ *
+ * @return void
+ */
+ public function setAnnotationFile(AnnotationFile $file)
+ {
+ $this->file = $file;
+ }
+}
diff --git a/src/LightQL/Entities/EntityManager.php b/src/LightQL/Entities/EntityManager.php
index 3acd650..c1788aa 100644
--- a/src/LightQL/Entities/EntityManager.php
+++ b/src/LightQL/Entities/EntityManager.php
@@ -34,6 +34,7 @@
use ElementaryFramework\Annotations\Annotations;
use ElementaryFramework\LightQL\Exceptions\EntityException;
+use ElementaryFramework\LightQL\Exceptions\ValueValidatorException;
use ElementaryFramework\LightQL\LightQL;
use ElementaryFramework\LightQL\Persistence\PersistenceUnit;
@@ -42,7 +43,6 @@
*
* Manage all entities, using one same persistence unit.
*
- * @final
* @category Entities
* @package LightQL
* @author Nana Axel
@@ -144,7 +144,7 @@ public function find(string $entityClass, $id): array
/**
* Persists an entity into the database.
*
- * @param Entity $entity The entity to create.
+ * @param Entity &$entity The entity to create.
*
* @throws \ElementaryFramework\Annotations\Exceptions\AnnotationException
* @throws \ElementaryFramework\LightQL\Exceptions\EntityException
@@ -158,6 +158,19 @@ public function persist(Entity &$entity)
$autoIncrementProperty = null;
$idProperty = null;
+ $valueValidator = null;
+
+ if (Annotations::classHasAnnotation($entity, "@validator")) {
+ $validatorAnnotation = Annotations::ofClass($entity, "@validator");
+
+ if (\is_subclass_of($validatorAnnotation[0]->validator, IValueValidator::class)) {
+ $validatorClass = new \ReflectionClass($validatorAnnotation[0]->validator);
+
+ $valueValidator = $validatorClass->newInstance();
+ } else {
+ throw new EntityException("The value validator of this entity doesn't implement the IValueValidator interface.");
+ }
+ }
/** @var Column $column */
foreach ($columns as $property => $column) {
@@ -203,8 +216,19 @@ public function persist(Entity &$entity)
}
}
+ /** @var Column $column */
foreach ($columns as $property => $column) {
- $fieldAndValues[$column->getName()] = $this->_lightql->quote($entity->get($column->getName()));
+ $value = $this->_lightql->quote($entity->get($column->getName()));
+
+ if ($valueValidator !== null) {
+ if ($valueValidator->validate($entityAnnotation[0]->table, $column->getName(), $value)) {
+ $fieldAndValues[$column->getName()] = $value;
+ } else {
+ throw new ValueValidatorException($property);
+ }
+ } else {
+ $fieldAndValues[$column->getName()] = $value;
+ }
}
$this->_lightql->beginTransaction();
@@ -228,7 +252,7 @@ public function persist(Entity &$entity)
/**
* Merges the entity in the database with the given one.
*
- * @param Entity $entity The entity to edit.
+ * @param Entity &$entity The entity to edit.
*
* @throws \ElementaryFramework\Annotations\Exceptions\AnnotationException
* @throws \ElementaryFramework\LightQL\Exceptions\EntityException
@@ -239,12 +263,25 @@ public function merge(Entity &$entity)
$columns = $entity->getColumns();
$fieldAndValues = array();
+ $valueValidator = null;
$where = array();
$entityReflection = new \ReflectionClass($entity);
$entityProperties = $entityReflection->getProperties();
+ if (Annotations::classHasAnnotation($entity, "@validator")) {
+ $validatorAnnotation = Annotations::ofClass($entity, "@validator");
+
+ if (\is_subclass_of($validatorAnnotation[0]->validator, IValueValidator::class)) {
+ $validatorClass = new \ReflectionClass($validatorAnnotation[0]->validator);
+
+ $valueValidator = $validatorClass->newInstance();
+ } else {
+ throw new EntityException("The value validator of this entity doesn't implement the IValueValidator interface.");
+ }
+ }
+
/** @var \ReflectionProperty $property */
foreach ($entityProperties as $property) {
$id = $entity->{$property->getName()};
@@ -261,8 +298,19 @@ public function merge(Entity &$entity)
}
}
+ /** @var Column $column */
foreach ($columns as $property => $column) {
- $fieldAndValues[$column->getName()] = $this->_lightql->quote($entity->get($column->getName()));
+ $value = $this->_lightql->quote($entity->get($column->getName()));
+
+ if ($valueValidator !== null) {
+ if ($valueValidator->validate($entityAnnotation[0]->table, $column->getName(), $value)) {
+ $fieldAndValues[$column->getName()] = $value;
+ } else {
+ throw new ValueValidatorException($property);
+ }
+ } else {
+ $fieldAndValues[$column->getName()] = $value;
+ }
if ($column->isPrimaryKey) {
$where[$column->getName()] = $this->_lightql->quote($entity->get($column->getName()));
@@ -287,7 +335,7 @@ public function merge(Entity &$entity)
/**
* Removes an entity from the database.
*
- * @param Entity $entity The entity to delete.
+ * @param Entity &$entity The entity to delete.
*
* @throws \ElementaryFramework\Annotations\Exceptions\AnnotationException
* @throws \ElementaryFramework\LightQL\Exceptions\EntityException
diff --git a/src/LightQL/Entities/IValueValidator.php b/src/LightQL/Entities/IValueValidator.php
new file mode 100644
index 0000000..afad44b
--- /dev/null
+++ b/src/LightQL/Entities/IValueValidator.php
@@ -0,0 +1,57 @@
+
+ * @copyright 2018 Aliens Group, Inc.
+ * @license MIT
+ * @version 1.0.0
+ * @link http://lightql.na2axl.tk
+ */
+
+namespace ElementaryFramework\LightQL\Entities;
+
+/**
+ * IEntityIdGenerator
+ *
+ * Defines a class as a primary key generator of a column.
+ *
+ * @category Entities
+ * @package LightQL
+ * @author Nana Axel
+ * @link http://lightql.na2axl.tk/docs/api/LightQL/Entities/IValueValidator
+ */
+interface IValueValidator
+{
+ /**
+ * Validates a value before send it to the database.
+ *
+ * @param string $table The name of the table in which the value will be sent.
+ * @param string $column The name of the column which will receive the value.
+ * @param mixed $value The value to validate.
+ *
+ * @return bool true if the value passes the validator, false otherwise.
+ */
+ function validate(string $table, string $column, $value);
+}
diff --git a/src/LightQL/Exceptions/ValueValidatorException.php b/src/LightQL/Exceptions/ValueValidatorException.php
new file mode 100644
index 0000000..146d586
--- /dev/null
+++ b/src/LightQL/Exceptions/ValueValidatorException.php
@@ -0,0 +1,54 @@
+
+ * @copyright 2018 Aliens Group, Inc.
+ * @license MIT
+ * @version 1.0.0
+ * @link http://lightql.na2axl.tk
+ */
+
+namespace ElementaryFramework\LightQL\Exceptions;
+
+/**
+ * Entity Exception
+ *
+ * @category Exceptions
+ * @package LightQL
+ * @author Nana Axel
+ * @link http://lightql.na2axl.tk/docs/api/LightQL/Exceptions/ValueValidatorException
+ */
+class ValueValidatorException extends \Exception
+{
+ /**
+ * Thrown when a value can't pass the validator of an entity.
+ *
+ * @param string $property The name of the property.
+ */
+ public function __construct(string $property)
+ {
+ parent::__construct("The value of the property \"{$property}\" have not pass the validator.");
+ }
+}
diff --git a/src/LightQL/LightQL.php b/src/LightQL/LightQL.php
index 5f8b9fd..b61a3fe 100644
--- a/src/LightQL/LightQL.php
+++ b/src/LightQL/LightQL.php
@@ -33,6 +33,7 @@
namespace ElementaryFramework\LightQL;
use ElementaryFramework\Annotations\Annotations;
+
use ElementaryFramework\LightQL\Annotations\AutoIncrementAnnotation;
use ElementaryFramework\LightQL\Annotations\ColumnAnnotation;
use ElementaryFramework\LightQL\Annotations\EntityAnnotation;
@@ -47,6 +48,8 @@
use ElementaryFramework\LightQL\Annotations\PersistenceUnitAnnotation;
use ElementaryFramework\LightQL\Annotations\SizeAnnotation;
use ElementaryFramework\LightQL\Annotations\UniqueAnnotation;
+use ElementaryFramework\LightQL\Annotations\ValidatorAnnotation;
+
use ElementaryFramework\LightQL\Exceptions\LightQLException;
/**
@@ -87,6 +90,7 @@ public static function registerAnnotations()
$manager->registerAnnotation("persistenceUnit", PersistenceUnitAnnotation::class);
$manager->registerAnnotation("size", SizeAnnotation::class);
$manager->registerAnnotation("unique", UniqueAnnotation::class);
+ $manager->registerAnnotation("validator", ValidatorAnnotation::class);
}
/**
From 85ccabb7e3a3b5fdce43bac837ce7f0a34c63aae Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Thu, 10 Jan 2019 18:05:41 +0100
Subject: [PATCH 03/42] Force the return type to bool
---
src/LightQL/Entities/IValueValidator.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/LightQL/Entities/IValueValidator.php b/src/LightQL/Entities/IValueValidator.php
index afad44b..e176d5b 100644
--- a/src/LightQL/Entities/IValueValidator.php
+++ b/src/LightQL/Entities/IValueValidator.php
@@ -53,5 +53,5 @@ interface IValueValidator
*
* @return bool true if the value passes the validator, false otherwise.
*/
- function validate(string $table, string $column, $value);
+ function validate(string $table, string $column, $value): bool;
}
From 02041b29a44a83d14afe47b79f0d3dfec4685554 Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Thu, 10 Jan 2019 18:26:35 +0100
Subject: [PATCH 04/42] Change the way the validator performs validations
This is to allow the use of annotations in the validation process.
---
src/LightQL/Entities/EntityManager.php | 4 ++--
src/LightQL/Entities/IValueValidator.php | 11 +++++------
2 files changed, 7 insertions(+), 8 deletions(-)
diff --git a/src/LightQL/Entities/EntityManager.php b/src/LightQL/Entities/EntityManager.php
index c1788aa..61b7cb8 100644
--- a/src/LightQL/Entities/EntityManager.php
+++ b/src/LightQL/Entities/EntityManager.php
@@ -221,7 +221,7 @@ public function persist(Entity &$entity)
$value = $this->_lightql->quote($entity->get($column->getName()));
if ($valueValidator !== null) {
- if ($valueValidator->validate($entityAnnotation[0]->table, $column->getName(), $value)) {
+ if ($valueValidator->validate($entity, $property)) {
$fieldAndValues[$column->getName()] = $value;
} else {
throw new ValueValidatorException($property);
@@ -303,7 +303,7 @@ public function merge(Entity &$entity)
$value = $this->_lightql->quote($entity->get($column->getName()));
if ($valueValidator !== null) {
- if ($valueValidator->validate($entityAnnotation[0]->table, $column->getName(), $value)) {
+ if ($valueValidator->validate($entity, $property)) {
$fieldAndValues[$column->getName()] = $value;
} else {
throw new ValueValidatorException($property);
diff --git a/src/LightQL/Entities/IValueValidator.php b/src/LightQL/Entities/IValueValidator.php
index e176d5b..079413e 100644
--- a/src/LightQL/Entities/IValueValidator.php
+++ b/src/LightQL/Entities/IValueValidator.php
@@ -33,9 +33,9 @@
namespace ElementaryFramework\LightQL\Entities;
/**
- * IEntityIdGenerator
+ * IValueValidator
*
- * Defines a class as a primary key generator of a column.
+ * Defines a class as the value validator of an entity.
*
* @category Entities
* @package LightQL
@@ -47,11 +47,10 @@ interface IValueValidator
/**
* Validates a value before send it to the database.
*
- * @param string $table The name of the table in which the value will be sent.
- * @param string $column The name of the column which will receive the value.
- * @param mixed $value The value to validate.
+ * @param Entity $entity The entity which will be sent to the database.
+ * @param string $property The name of the property to validate.
*
* @return bool true if the value passes the validator, false otherwise.
*/
- function validate(string $table, string $column, $value): bool;
+ function validate(Entity $entity, string $property): bool;
}
From 8c80e85118222927684ce0b811c52d28068b8004 Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Thu, 10 Jan 2019 19:27:33 +0100
Subject: [PATCH 05/42] Add support for value transformers
Used to convert value from database to entity, and from entity to
database. Useful when you want to serialize a value before storing it
into the database, and unserialize it back when you are retireving it
from the database.
---
.../Annotations/TransformerAnnotation.php | 105 ++++++++++++++++++
src/LightQL/Entities/EntityManager.php | 62 ++++++++---
src/LightQL/Entities/IValueTransformer.php | 67 +++++++++++
src/LightQL/Sessions/Facade.php | 25 ++++-
4 files changed, 242 insertions(+), 17 deletions(-)
create mode 100644 src/LightQL/Annotations/TransformerAnnotation.php
create mode 100644 src/LightQL/Entities/IValueTransformer.php
diff --git a/src/LightQL/Annotations/TransformerAnnotation.php b/src/LightQL/Annotations/TransformerAnnotation.php
new file mode 100644
index 0000000..92fea5d
--- /dev/null
+++ b/src/LightQL/Annotations/TransformerAnnotation.php
@@ -0,0 +1,105 @@
+
+ * @copyright 2018 Aliens Group, Inc.
+ * @license MIT
+ * @version 1.0.0
+ * @link http://lightql.na2axl.tk
+ */
+
+namespace ElementaryFramework\LightQL\Annotations;
+
+use ElementaryFramework\Annotations\Annotation;
+use ElementaryFramework\Annotations\AnnotationFile;
+use ElementaryFramework\Annotations\IAnnotationFileAware;
+use ElementaryFramework\Annotations\Exceptions\AnnotationException;
+
+/**
+ * Transformer Annotation
+ *
+ * Used to define the value transformer of an entity.
+ *
+ * This annotation have to be associated with the @entity
+ * annotation to take effect.
+ *
+ * @usage('class' => true, 'inherited' => true)
+ *
+ * @category Annotations
+ * @package LightQL
+ * @author Nana Axel
+ * @link http://lightql.na2axl.tk/docs/api/LightQL/Annotations/TransformerAnnotation
+ */
+class TransformerAnnotation extends Annotation implements IAnnotationFileAware
+{
+ /**
+ * Specify the class name to use as the value transformer
+ * of the current entity.
+ *
+ * @var string
+ */
+ public $transformer;
+
+ /**
+ * Annotation file.
+ *
+ * @var AnnotationFile
+ */
+ protected $file;
+
+ /**
+ * Initialize the annotation.
+ *
+ * @param array $properties The array of annotation properties
+ *
+ * @throws AnnotationException
+ *
+ * @return void
+ */
+ public function initAnnotation(array $properties)
+ {
+ $this->map($properties, array("transformer"));
+
+ parent::initAnnotation($properties);
+
+ if (!isset($this->transformer)) {
+ throw new AnnotationException(self::class . " must have a \"transformer\" property");
+ }
+
+ $this->transformer = $this->file->resolveType($this->transformer);
+ }
+
+ /**
+ * Provides information about file, that contains this annotation.
+ *
+ * @param AnnotationFile $file Annotation file.
+ *
+ * @return void
+ */
+ public function setAnnotationFile(AnnotationFile $file)
+ {
+ $this->file = $file;
+ }
+}
diff --git a/src/LightQL/Entities/EntityManager.php b/src/LightQL/Entities/EntityManager.php
index 61b7cb8..5e469b1 100644
--- a/src/LightQL/Entities/EntityManager.php
+++ b/src/LightQL/Entities/EntityManager.php
@@ -158,7 +158,10 @@ public function persist(Entity &$entity)
$autoIncrementProperty = null;
$idProperty = null;
+ /** @var IValueValidator $valueValidator */
$valueValidator = null;
+ /** @var IValueTransformer $valueTransformer */
+ $valueTransformer = null;
if (Annotations::classHasAnnotation($entity, "@validator")) {
$validatorAnnotation = Annotations::ofClass($entity, "@validator");
@@ -172,6 +175,18 @@ public function persist(Entity &$entity)
}
}
+ if (Annotations::classHasAnnotation($entity, "@transformer")) {
+ $transformerAnnotation = Annotations::ofClass($entity, "@transformer");
+
+ if (\is_subclass_of($transformerAnnotation[0]->transformer, IValueTransformer::class)) {
+ $transformerClass = new \ReflectionClass($transformerAnnotation[0]->transformer);
+
+ $valueTransformer = $transformerClass->newInstance();
+ } else {
+ throw new EntityException("The value transformer of this entity doesn't implement the IValueTransformer interface.");
+ }
+ }
+
/** @var Column $column */
foreach ($columns as $property => $column) {
if ($autoIncrementProperty === null && $column->isAutoIncrement) {
@@ -220,15 +235,15 @@ public function persist(Entity &$entity)
foreach ($columns as $property => $column) {
$value = $this->_lightql->quote($entity->get($column->getName()));
- if ($valueValidator !== null) {
- if ($valueValidator->validate($entity, $property)) {
- $fieldAndValues[$column->getName()] = $value;
- } else {
- throw new ValueValidatorException($property);
- }
- } else {
- $fieldAndValues[$column->getName()] = $value;
+ if ($valueValidator !== null && !$valueValidator->validate($entity, $property)) {
+ throw new ValueValidatorException($property);
}
+
+ if ($valueTransformer !== null) {
+ $value = $valueTransformer->toDatabaseValue($entity, $property);
+ }
+
+ $fieldAndValues[$column->getName()] = $value;
}
$this->_lightql->beginTransaction();
@@ -263,7 +278,10 @@ public function merge(Entity &$entity)
$columns = $entity->getColumns();
$fieldAndValues = array();
+ /** @var IValueValidator $valueValidator */
$valueValidator = null;
+ /** @var IValueTransformer $valueTransformer */
+ $valueTransformer = null;
$where = array();
@@ -282,6 +300,18 @@ public function merge(Entity &$entity)
}
}
+ if (Annotations::classHasAnnotation($entity, "@transformer")) {
+ $transformerAnnotation = Annotations::ofClass($entity, "@transformer");
+
+ if (\is_subclass_of($transformerAnnotation[0]->transformer, IValueTransformer::class)) {
+ $transformerClass = new \ReflectionClass($transformerAnnotation[0]->transformer);
+
+ $valueTransformer = $transformerClass->newInstance();
+ } else {
+ throw new EntityException("The value transformer of this entity doesn't implement the IValueTransformer interface.");
+ }
+ }
+
/** @var \ReflectionProperty $property */
foreach ($entityProperties as $property) {
$id = $entity->{$property->getName()};
@@ -302,16 +332,16 @@ public function merge(Entity &$entity)
foreach ($columns as $property => $column) {
$value = $this->_lightql->quote($entity->get($column->getName()));
- if ($valueValidator !== null) {
- if ($valueValidator->validate($entity, $property)) {
- $fieldAndValues[$column->getName()] = $value;
- } else {
- throw new ValueValidatorException($property);
- }
- } else {
- $fieldAndValues[$column->getName()] = $value;
+ if ($valueValidator !== null && !$valueValidator->validate($entity, $property)) {
+ throw new ValueValidatorException($property);
}
+ if ($valueTransformer !== null) {
+ $value = $valueTransformer->toDatabaseValue($entity, $property);
+ }
+
+ $fieldAndValues[$column->getName()] = $value;
+
if ($column->isPrimaryKey) {
$where[$column->getName()] = $this->_lightql->quote($entity->get($column->getName()));
}
diff --git a/src/LightQL/Entities/IValueTransformer.php b/src/LightQL/Entities/IValueTransformer.php
new file mode 100644
index 0000000..595edf8
--- /dev/null
+++ b/src/LightQL/Entities/IValueTransformer.php
@@ -0,0 +1,67 @@
+
+ * @copyright 2018 Aliens Group, Inc.
+ * @license MIT
+ * @version 1.0.0
+ * @link http://lightql.na2axl.tk
+ */
+
+namespace ElementaryFramework\LightQL\Entities;
+
+/**
+ * IValueTransformer
+ *
+ * Defines a class as the value transformer of an entity.
+ *
+ * @category Entities
+ * @package LightQL
+ * @author Nana Axel
+ * @link http://lightql.na2axl.tk/docs/api/LightQL/Entities/IValueTransformer
+ */
+interface IValueTransformer
+{
+ /**
+ * Transforms a value before send it to the database.
+ *
+ * @param Entity $entity The entity which will be sent to the database.
+ * @param string $property The name of the property to transform value.
+ *
+ * @return mixed The transformed value.
+ */
+ function toDatabaseValue(Entity $entity, string $property);
+
+ /**
+ * Transforms a value before send it to the entity.
+ *
+ * @param string $table The name of the table which provide the value.
+ * @param string $column The name of the column which hold the value.
+ * @param mixed $value The value to transform.
+ *
+ * @return mixed The transformed value.
+ */
+ function toEntityValue(string $table, string $column, $value);
+}
diff --git a/src/LightQL/Sessions/Facade.php b/src/LightQL/Sessions/Facade.php
index d607a68..fe91f06 100644
--- a/src/LightQL/Sessions/Facade.php
+++ b/src/LightQL/Sessions/Facade.php
@@ -39,6 +39,7 @@
use ElementaryFramework\LightQL\Entities\Entity;
use ElementaryFramework\LightQL\Entities\EntityManager;
use ElementaryFramework\LightQL\Entities\IEntity;
+use ElementaryFramework\LightQL\Entities\IValueTransformer;
use ElementaryFramework\LightQL\Entities\Query;
use ElementaryFramework\LightQL\Exceptions\EntityException;
use ElementaryFramework\LightQL\Exceptions\FacadeException;
@@ -542,7 +543,7 @@ private function _parseRawEntities($rawEntities, $annotations): array
* Parses raw data to Entity.
*
* @param array $rawEntity Raw entity data provided from database.
- * @param array $annotations The set of entity annotations.
+ * @param EntityAnnotation[] $annotations The set of entity annotations.
*
* @return Entity
*
@@ -552,6 +553,28 @@ private function _parseRawEntities($rawEntities, $annotations): array
*/
private function _parseRawEntity($rawEntity, $annotations): Entity
{
+ /** @var IValueTransformer $valueTransformer */
+ $valueTransformer = null;
+
+ if (Annotations::classHasAnnotation($this->getEntityClassName(), "@transformer")) {
+ $transformerAnnotation = Annotations::ofClass($this->getEntityClassName(), "@transformer");
+
+ if (\is_subclass_of($transformerAnnotation[0]->transformer, IValueTransformer::class)) {
+ $transformerClass = new \ReflectionClass($transformerAnnotation[0]->transformer);
+
+ $valueTransformer = $transformerClass->newInstance();
+ } else {
+ throw new EntityException("The value transformer of this entity doesn't implement the IValueTransformer interface.");
+ }
+ }
+
+ if ($valueTransformer !== null) {
+ foreach ($rawEntity as $column => &$value) {
+ $value = $valueTransformer->toEntityValue($annotations[0]->table, $column, $value);
+ }
+ unset($value);
+ }
+
/** @var Entity $entity */
$entity = $this->_class->newInstance($rawEntity);
From 078f319820660369895b06825a63a633beab324e Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Fri, 11 Jan 2019 05:20:12 +0100
Subject: [PATCH 06/42] Register the TransformAnnotation
---
src/LightQL/LightQL.php | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/LightQL/LightQL.php b/src/LightQL/LightQL.php
index b61a3fe..523b4cd 100644
--- a/src/LightQL/LightQL.php
+++ b/src/LightQL/LightQL.php
@@ -47,6 +47,7 @@
use ElementaryFramework\LightQL\Annotations\OneToOneAnnotation;
use ElementaryFramework\LightQL\Annotations\PersistenceUnitAnnotation;
use ElementaryFramework\LightQL\Annotations\SizeAnnotation;
+use ElementaryFramework\LightQL\Annotations\TransformerAnnotation;
use ElementaryFramework\LightQL\Annotations\UniqueAnnotation;
use ElementaryFramework\LightQL\Annotations\ValidatorAnnotation;
@@ -89,6 +90,7 @@ public static function registerAnnotations()
$manager->registerAnnotation("oneToOne", OneToOneAnnotation::class);
$manager->registerAnnotation("persistenceUnit", PersistenceUnitAnnotation::class);
$manager->registerAnnotation("size", SizeAnnotation::class);
+ $manager->registerAnnotation("transformer", TransformerAnnotation::class);
$manager->registerAnnotation("unique", UniqueAnnotation::class);
$manager->registerAnnotation("validator", ValidatorAnnotation::class);
}
From 6c99467b277d32be2849fa724e4050f4c9cd2c4a Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Fri, 11 Jan 2019 05:27:52 +0100
Subject: [PATCH 07/42] Add a banner image for the project
---
README.md | 4 ++++
assets/banner.png | Bin 0 -> 15963 bytes
2 files changed, 4 insertions(+)
create mode 100644 assets/banner.png
diff --git a/README.md b/README.md
index a441b4d..63af52e 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,7 @@
+
+
+
+
# LightQL
The lightweight PHP ORM
diff --git a/assets/banner.png b/assets/banner.png
new file mode 100644
index 0000000000000000000000000000000000000000..03198898699bed32fdd3a91e975f74e6516767fd
GIT binary patch
literal 15963
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
d%m(e}kfci)mlW=UKaJC
Date: Fri, 11 Jan 2019 06:04:26 +0100
Subject: [PATCH 08/42] Quote the transformed value
---
src/LightQL/Entities/EntityManager.php | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/LightQL/Entities/EntityManager.php b/src/LightQL/Entities/EntityManager.php
index 5e469b1..b5526a6 100644
--- a/src/LightQL/Entities/EntityManager.php
+++ b/src/LightQL/Entities/EntityManager.php
@@ -240,7 +240,7 @@ public function persist(Entity &$entity)
}
if ($valueTransformer !== null) {
- $value = $valueTransformer->toDatabaseValue($entity, $property);
+ $value = $this->_lightql->quote($valueTransformer->toDatabaseValue($entity, $property));
}
$fieldAndValues[$column->getName()] = $value;
@@ -337,7 +337,7 @@ public function merge(Entity &$entity)
}
if ($valueTransformer !== null) {
- $value = $valueTransformer->toDatabaseValue($entity, $property);
+ $value = $this->_lightql->quote($valueTransformer->toDatabaseValue($entity, $property));
}
$fieldAndValues[$column->getName()] = $value;
From 7c090327c166e878b6dd5abdcb3dc502b870478e Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Thu, 24 Jan 2019 15:44:07 +0100
Subject: [PATCH 09/42] Update README.md
---
README.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/README.md b/README.md
index 63af52e..24f4abd 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,7 @@
+

+
# LightQL
From 6c0d693105140820768dc473402ccc79ff9e5045 Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Tue, 19 Feb 2019 21:05:04 +0100
Subject: [PATCH 10/42] Add support for server port number in configuration
files, and support for XML-based configuration files
---
src/LightQL/Persistence/PersistenceUnit.php | 45 ++++++++++++++++++++-
1 file changed, 43 insertions(+), 2 deletions(-)
diff --git a/src/LightQL/Persistence/PersistenceUnit.php b/src/LightQL/Persistence/PersistenceUnit.php
index 383063e..4381570 100644
--- a/src/LightQL/Persistence/PersistenceUnit.php
+++ b/src/LightQL/Persistence/PersistenceUnit.php
@@ -81,6 +81,13 @@ class PersistenceUnit
*/
private $_password;
+ /**
+ * The port number of the DBMS.
+ *
+ * @var int
+ */
+ private $_port;
+
/**
* The list of registered persistence unit files.
*
@@ -132,6 +139,25 @@ private function __construct(string $key)
$content = parse_ini_file(self::$_registry[$key]);
} elseif ($extension === "json") {
$content = json_decode(file_get_contents(self::$_registry[$key]), true);
+ } elseif ($extension === "xml") {
+ $dom = new \DOMDocument("1.0", "utf-8");
+ $dom->loadXML(file_get_contents(self::$_registry[$key]));
+ if ($dom->documentElement->nodeName !== "persistenceUnit") {
+ throw new PersistenceUnitException("Invalid persistence unit XML configuration file provided.");
+ } else {
+ /** @var \DOMElement $node */
+ foreach ($dom->documentElement->childNodes as $node) {
+ switch (strtolower($node->nodeName)) {
+ case "dbms": $content["DBMS"] = $node->nodeValue; break;
+ case "hostname": $content["Hostname"] = $node->nodeValue; break;
+ case "database": $content["DatabaseName"] = $node->nodeValue; break;
+ case "username": $content["Username"] = $node->nodeValue; break;
+ case "password": $content["Password"] = $node->nodeValue; break;
+ case "port": $content["Port"] = $node->nodeValue; break;
+ default: throw new PersistenceUnitException("Invalid persistence unit XML configuration file provided. Unknown configuration item \"{$node->nodeName}\"");
+ }
+ }
+ }
} else {
throw new PersistenceUnitException("Unsupported file type used to create persistence unit {$filename}.");
}
@@ -165,6 +191,10 @@ private function __construct(string $key)
} else {
throw new PersistenceUnitException("Malformed persistence unit configuration file, missing the Password value.");
}
+
+ $this->_port = array_key_exists("Port", $content)
+ ? intval($content["Port"])
+ : null;
} else {
throw new PersistenceUnitException('Unable to find the persistence unit with the key "' . $key . '". Have you registered this persistence unit?');
}
@@ -176,8 +206,9 @@ private function __construct(string $key)
* @param string $key The persistence unit name.
*
* @return PersistenceUnit
+ * @throws PersistenceUnitException
*/
- public static function create(string $key)
+ public static function create(string $key): PersistenceUnit
{
if (array_key_exists($key, self::$_units)) {
return self::$_units[$key];
@@ -191,7 +222,7 @@ public static function create(string $key)
*
* @return string
*/
- public function getDbms()
+ public function getDbms(): string
{
return $this->_dbms;
}
@@ -235,4 +266,14 @@ public function getUsername(): string
{
return $this->_username;
}
+
+ /**
+ * Returns the port number.
+ *
+ * @return int
+ */
+ public function getPort(): int
+ {
+ return $this->_port;
+ }
}
From e5ea3bb3e0036bfe85f9be1e917d2e8ae494cb25 Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Tue, 19 Feb 2019 21:17:05 +0100
Subject: [PATCH 11/42] Update EntityManager.php
* Add support for server port number
* Move some code blocks into methods
* Update doc blocks
* Small fixes
---
src/LightQL/Entities/EntityManager.php | 127 +++++++++++++------------
1 file changed, 64 insertions(+), 63 deletions(-)
diff --git a/src/LightQL/Entities/EntityManager.php b/src/LightQL/Entities/EntityManager.php
index b5526a6..c238470 100644
--- a/src/LightQL/Entities/EntityManager.php
+++ b/src/LightQL/Entities/EntityManager.php
@@ -85,7 +85,8 @@ public function __construct(PersistenceUnit $persistenceUnit)
"database" => $this->_persistenceUnit->getDatabase(),
"hostname" => $this->_persistenceUnit->getHostname(),
"username" => $this->_persistenceUnit->getUsername(),
- "password" => $this->_persistenceUnit->getPassword()
+ "password" => $this->_persistenceUnit->getPassword(),
+ "port" => $this->_persistenceUnit->getPort()
)
);
}
@@ -146,8 +147,10 @@ public function find(string $entityClass, $id): array
*
* @param Entity &$entity The entity to create.
*
+ * @throws EntityException
+ * @throws ValueValidatorException
* @throws \ElementaryFramework\Annotations\Exceptions\AnnotationException
- * @throws \ElementaryFramework\LightQL\Exceptions\EntityException
+ * @throws \ReflectionException
*/
public function persist(Entity &$entity)
{
@@ -158,34 +161,8 @@ public function persist(Entity &$entity)
$autoIncrementProperty = null;
$idProperty = null;
- /** @var IValueValidator $valueValidator */
- $valueValidator = null;
- /** @var IValueTransformer $valueTransformer */
- $valueTransformer = null;
-
- if (Annotations::classHasAnnotation($entity, "@validator")) {
- $validatorAnnotation = Annotations::ofClass($entity, "@validator");
-
- if (\is_subclass_of($validatorAnnotation[0]->validator, IValueValidator::class)) {
- $validatorClass = new \ReflectionClass($validatorAnnotation[0]->validator);
-
- $valueValidator = $validatorClass->newInstance();
- } else {
- throw new EntityException("The value validator of this entity doesn't implement the IValueValidator interface.");
- }
- }
-
- if (Annotations::classHasAnnotation($entity, "@transformer")) {
- $transformerAnnotation = Annotations::ofClass($entity, "@transformer");
-
- if (\is_subclass_of($transformerAnnotation[0]->transformer, IValueTransformer::class)) {
- $transformerClass = new \ReflectionClass($transformerAnnotation[0]->transformer);
-
- $valueTransformer = $transformerClass->newInstance();
- } else {
- throw new EntityException("The value transformer of this entity doesn't implement the IValueTransformer interface.");
- }
- }
+ $valueValidator = $this->_getValueValidatorOfEntity($entity);
+ $valueTransformer = $this->_getValueTransformerOfEntity($entity);
/** @var Column $column */
foreach ($columns as $property => $column) {
@@ -253,7 +230,7 @@ public function persist(Entity &$entity)
->insert($fieldAndValues);
if ($autoIncrementProperty !== null) {
- $entity->$autoIncrementProperty = $this->_lightql->lastInsertID();
+ $entity->{$autoIncrementProperty} = $this->_lightql->lastInsertID();
}
$this->_lightql->commit();
@@ -269,8 +246,10 @@ public function persist(Entity &$entity)
*
* @param Entity &$entity The entity to edit.
*
+ * @throws EntityException
+ * @throws ValueValidatorException
* @throws \ElementaryFramework\Annotations\Exceptions\AnnotationException
- * @throws \ElementaryFramework\LightQL\Exceptions\EntityException
+ * @throws \ReflectionException
*/
public function merge(Entity &$entity)
{
@@ -278,40 +257,14 @@ public function merge(Entity &$entity)
$columns = $entity->getColumns();
$fieldAndValues = array();
- /** @var IValueValidator $valueValidator */
- $valueValidator = null;
- /** @var IValueTransformer $valueTransformer */
- $valueTransformer = null;
+ $valueValidator = $this->_getValueValidatorOfEntity($entity);
+ $valueTransformer = $this->_getValueTransformerOfEntity($entity);
$where = array();
$entityReflection = new \ReflectionClass($entity);
$entityProperties = $entityReflection->getProperties();
- if (Annotations::classHasAnnotation($entity, "@validator")) {
- $validatorAnnotation = Annotations::ofClass($entity, "@validator");
-
- if (\is_subclass_of($validatorAnnotation[0]->validator, IValueValidator::class)) {
- $validatorClass = new \ReflectionClass($validatorAnnotation[0]->validator);
-
- $valueValidator = $validatorClass->newInstance();
- } else {
- throw new EntityException("The value validator of this entity doesn't implement the IValueValidator interface.");
- }
- }
-
- if (Annotations::classHasAnnotation($entity, "@transformer")) {
- $transformerAnnotation = Annotations::ofClass($entity, "@transformer");
-
- if (\is_subclass_of($transformerAnnotation[0]->transformer, IValueTransformer::class)) {
- $transformerClass = new \ReflectionClass($transformerAnnotation[0]->transformer);
-
- $valueTransformer = $transformerClass->newInstance();
- } else {
- throw new EntityException("The value transformer of this entity doesn't implement the IValueTransformer interface.");
- }
- }
-
/** @var \ReflectionProperty $property */
foreach ($entityProperties as $property) {
$id = $entity->{$property->getName()};
@@ -367,15 +320,15 @@ public function merge(Entity &$entity)
*
* @param Entity &$entity The entity to delete.
*
+ * @throws EntityException
* @throws \ElementaryFramework\Annotations\Exceptions\AnnotationException
- * @throws \ElementaryFramework\LightQL\Exceptions\EntityException
+ * @throws \ReflectionException
*/
public function delete(Entity &$entity)
{
$entityAnnotation = Annotations::ofClass($entity, "@entity");
$columns = $entity->getColumns();
- $fieldAndValues = array();
$where = array();
$pk = array();
@@ -416,7 +369,7 @@ public function delete(Entity &$entity)
if (count($pk) > 0) {
foreach ($pk as $item) {
- $entity->$item = null;
+ $entity->{$item} = null;
}
}
@@ -438,4 +391,52 @@ public function getLightQL(): LightQL
{
return $this->_lightql;
}
+
+ /**
+ * @param $entity
+ * @return IValueValidator
+ * @throws EntityException
+ * @throws \ElementaryFramework\Annotations\Exceptions\AnnotationException
+ * @throws \ReflectionException
+ */
+ private function _getValueValidatorOfEntity($entity)
+ {
+ if (Annotations::classHasAnnotation($entity, "@validator")) {
+ $validatorAnnotation = Annotations::ofClass($entity, "@validator");
+
+ if (\is_subclass_of($validatorAnnotation[0]->validator, IValueValidator::class)) {
+ $validatorClass = new \ReflectionClass($validatorAnnotation[0]->validator);
+
+ return $validatorClass->newInstance();
+ } else {
+ throw new EntityException("The value validator of this entity doesn't implement the IValueValidator interface.");
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @param $entity
+ * @return IValueTransformer
+ * @throws EntityException
+ * @throws \ElementaryFramework\Annotations\Exceptions\AnnotationException
+ * @throws \ReflectionException
+ */
+ private function _getValueTransformerOfEntity($entity)
+ {
+ if (Annotations::classHasAnnotation($entity, "@transformer")) {
+ $transformerAnnotation = Annotations::ofClass($entity, "@transformer");
+
+ if (\is_subclass_of($transformerAnnotation[0]->transformer, IValueTransformer::class)) {
+ $transformerClass = new \ReflectionClass($transformerAnnotation[0]->transformer);
+
+ return $transformerClass->newInstance();
+ } else {
+ throw new EntityException("The value transformer of this entity doesn't implement the IValueTransformer interface.");
+ }
+ }
+
+ return null;
+ }
}
From e35922f01f34f04356e46cf3b5afa20c2a808464 Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Tue, 19 Feb 2019 21:21:19 +0100
Subject: [PATCH 12/42] Introduce Facade listeners, used to listen facade
operations (create, update, delete) and execute a predefined action or cancel
the operation
---
.../Annotations/ListenerAnnotation.php | 102 +++++++++++++++
.../OperationCancelledException.php | 49 ++++++++
src/LightQL/Sessions/Facade.php | 117 ++++++++++++------
src/LightQL/Sessions/IFacadeListener.php | 96 ++++++++++++++
4 files changed, 328 insertions(+), 36 deletions(-)
create mode 100644 src/LightQL/Annotations/ListenerAnnotation.php
create mode 100644 src/LightQL/Exceptions/OperationCancelledException.php
create mode 100644 src/LightQL/Sessions/IFacadeListener.php
diff --git a/src/LightQL/Annotations/ListenerAnnotation.php b/src/LightQL/Annotations/ListenerAnnotation.php
new file mode 100644
index 0000000..46bae0c
--- /dev/null
+++ b/src/LightQL/Annotations/ListenerAnnotation.php
@@ -0,0 +1,102 @@
+
+ * @copyright 2018 Aliens Group, Inc.
+ * @license MIT
+ * @version 1.0.0
+ * @link http://lightql.na2axl.tk
+ */
+
+namespace ElementaryFramework\LightQL\Annotations;
+
+use ElementaryFramework\Annotations\Annotation;
+use ElementaryFramework\Annotations\AnnotationFile;
+use ElementaryFramework\Annotations\Exceptions\AnnotationException;
+use ElementaryFramework\Annotations\IAnnotationFileAware;
+
+/**
+ * Listener Annotation
+ *
+ * Used to define the listener for entity facades.
+ *
+ * @usage('class' => true, 'inherited' => true)
+ *
+ * @category Annotations
+ * @package LightQL
+ * @author Nana Axel
+ * @link http://lightql.na2axl.tk/docs/api/LightQL/Annotations/ListenerAnnotation
+ */
+class ListenerAnnotation extends Annotation implements IAnnotationFileAware
+{
+ /**
+ * Specify the class name to use as the listener
+ * of the current entity facade.
+ *
+ * @var string
+ */
+ public $listener;
+
+ /**
+ * Annotation file.
+ *
+ * @var AnnotationFile
+ */
+ protected $file;
+
+ /**
+ * Initialize the annotation.
+ *
+ * @param array $properties The array of annotation properties
+ *
+ * @throws AnnotationException
+ *
+ * @return void
+ */
+ public function initAnnotation(array $properties)
+ {
+ $this->map($properties, array("listener"));
+
+ parent::initAnnotation($properties);
+
+ if (!isset($this->listener)) {
+ throw new AnnotationException(self::class . " must have a \"listener\" property");
+ }
+
+ $this->listener = $this->file->resolveType($this->listener);
+ }
+
+ /**
+ * Provides information about file, that contains this annotation.
+ *
+ * @param AnnotationFile $file Annotation file.
+ *
+ * @return void
+ */
+ public function setAnnotationFile(AnnotationFile $file)
+ {
+ $this->file = $file;
+ }
+}
diff --git a/src/LightQL/Exceptions/OperationCancelledException.php b/src/LightQL/Exceptions/OperationCancelledException.php
new file mode 100644
index 0000000..c47d3a3
--- /dev/null
+++ b/src/LightQL/Exceptions/OperationCancelledException.php
@@ -0,0 +1,49 @@
+
+ * @copyright 2018 Aliens Group, Inc.
+ * @license MIT
+ * @version 1.0.0
+ * @link http://lightql.na2axl.tk
+ */
+
+namespace ElementaryFramework\LightQL\Exceptions;
+
+/**
+ * Operation Cancelled Exception
+ *
+ * @category Exceptions
+ * @package LightQL
+ * @author Nana Axel
+ * @link http://lightql.na2axl.tk/docs/api/LightQL/Exceptions/OperationCancelledException
+ */
+class OperationCancelledException extends \Exception
+{
+ public function __construct(string $listenerClassName)
+ {
+ parent::__construct("The operation has been cancelled by the listener: \"{$listenerClassName}\"");
+ }
+}
diff --git a/src/LightQL/Sessions/Facade.php b/src/LightQL/Sessions/Facade.php
index fe91f06..cf4d32c 100644
--- a/src/LightQL/Sessions/Facade.php
+++ b/src/LightQL/Sessions/Facade.php
@@ -34,7 +34,7 @@
use ElementaryFramework\Annotations\Annotations;
use ElementaryFramework\Annotations\Exceptions\AnnotationException;
-
+use ElementaryFramework\LightQL\Annotations\EntityAnnotation;
use ElementaryFramework\LightQL\Annotations\NamedQueryAnnotation;
use ElementaryFramework\LightQL\Entities\Entity;
use ElementaryFramework\LightQL\Entities\EntityManager;
@@ -43,6 +43,7 @@
use ElementaryFramework\LightQL\Entities\Query;
use ElementaryFramework\LightQL\Exceptions\EntityException;
use ElementaryFramework\LightQL\Exceptions\FacadeException;
+use ElementaryFramework\LightQL\Exceptions\OperationCancelledException;
use ElementaryFramework\LightQL\Persistence\PersistenceUnit;
/**
@@ -72,15 +73,24 @@ abstract class Facade implements IFacade
*/
private $_class;
+ /**
+ * The listener of this facade.
+ *
+ * @var IFacadeListener
+ */
+ private $_listener;
+
/**
* Facade constructor.
*
* @param string $class The entity class name managed by this facade.
*
+ * @throws AnnotationException When the Facade is unable to read an annotation.
* @throws EntityException When the entity class or object doesn't have an @entity annotation.
- * @throws FacadeException When the "entityManager" property of this Facade doesn't have a @persistenceUnit annotation.
* @throws FacadeException When the entity class or object doesn't inherit from the Entity class.
- * @throws AnnotationException When the Facade is unable to read an annotation.
+ * @throws \ElementaryFramework\LightQL\Exceptions\LightQLException
+ * @throws \ElementaryFramework\LightQL\Exceptions\PersistenceUnitException
+ * @throws \ReflectionException
*/
public function __construct($class)
{
@@ -96,6 +106,11 @@ public function __construct($class)
throw new FacadeException("Unable to create a facade. The entity class or object seems to be invalid.");
}
+ if (Annotations::classHasAnnotation($this, "@listener")) {
+ $listenerReflection = new \ReflectionClass(Annotations::ofClass($this, "@listener")[0]->listener);
+ $this->_listener = $listenerReflection->newInstance();
+ }
+
$this->_class = new \ReflectionClass($class);
$annotations = Annotations::ofProperty($this, "entityManager", "@persistenceUnit");
@@ -107,9 +122,9 @@ public function __construct($class)
*
* @param Entity $entity The entity to create.
*
- * @throws FacadeException
- * @throws \ElementaryFramework\Annotations\Exceptions\AnnotationException
- * @throws \ElementaryFramework\LightQL\Exceptions\EntityException
+ * @throws AnnotationException
+ * @throws FacadeException When the facade is unable to create the entity.
+ * @throws OperationCancelledException When the operation has been cancelled by a listener
*/
public function create(Entity &$entity)
{
@@ -117,6 +132,10 @@ public function create(Entity &$entity)
throw new FacadeException("Cannot create entity. The type of the entity is not valid for this facade.");
}
+ if ($this->_listener instanceof IFacadeListener && !$this->_listener->beforeCreate($entity)) {
+ throw new OperationCancelledException(Annotations::ofClass($this, "@listener")[0]->listener);
+ }
+
try {
$this->entityManager->persist($entity);
@@ -132,8 +151,10 @@ public function create(Entity &$entity)
$this->_fetchOneToOne($entity, $property);
}
}
+
+ $this->_listener instanceof IFacadeListener && $this->_listener->onCreate($entity);
} catch (\Exception $e) {
- throw new FacadeException($e->getMessage());
+ throw new FacadeException("Unable to create the entity. See internal exception for more details.", 0, $e);
}
}
@@ -142,9 +163,12 @@ public function create(Entity &$entity)
*
* @param Entity $entity The entity to edit.
*
- * @throws FacadeException
- * @throws \ElementaryFramework\Annotations\Exceptions\AnnotationException
- * @throws \ElementaryFramework\LightQL\Exceptions\EntityException
+ * @throws AnnotationException
+ * @throws EntityException
+ * @throws FacadeException When the facade is unable to edit the entity
+ * @throws OperationCancelledException When the operation has been cancelled by a listener
+ * @throws \ElementaryFramework\LightQL\Exceptions\ValueValidatorException
+ * @throws \ReflectionException
*/
public function edit(Entity &$entity)
{
@@ -152,7 +176,13 @@ public function edit(Entity &$entity)
throw new FacadeException("Cannot edit entity. The type of the entity is not valid for this facade.");
}
+ if ($this->_listener instanceof IFacadeListener && !$this->_listener->beforeEdit($entity)) {
+ throw new OperationCancelledException(Annotations::ofClass($this, "@listener")[0]->listener);
+ }
+
$this->entityManager->merge($entity);
+
+ $this->_listener instanceof IFacadeListener && $this->_listener->onEdit($entity);
}
/**
@@ -160,18 +190,27 @@ public function edit(Entity &$entity)
*
* @param Entity $entity The entity to delete.
*
- * @throws FacadeException
- * @throws \ElementaryFramework\Annotations\Exceptions\AnnotationException
- * @throws \ElementaryFramework\LightQL\Exceptions\EntityException
+ * @throws AnnotationException
+ * @throws EntityException
+ * @throws FacadeException When the facade is unable to delete the entity
+ * @throws OperationCancelledException When the operation has been cancelled by a listener
+ * @throws \ElementaryFramework\LightQL\Exceptions\ValueValidatorException
+ * @throws \ReflectionException
*/
public function delete(Entity &$entity)
{
if (!$this->_class->isInstance($entity)) {
- throw new FacadeException("Cannot edit entity. The type of the entity is not valid for this facade.");
+ throw new FacadeException("Cannot delete entity. The type of the entity is not valid for this facade.");
+ }
+
+ if ($this->_listener instanceof IFacadeListener && !$this->_listener->beforeDelete($entity)) {
+ throw new OperationCancelledException(Annotations::ofClass($this, "@listener")[0]->listener);
}
$this->entityManager->merge($entity);
$this->entityManager->delete($entity);
+
+ $this->_listener instanceof IFacadeListener && $this->_listener->onDelete($entity);
}
/**
@@ -181,8 +220,10 @@ public function delete(Entity &$entity)
*
* @return Entity
*
- * @throws \ElementaryFramework\Annotations\Exceptions\AnnotationException
+ * @throws AnnotationException
+ * @throws EntityException
* @throws \ElementaryFramework\LightQL\Exceptions\LightQLException
+ * @throws \ReflectionException
*/
public function find($id): Entity
{
@@ -199,9 +240,10 @@ public function find($id): Entity
*
* @return Entity[]
*
+ * @throws AnnotationException
* @throws EntityException
- * @throws \ElementaryFramework\Annotations\Exceptions\AnnotationException
* @throws \ElementaryFramework\LightQL\Exceptions\LightQLException
+ * @throws \ReflectionException
*/
public function findAll(): array
{
@@ -218,14 +260,15 @@ public function findAll(): array
/**
* Find all entities in the given range.
*
- * @param int $start The starting offset.
+ * @param int $start The starting offset.
* @param int $length The number of entities to find.
*
* @return Entity[]
*
+ * @throws AnnotationException
* @throws EntityException
- * @throws \ElementaryFramework\Annotations\Exceptions\AnnotationException
* @throws \ElementaryFramework\LightQL\Exceptions\LightQLException
+ * @throws \ReflectionException
*/
public function findRange(int $start, int $length): array
{
@@ -330,10 +373,10 @@ private function _fetchManyToMany(&$entity, $property)
{
$manyToMany = Annotations::ofProperty($entity, $property, "@manyToMany");
$column = Annotations::ofProperty($entity, $property, "@column");
- $entityAnnotations = Annotations::ofClass($entity, "@entity");
$mappedPropertyName = null;
+ /** @var Entity $referencedEntity */
$referencedEntity = new $manyToMany[0]->entity;
foreach ($referencedEntity->getColumns() as $p => $c) {
if ($c->isManyToMany) {
@@ -365,7 +408,7 @@ private function _fetchManyToMany(&$entity, $property)
array(
"side" => "LEFT",
"table" => $referencedEntityAnnotations[0]->table,
- "cond" => "{$manyToMany[0]->crossTable}.{$mappedPropertyAnnotation[0]->referencedColumn} = {$referencedEntityAnnotations[0]->table}.{$mappedPropertyColumnAnnotation[0]->name}"
+ "cond" => "{$manyToMany[0]->crossTable}.{$mappedPropertyManyToManyAnnotation[0]->referencedColumn} = {$referencedEntityAnnotations[0]->table}.{$mappedPropertyColumnAnnotation[0]->name}"
)
)
);
@@ -379,10 +422,11 @@ private function _fetchManyToMany(&$entity, $property)
/**
* Fetch data for a one-to-many relation.
*
- * @param IEntity $entity The managed entity.
- * @param string $property The property in one-to-many relation.
+ * @param IEntity $entity The managed entity.
+ * @param string $property The property in one-to-many relation.
*
- * @throws \ElementaryFramework\Annotations\Exceptions\AnnotationException
+ * @throws AnnotationException
+ * @throws EntityException
* @throws \ElementaryFramework\LightQL\Exceptions\LightQLException
*/
private function _fetchOneToMany(&$entity, $property)
@@ -397,8 +441,6 @@ private function _fetchOneToMany(&$entity, $property)
throw new EntityException("Unable to find a suitable property with @manyToOne annotation in the entity \"{$oneToMany[0]->entity}\".");
}
- $mappedPropertyManyToOneAnnotation = Annotations::ofProperty($oneToMany[0]->entity, $mappedPropertyName, "@manyToOne");
-
$lightql = $this->entityManager->getLightQL();
$result = $lightql
@@ -418,10 +460,11 @@ private function _fetchOneToMany(&$entity, $property)
/**
* Fetch data for a many-to-one relation.
*
- * @param IEntity $entity The managed entity.
- * @param string $property The property in many-to-one relation.
+ * @param IEntity $entity The managed entity.
+ * @param string $property The property in many-to-one relation.
*
- * @throws \ElementaryFramework\Annotations\Exceptions\AnnotationException
+ * @throws AnnotationException
+ * @throws EntityException
* @throws \ElementaryFramework\LightQL\Exceptions\LightQLException
*/
private function _fetchManyToOne(&$entity, $property)
@@ -454,10 +497,11 @@ private function _fetchManyToOne(&$entity, $property)
/**
* Fetch data for a one-to-one relation.
*
- * @param IEntity $entity The managed entity.
- * @param string $property The property in one-to-one relation.
+ * @param IEntity $entity The managed entity.
+ * @param string $property The property in one-to-one relation.
*
- * @throws \ElementaryFramework\Annotations\Exceptions\AnnotationException
+ * @throws AnnotationException
+ * @throws EntityException
* @throws \ElementaryFramework\LightQL\Exceptions\LightQLException
*/
private function _fetchOneToOne(&$entity, $property)
@@ -472,8 +516,6 @@ private function _fetchOneToOne(&$entity, $property)
throw new EntityException("Unable to find a suitable property with @oneToOne annotation in the entity \"{$oneToOne[0]->entity}\".");
}
- $mappedPropertyAnnotation = Annotations::ofProperty($oneToOne[0]->entity, $mappedPropertyName, "@oneToOne");
-
$lightql = $this->entityManager->getLightQL();
$result = $lightql
@@ -504,6 +546,7 @@ private function _resolveMappedPropertyName(string $entityClass, string $check,
{
$mappedPropertyName = null;
+ /** @var Entity $referencedEntity */
$referencedEntity = new $entityClass;
foreach ($referencedEntity->getColumns() as $p => $c) {
if ($c->{"is{$check}"} && $c->getName() === $column) {
@@ -524,9 +567,10 @@ private function _resolveMappedPropertyName(string $entityClass, string $check,
*
* @return Entity[]
*
+ * @throws AnnotationException
* @throws EntityException
- * @throws \ElementaryFramework\Annotations\Exceptions\AnnotationException
* @throws \ElementaryFramework\LightQL\Exceptions\LightQLException
+ * @throws \ReflectionException
*/
private function _parseRawEntities($rawEntities, $annotations): array
{
@@ -542,14 +586,15 @@ private function _parseRawEntities($rawEntities, $annotations): array
/**
* Parses raw data to Entity.
*
- * @param array $rawEntity Raw entity data provided from database.
+ * @param array $rawEntity Raw entity data provided from database.
* @param EntityAnnotation[] $annotations The set of entity annotations.
*
* @return Entity
*
+ * @throws AnnotationException
* @throws EntityException
- * @throws \ElementaryFramework\Annotations\Exceptions\AnnotationException
* @throws \ElementaryFramework\LightQL\Exceptions\LightQLException
+ * @throws \ReflectionException
*/
private function _parseRawEntity($rawEntity, $annotations): Entity
{
diff --git a/src/LightQL/Sessions/IFacadeListener.php b/src/LightQL/Sessions/IFacadeListener.php
new file mode 100644
index 0000000..591ab44
--- /dev/null
+++ b/src/LightQL/Sessions/IFacadeListener.php
@@ -0,0 +1,96 @@
+
+ * @copyright 2018 Aliens Group, Inc.
+ * @license MIT
+ * @version 1.0.0
+ * @link http://lightql.na2axl.tk
+ */
+
+namespace ElementaryFramework\LightQL\Sessions;
+
+use ElementaryFramework\LightQL\Entities\Entity;
+
+/**
+ * IFacadeListener
+ *
+ * Provide methods for all entity facade listeners.
+ *
+ * @category Sessions
+ * @package LightQL
+ * @author Nana Axel
+ * @link http://lightql.na2axl.tk/docs/api/LightQL/Sessions/IFacadeListener
+ */
+interface IFacadeListener
+{
+ /**
+ * An entity will be created.
+ *
+ * @param Entity $entity The entity to create.
+ *
+ * @return bool true if we can execute the query, false to cancel the entity creation.
+ */
+ function beforeCreate(Entity &$entity): bool;
+
+ /**
+ * An entity was just created.
+ *
+ * @param Entity $entity The created entity.
+ */
+ function onCreate(Entity $entity);
+
+ /**
+ * An entity will be edited.
+ *
+ * @param Entity $entity The entity to edit.
+ *
+ * @return bool true if we can execute the query, false to cancel the entity edition.
+ */
+ function beforeEdit(Entity &$entity): bool;
+
+ /**
+ * An entity was just edited.
+ *
+ * @param Entity $entity The entity to edit.
+ */
+ function onEdit(Entity $entity);
+
+ /**
+ * An entity will be deleted.
+ *
+ * @param Entity $entity The entity to delete.
+ *
+ * @return bool true if we can execute the query, false to cancel the entity deletion.
+ */
+ function beforeDelete(Entity &$entity): bool;
+
+ /**
+ * An entity was just deleted.
+ *
+ * @param Entity $entity The entity to delete.
+ */
+ function onDelete(Entity $entity);
+}
\ No newline at end of file
From 70d367c2f43ca68b5c7536aa460bd8a5b44e848b Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Tue, 19 Feb 2019 22:22:16 +0100
Subject: [PATCH 13/42] Register ListenerAnnotation into LightQL
---
src/LightQL/LightQL.php | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/LightQL/LightQL.php b/src/LightQL/LightQL.php
index 523b4cd..98c1f6d 100644
--- a/src/LightQL/LightQL.php
+++ b/src/LightQL/LightQL.php
@@ -33,12 +33,12 @@
namespace ElementaryFramework\LightQL;
use ElementaryFramework\Annotations\Annotations;
-
use ElementaryFramework\LightQL\Annotations\AutoIncrementAnnotation;
use ElementaryFramework\LightQL\Annotations\ColumnAnnotation;
use ElementaryFramework\LightQL\Annotations\EntityAnnotation;
use ElementaryFramework\LightQL\Annotations\IdAnnotation;
use ElementaryFramework\LightQL\Annotations\IdGeneratorAnnotation;
+use ElementaryFramework\LightQL\Annotations\ListenerAnnotation;
use ElementaryFramework\LightQL\Annotations\ManyToManyAnnotation;
use ElementaryFramework\LightQL\Annotations\ManyToOneAnnotation;
use ElementaryFramework\LightQL\Annotations\NamedQueryAnnotation;
@@ -50,7 +50,6 @@
use ElementaryFramework\LightQL\Annotations\TransformerAnnotation;
use ElementaryFramework\LightQL\Annotations\UniqueAnnotation;
use ElementaryFramework\LightQL\Annotations\ValidatorAnnotation;
-
use ElementaryFramework\LightQL\Exceptions\LightQLException;
/**
@@ -82,6 +81,7 @@ public static function registerAnnotations()
$manager->registerAnnotation("entity", EntityAnnotation::class);
$manager->registerAnnotation("id", IdAnnotation::class);
$manager->registerAnnotation("idGenerator", IdGeneratorAnnotation::class);
+ $manager->registerAnnotation("listener", ListenerAnnotation::class);
$manager->registerAnnotation("manyToMany", ManyToManyAnnotation::class);
$manager->registerAnnotation("manyToOne", ManyToOneAnnotation::class);
$manager->registerAnnotation("namedQuery", NamedQueryAnnotation::class);
From 2a5c5d3524f85460d842edf04e37d9e94e091c36 Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Tue, 19 Feb 2019 22:26:27 +0100
Subject: [PATCH 14/42] Fix PostgreSQL support
---
src/LightQL/LightQL.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/LightQL/LightQL.php b/src/LightQL/LightQL.php
index 98c1f6d..bd92b52 100644
--- a/src/LightQL/LightQL.php
+++ b/src/LightQL/LightQL.php
@@ -846,7 +846,7 @@ public function insert(array $fieldsAndValues): bool
$column = implode(",", $columns);
$value = implode(",", $values);
- $this->_queryString = trim("INSERT INTO {$this->table}({$column}) VALUE ({$value})");
+ $this->_queryString = trim("INSERT INTO {$this->table}({$column}) VALUES ({$value})");
$getFieldsData = $this->prepare($this->_queryString);
From 28ed1b471aec5f23d53a378f63fc983fef576934 Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Fri, 22 Feb 2019 11:30:48 +0100
Subject: [PATCH 15/42] Add missing @throws in doc blocks
---
src/LightQL/Entities/Entity.php | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/LightQL/Entities/Entity.php b/src/LightQL/Entities/Entity.php
index 0e2bef4..0797404 100644
--- a/src/LightQL/Entities/Entity.php
+++ b/src/LightQL/Entities/Entity.php
@@ -35,7 +35,6 @@
use ElementaryFramework\Annotations\Annotations;
use ElementaryFramework\Annotations\IAnnotation;
use ElementaryFramework\LightQL\Exceptions\EntityException;
-use ElementaryFramework\LightQL\Exceptions\AnnotationException;
/**
* Entity
@@ -87,7 +86,7 @@ abstract class Entity implements IEntity
* @param array $data The raw database data.
*
* @throws EntityException
- * @throws AnnotationException
+ * @throws \ElementaryFramework\Annotations\Exceptions\AnnotationException
* @throws \ReflectionException
*/
public function __construct(array $data = array())
@@ -164,6 +163,7 @@ public function hydrate(array $data)
* @param string $column The table column name.
*
* @return mixed
+ * @throws \ElementaryFramework\Annotations\Exceptions\AnnotationException
*/
public function get(string $column)
{
From 6ee51bf6381d3c3520bd835f2d9a2b90f849e4d3 Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Mon, 11 Mar 2019 12:23:37 +0100
Subject: [PATCH 16/42] Use IEntity instead of Entity in IFacade interface
---
src/LightQL/Sessions/Facade.php | 18 ++++++++--------
src/LightQL/Sessions/IFacade.php | 22 ++++++++++----------
src/LightQL/Sessions/IFacadeListener.php | 26 ++++++++++++------------
3 files changed, 33 insertions(+), 33 deletions(-)
diff --git a/src/LightQL/Sessions/Facade.php b/src/LightQL/Sessions/Facade.php
index cf4d32c..0b2a69f 100644
--- a/src/LightQL/Sessions/Facade.php
+++ b/src/LightQL/Sessions/Facade.php
@@ -103,7 +103,7 @@ public function __construct($class)
}
if (!is_subclass_of($class, Entity::class)) {
- throw new FacadeException("Unable to create a facade. The entity class or object seems to be invalid.");
+ throw new FacadeException("Unable to create a facade. The entity class or object need to inherit directly from the class Entity.");
}
if (Annotations::classHasAnnotation($this, "@listener")) {
@@ -120,13 +120,13 @@ public function __construct($class)
/**
* Creates an entity.
*
- * @param Entity $entity The entity to create.
+ * @param IEntity $entity The entity to create.
*
* @throws AnnotationException
* @throws FacadeException When the facade is unable to create the entity.
* @throws OperationCancelledException When the operation has been cancelled by a listener
*/
- public function create(Entity &$entity)
+ public function create(IEntity &$entity)
{
if (!$this->_class->isInstance($entity)) {
throw new FacadeException("Cannot create entity. The type of the entity is not valid for this facade.");
@@ -161,7 +161,7 @@ public function create(Entity &$entity)
/**
* Edit an entity.
*
- * @param Entity $entity The entity to edit.
+ * @param IEntity $entity The entity to edit.
*
* @throws AnnotationException
* @throws EntityException
@@ -170,7 +170,7 @@ public function create(Entity &$entity)
* @throws \ElementaryFramework\LightQL\Exceptions\ValueValidatorException
* @throws \ReflectionException
*/
- public function edit(Entity &$entity)
+ public function edit(IEntity &$entity)
{
if (!$this->_class->isInstance($entity)) {
throw new FacadeException("Cannot edit entity. The type of the entity is not valid for this facade.");
@@ -188,7 +188,7 @@ public function edit(Entity &$entity)
/**
* Delete an entity.
*
- * @param Entity $entity The entity to delete.
+ * @param IEntity $entity The entity to delete.
*
* @throws AnnotationException
* @throws EntityException
@@ -197,7 +197,7 @@ public function edit(Entity &$entity)
* @throws \ElementaryFramework\LightQL\Exceptions\ValueValidatorException
* @throws \ReflectionException
*/
- public function delete(Entity &$entity)
+ public function delete(IEntity &$entity)
{
if (!$this->_class->isInstance($entity)) {
throw new FacadeException("Cannot delete entity. The type of the entity is not valid for this facade.");
@@ -218,14 +218,14 @@ public function delete(Entity &$entity)
*
* @param mixed $id The id of the entity to find
*
- * @return Entity
+ * @return IEntity
*
* @throws AnnotationException
* @throws EntityException
* @throws \ElementaryFramework\LightQL\Exceptions\LightQLException
* @throws \ReflectionException
*/
- public function find($id): Entity
+ public function find($id): IEntity
{
$annotations = Annotations::ofClass($this->getEntityClassName(), "@entity");
diff --git a/src/LightQL/Sessions/IFacade.php b/src/LightQL/Sessions/IFacade.php
index 9f6f2db..f090b6f 100644
--- a/src/LightQL/Sessions/IFacade.php
+++ b/src/LightQL/Sessions/IFacade.php
@@ -32,7 +32,7 @@
namespace ElementaryFramework\LightQL\Sessions;
-use ElementaryFramework\LightQL\Entities\Entity;
+use ElementaryFramework\LightQL\Entities\IEntity;
/**
* IFacade
@@ -49,37 +49,37 @@ interface IFacade
/**
* Creates an entity.
*
- * @param Entity $entity The entity to create.
+ * @param IEntity $entity The entity to create.
*/
- function create(Entity &$entity);
+ function create(IEntity &$entity);
/**
* Edit an entity.
*
- * @param Entity $entity The entity to edit.
+ * @param IEntity $entity The entity to edit.
*/
- function edit(Entity &$entity);
+ function edit(IEntity &$entity);
/**
* Delete an entity.
*
- * @param Entity $entity The entity to delete.
+ * @param IEntity $entity The entity to delete.
*/
- function delete(Entity &$entity);
+ function delete(IEntity &$entity);
/**
* Find an entity.
*
* @param mixed $id The id of the entity to find
*
- * @return Entity
+ * @return IEntity
*/
- function find($id): Entity;
+ function find($id): IEntity;
/**
* Find all entities.
*
- * @return Entity[]
+ * @return IEntity[]
*/
function findAll(): array;
@@ -89,7 +89,7 @@ function findAll(): array;
* @param int $start The starting offset.
* @param int $length The number of entities to find.
*
- * @return Entity[]
+ * @return IEntity[]
*/
function findRange(int $start, int $length): array;
diff --git a/src/LightQL/Sessions/IFacadeListener.php b/src/LightQL/Sessions/IFacadeListener.php
index 591ab44..78e0cec 100644
--- a/src/LightQL/Sessions/IFacadeListener.php
+++ b/src/LightQL/Sessions/IFacadeListener.php
@@ -32,7 +32,7 @@
namespace ElementaryFramework\LightQL\Sessions;
-use ElementaryFramework\LightQL\Entities\Entity;
+use ElementaryFramework\LightQL\Entities\IEntity;
/**
* IFacadeListener
@@ -49,48 +49,48 @@ interface IFacadeListener
/**
* An entity will be created.
*
- * @param Entity $entity The entity to create.
+ * @param IEntity $entity The entity to create.
*
* @return bool true if we can execute the query, false to cancel the entity creation.
*/
- function beforeCreate(Entity &$entity): bool;
+ function beforeCreate(IEntity &$entity): bool;
/**
* An entity was just created.
*
- * @param Entity $entity The created entity.
+ * @param IEntity $entity The created entity.
*/
- function onCreate(Entity $entity);
+ function onCreate(IEntity $entity);
/**
* An entity will be edited.
*
- * @param Entity $entity The entity to edit.
+ * @param IEntity $entity The entity to edit.
*
* @return bool true if we can execute the query, false to cancel the entity edition.
*/
- function beforeEdit(Entity &$entity): bool;
+ function beforeEdit(IEntity &$entity): bool;
/**
* An entity was just edited.
*
- * @param Entity $entity The entity to edit.
+ * @param IEntity $entity The entity to edit.
*/
- function onEdit(Entity $entity);
+ function onEdit(IEntity $entity);
/**
* An entity will be deleted.
*
- * @param Entity $entity The entity to delete.
+ * @param IEntity $entity The entity to delete.
*
* @return bool true if we can execute the query, false to cancel the entity deletion.
*/
- function beforeDelete(Entity &$entity): bool;
+ function beforeDelete(IEntity &$entity): bool;
/**
* An entity was just deleted.
*
- * @param Entity $entity The entity to delete.
+ * @param IEntity $entity The entity to delete.
*/
- function onDelete(Entity $entity);
+ function onDelete(IEntity $entity);
}
\ No newline at end of file
From 2340fb187af473ba5b44230b0da921a27ebf02b2 Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Thu, 14 Mar 2019 12:12:07 +0100
Subject: [PATCH 17/42] Fix XML persistence unit parsing
---
src/LightQL/Persistence/PersistenceUnit.php | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/src/LightQL/Persistence/PersistenceUnit.php b/src/LightQL/Persistence/PersistenceUnit.php
index 4381570..37118c4 100644
--- a/src/LightQL/Persistence/PersistenceUnit.php
+++ b/src/LightQL/Persistence/PersistenceUnit.php
@@ -86,7 +86,7 @@ class PersistenceUnit
*
* @var int
*/
- private $_port;
+ private $_port = -1;
/**
* The list of registered persistence unit files.
@@ -148,12 +148,16 @@ private function __construct(string $key)
/** @var \DOMElement $node */
foreach ($dom->documentElement->childNodes as $node) {
switch (strtolower($node->nodeName)) {
+ case "#text":
+ break;
case "dbms": $content["DBMS"] = $node->nodeValue; break;
case "hostname": $content["Hostname"] = $node->nodeValue; break;
case "database": $content["DatabaseName"] = $node->nodeValue; break;
case "username": $content["Username"] = $node->nodeValue; break;
case "password": $content["Password"] = $node->nodeValue; break;
- case "port": $content["Port"] = $node->nodeValue; break;
+ case "port":
+ $content["Port"] = intval($node->nodeValue);
+ break;
default: throw new PersistenceUnitException("Invalid persistence unit XML configuration file provided. Unknown configuration item \"{$node->nodeName}\"");
}
}
@@ -196,7 +200,7 @@ private function __construct(string $key)
? intval($content["Port"])
: null;
} else {
- throw new PersistenceUnitException('Unable to find the persistence unit with the key "' . $key . '". Have you registered this persistence unit?');
+ throw new PersistenceUnitException("Unable to find the persistence unit with the key \"{$key}\". Have you registered this persistence unit?");
}
}
From 0a71b4a84b095d7fb8b5336fb03dc57bc1fc4aac Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Thu, 14 Mar 2019 12:24:38 +0100
Subject: [PATCH 18/42] Introduce generic entities and generic facades
GenericEntity is a class which can adapt to any simple database tables.
GenericFacade is a class capable to query into any kind of table, and
generate objects of type GenericEntity.
Theses ones can be useful when the user doesn't want to write their
entities in a class-per-table model.
---
src/LightQL/Entities/GenericEntity.php | 165 +++++++++++++
src/LightQL/Sessions/GenericFacade.php | 313 +++++++++++++++++++++++++
2 files changed, 478 insertions(+)
create mode 100644 src/LightQL/Entities/GenericEntity.php
create mode 100644 src/LightQL/Sessions/GenericFacade.php
diff --git a/src/LightQL/Entities/GenericEntity.php b/src/LightQL/Entities/GenericEntity.php
new file mode 100644
index 0000000..825b252
--- /dev/null
+++ b/src/LightQL/Entities/GenericEntity.php
@@ -0,0 +1,165 @@
+
+ * @copyright 2018 Aliens Group, Inc.
+ * @license MIT
+ * @version 1.0.0
+ * @link http://lightql.na2axl.tk
+ */
+
+namespace ElementaryFramework\LightQL\Entities;
+
+/**
+ * Generic Entity
+ *
+ * Represent an entity which can adapt to any table structure.
+ *
+ * @package ElementaryFramework\LightQL\Entities
+ * @author Nana Axel
+ * @link http://lightql.na2axl.tk/docs/api/LightQL/Entities/GenericEntity
+ */
+final class GenericEntity implements IEntity
+{
+ /**
+ * The name of the table held by this entity.
+ *
+ * @var string
+ */
+ private $_name;
+
+ /**
+ * The name of the column defined as the primary key of the table, if any.
+ *
+ * @var string
+ */
+ private $_pk;
+
+ /**
+ * The collection of key-value pairs, in which keys represent columns names.
+ *
+ * @var array
+ */
+ private $_data;
+
+ /**
+ * GenericEntity constructor.
+ *
+ * @param string $name The name of the table managed by this GenericEntity.
+ * @param null|string $pk The name of the column defined as the primary key of the table, if any.
+ * @param array $data The initial values of columns in this GenericEntity.
+ */
+ public function __construct(string $name, string $pk, array $data = array())
+ {
+ $this->_name = $name;
+ $this->_pk = $pk;
+
+ $this->hydrate($data);
+ }
+
+ /**
+ * Populates data in the entity.
+ *
+ * @param array $data The raw database data.
+ */
+ public function hydrate(array $data)
+ {
+ foreach ($data as $column => $value) {
+ $this->set($column, $value);
+ }
+ }
+
+ /**
+ * Sets the raw value of a table column.
+ *
+ * @param string $column The table column name.
+ * @param mixed $value The table column value.
+ */
+ public function set(string $column, $value)
+ {
+ $this->_data[$column] = $value;
+ }
+
+ /**
+ * @param string $name The column's name.
+ *
+ * @return mixed
+ */
+ public function __get($name)
+ {
+ return $this->get($name);
+ }
+
+ /**
+ * @param string $name The column's name.
+ * @param mixed $value The value to assign.
+ */
+ public function __set($name, $value)
+ {
+ $this->set($name, $value);
+ }
+
+ /**
+ * Gets the raw value of a table column.
+ *
+ * @param string $column The table column name.
+ *
+ * @return mixed
+ */
+ public function get(string $column)
+ {
+ return array_key_exists($column, $this->_data) ? $this->_data[$column] : null;
+ }
+
+ /**
+ * Returns the name of the table managed by this GenericEntity.
+ *
+ * @return string
+ */
+ public function getName(): string
+ {
+ return $this->_name;
+ }
+
+ /**
+ * Return the data.
+ *
+ * @return array
+ */
+ public function getData(): array
+ {
+ return $this->_data;
+ }
+
+ /**
+ * Return the column name of the primary key.
+ *
+ * @return string
+ */
+ public function getPk(): string
+ {
+ return $this->_pk;
+ }
+}
\ No newline at end of file
diff --git a/src/LightQL/Sessions/GenericFacade.php b/src/LightQL/Sessions/GenericFacade.php
new file mode 100644
index 0000000..3f54c98
--- /dev/null
+++ b/src/LightQL/Sessions/GenericFacade.php
@@ -0,0 +1,313 @@
+
+ * @copyright 2018 Aliens Group, Inc.
+ * @license MIT
+ * @version 1.0.0
+ * @link http://lightql.na2axl.tk
+ */
+
+namespace ElementaryFramework\LightQL\Sessions;
+
+use ElementaryFramework\LightQL\Entities\GenericEntity;
+use ElementaryFramework\LightQL\Entities\IEntity;
+use ElementaryFramework\LightQL\Exceptions\EntityException;
+use ElementaryFramework\LightQL\Exceptions\FacadeException;
+use ElementaryFramework\LightQL\LightQL;
+use ElementaryFramework\LightQL\Persistence\PersistenceUnit;
+
+final class GenericFacade implements IFacade
+{
+ /**
+ * The persistence unit used by this instance.
+ *
+ * @var PersistenceUnit
+ */
+ private $_persistenceUnit;
+
+ /**
+ * The managed LightQL instance.
+ *
+ * @var LightQL
+ */
+ private $_lightql;
+
+ /**
+ * GenericFacade constructor.
+ *
+ * @param PersistenceUnit $persistenceUnit The persistence unit to use with this GenericFacade.
+ *
+ * @throws \ElementaryFramework\LightQL\Exceptions\LightQLException
+ */
+ public function __construct(PersistenceUnit $persistenceUnit)
+ {
+ // Save the persistence unit
+ $this->_persistenceUnit = $persistenceUnit;
+
+ // Create a LightQL instance
+ $this->_lightql = new LightQL(
+ array(
+ "dbms" => $this->_persistenceUnit->getDbms(),
+ "database" => $this->_persistenceUnit->getDatabase(),
+ "hostname" => $this->_persistenceUnit->getHostname(),
+ "username" => $this->_persistenceUnit->getUsername(),
+ "password" => $this->_persistenceUnit->getPassword(),
+ "port" => $this->_persistenceUnit->getPort()
+ )
+ );
+ }
+
+ /**
+ * Creates an entity.
+ *
+ * @param IEntity $entity The entity to create.
+ *
+ * @throws FacadeException
+ * @throws EntityException
+ */
+ public function create(IEntity &$entity)
+ {
+ if ($entity instanceof GenericEntity) {
+ $this->_lightql->beginTransaction();
+ try {
+ $this->_lightql
+ ->from($entity->getName())
+ ->insert($entity->getData());
+
+ $this->_lightql->commit();
+ } catch (\Exception $e) {
+ $this->_lightql->rollback();
+
+ throw new EntityException($e->getMessage());
+ }
+ } else {
+ throw new FacadeException("Only GenericEntity instances can be used with GenericFacade.");
+ }
+ }
+
+ /**
+ * Edit an entity.
+ *
+ * @param IEntity $entity The entity to edit.
+ * @throws EntityException
+ * @throws FacadeException
+ */
+ public function edit(IEntity &$entity)
+ {
+ if ($entity instanceof GenericEntity) {
+ $this->_lightql->beginTransaction();
+ try {
+ $this->_lightql
+ ->from($entity->getName())
+ ->where(array(
+ $entity->getPk() => $this->_lightql->quote($entity->get($entity->getPk()))
+ ))
+ ->update($entity->getData());
+
+ $this->_lightql->commit();
+ } catch (\Exception $e) {
+ $this->_lightql->rollback();
+
+ throw new EntityException($e->getMessage());
+ }
+ } else {
+ throw new FacadeException("Only GenericEntity instances can be used with GenericFacade.");
+ }
+ }
+
+ /**
+ * Delete an entity.
+ *
+ * @param IEntity $entity The entity to delete.
+ * @throws EntityException
+ * @throws FacadeException
+ */
+ function delete(IEntity &$entity)
+ {
+ if ($entity instanceof GenericEntity) {
+ $this->_lightql->beginTransaction();
+ try {
+ $this->_lightql
+ ->from($entity->getName())
+ ->where(array(
+ $entity->getPk() => $this->_lightql->quote($entity->get($entity->getPk()))
+ ))
+ ->delete();
+
+ $this->_lightql->commit();
+ } catch (\Exception $e) {
+ $this->_lightql->rollback();
+
+ throw new EntityException($e->getMessage());
+ }
+ } else {
+ throw new FacadeException("Only GenericEntity instances can be used with GenericFacade.");
+ }
+ }
+
+ /**
+ * Find an entity.
+ *
+ * This method is unavailable. Use findGeneric instead.
+ *
+ * @param mixed $id The id of the entity to find
+ *
+ * @return IEntity
+ *
+ * @throws FacadeException
+ */
+ public function find($id): IEntity
+ {
+ throw new FacadeException("The \"find\" method is unavailable in GenericFacade, use \"findGeneric\" instead.");
+ }
+
+ /**
+ * Find an entity.
+ *
+ * @param string $table The name of the table
+ * @param string $pk The name of the column with primary key property
+ * @param mixed $id The pk value of the entity to find
+ *
+ * @return IEntity
+ *
+ * @throws \ElementaryFramework\LightQL\Exceptions\LightQLException
+ */
+ public function findGeneric(string $table, string $pk, $id): IEntity
+ {
+ $raw = $this->_lightql
+ ->from($table)
+ ->where(array($pk => $this->_lightql->quote($id)))
+ ->selectFirst();
+
+ return new GenericEntity($table, $pk, $raw);
+ }
+
+ /**
+ * Find all entities.
+ *
+ * This method is unavailable. Use findGeneric instead.
+ *
+ * @return IEntity[]
+ *
+ * @throws FacadeException
+ */
+ public function findAll(): array
+ {
+ throw new FacadeException("The \"findAll\" method is unavailable in GenericFacade, use \"findAllGeneric\" instead.");
+ }
+
+ /**
+ * Find all entities.
+ *
+ * @param string $table The name of the table
+ * @param string $pk The name of the column with primary key property
+ *
+ * @return IEntity[]
+ *
+ * @throws \ElementaryFramework\LightQL\Exceptions\LightQLException
+ */
+ public function findAllGeneric(string $table, string $pk): array
+ {
+ $rawEntities = $this->_lightql
+ ->from($table)
+ ->selectArray();
+
+ return array_map(function ($raw) use ($table, $pk) {
+ return new GenericEntity($table, $pk, $raw);
+ }, $rawEntities);
+ }
+
+ /**
+ * Find all entities in the given range.
+ *
+ * This method is unavailable. Use findRangeGeneric instead.
+ *
+ * @param int $start The starting offset.
+ * @param int $length The number of entities to find.
+ *
+ * @return IEntity[]
+ *
+ * @throws FacadeException
+ */
+ public function findRange(int $start, int $length): array
+ {
+ throw new FacadeException("The \"findRange\" method is unavailable in GenericFacade, use \"findRangeGeneric\" instead.");
+ }
+
+ /**
+ * Find all entities in the given range.
+ *
+ * @param string $table The name of the table
+ * @param string $pk The name of the column with primary key property
+ * @param int $start The starting offset.
+ * @param int $length The number of entities to find.
+ *
+ * @return IEntity[]
+ *
+ * @throws \ElementaryFramework\LightQL\Exceptions\LightQLException
+ */
+ public function findRangeGeneric(string $table, string $pk, int $start, int $length): array
+ {
+ $rawEntities = $this->_lightql
+ ->from($table)
+ ->limit($start, $length)
+ ->selectArray();
+
+ return array_map(function ($raw) use ($table, $pk) {
+ return new GenericEntity($table, $pk, $raw);
+ }, $rawEntities);
+ }
+
+ /**
+ * Count the number of entities.
+ *
+ * This method is unavailable. Use countGeneric instead.
+ *
+ * @return int
+ *
+ * @throws FacadeException
+ */
+ public function count(): int
+ {
+ throw new FacadeException("The \"count\" method is unavailable in GenericFacade, use \"countGeneric\" instead.");
+ }
+
+ /**
+ * Count the number of entities.
+ *
+ * @param string $table The name of the table
+ *
+ * @return int
+ *
+ * @throws \ElementaryFramework\LightQL\Exceptions\LightQLException
+ */
+ public function countGeneric(string $table): int
+ {
+ return $this->_lightql
+ ->from($table)
+ ->count();
+ }
+}
\ No newline at end of file
From 9233b8593078f6233f2a90f7cbcb08be0bb3ad04 Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Thu, 14 Mar 2019 12:29:22 +0100
Subject: [PATCH 19/42] Update LightQL.php
---
src/LightQL/LightQL.php | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/LightQL/LightQL.php b/src/LightQL/LightQL.php
index bd92b52..4001eec 100644
--- a/src/LightQL/LightQL.php
+++ b/src/LightQL/LightQL.php
@@ -261,7 +261,7 @@ public function __construct(array $options = null)
return false;
}
} else {
- if (isset($options["port"]) && is_int($options["port"] * 1)) {
+ if (isset($options["port"]) && is_int($options["port"] * 1) && intval($options["port"]) > -1) {
$port = $options["port"];
}
@@ -620,7 +620,7 @@ protected function resetClauses()
*
* @throws \ElementaryFramework\LightQL\Exceptions\LightQLException
*
- * @return array
+ * @return array|null
*/
public function selectFirst($columns = "*")
{
@@ -805,13 +805,13 @@ public function count($columns = "*")
$getFieldsData = $this->prepare($this->_queryString);
if ($getFieldsData->execute() !== false) {
+ $this->resetClauses();
+
if (null === $this->_group) {
- $this->resetClauses();
$data = $getFieldsData->fetch();
return (int) $data['lightql_count'];
}
- $this->resetClauses();
$res = array();
while ($data = $getFieldsData->fetch()) {
From 790fd6ccbe20146f529f954108b625983da6cb5f Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Sat, 6 Apr 2019 03:41:04 +0200
Subject: [PATCH 20/42] Verify if the persistence unit file exists before parse
it.
---
src/LightQL/Persistence/PersistenceUnit.php | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/src/LightQL/Persistence/PersistenceUnit.php b/src/LightQL/Persistence/PersistenceUnit.php
index 37118c4..01ce51e 100644
--- a/src/LightQL/Persistence/PersistenceUnit.php
+++ b/src/LightQL/Persistence/PersistenceUnit.php
@@ -130,18 +130,24 @@ public static function purge()
private function __construct(string $key)
{
if (array_key_exists($key, self::$_registry)) {
- $filename = basename(self::$_registry[$key]);
+ $filepath = self::$_registry[$key];
+
+ if (!file_exists($filepath)) {
+ throw new PersistenceUnitException("The persistence unit file at the path \"{$filepath}\" cannot be found.");
+ }
+
+ $filename = basename($filepath);
$parts = explode(".", $filename);
$extension = $parts[count($parts) - 1];
$content = null;
if ($extension === "ini") {
- $content = parse_ini_file(self::$_registry[$key]);
+ $content = parse_ini_file($filepath);
} elseif ($extension === "json") {
- $content = json_decode(file_get_contents(self::$_registry[$key]), true);
+ $content = json_decode(file_get_contents($filepath), true);
} elseif ($extension === "xml") {
$dom = new \DOMDocument("1.0", "utf-8");
- $dom->loadXML(file_get_contents(self::$_registry[$key]));
+ $dom->loadXML(file_get_contents($filepath));
if ($dom->documentElement->nodeName !== "persistenceUnit") {
throw new PersistenceUnitException("Invalid persistence unit XML configuration file provided.");
} else {
From d1aa0bceb4257a3476569d78a00037d33a69ebaa Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Mon, 8 Apr 2019 01:56:01 +0200
Subject: [PATCH 21/42] Fix default nul value for port
---
src/LightQL/Persistence/PersistenceUnit.php | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/LightQL/Persistence/PersistenceUnit.php b/src/LightQL/Persistence/PersistenceUnit.php
index 01ce51e..9a1cdd9 100644
--- a/src/LightQL/Persistence/PersistenceUnit.php
+++ b/src/LightQL/Persistence/PersistenceUnit.php
@@ -202,9 +202,9 @@ private function __construct(string $key)
throw new PersistenceUnitException("Malformed persistence unit configuration file, missing the Password value.");
}
- $this->_port = array_key_exists("Port", $content)
- ? intval($content["Port"])
- : null;
+ if (array_key_exists("Port", $content)) {
+ $this->_port = intval($content["Port"]);
+ }
} else {
throw new PersistenceUnitException("Unable to find the persistence unit with the key \"{$key}\". Have you registered this persistence unit?");
}
From 817106b81f0ef0f2dfb5d754cf89b706fb41498e Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Mon, 8 Apr 2019 03:06:09 +0200
Subject: [PATCH 22/42] Resolve only public properties
---
src/LightQL/Entities/Entity.php | 2 +-
src/LightQL/Entities/EntityManager.php | 10 +++++-----
src/LightQL/Sessions/Facade.php | 2 +-
3 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/src/LightQL/Entities/Entity.php b/src/LightQL/Entities/Entity.php
index 0797404..0577feb 100644
--- a/src/LightQL/Entities/Entity.php
+++ b/src/LightQL/Entities/Entity.php
@@ -96,7 +96,7 @@ public function __construct(array $data = array())
}
$this->_reflection = new \ReflectionClass($this);
- $properties = $this->_reflection->getProperties();
+ $properties = $this->_reflection->getProperties(T_PUBLIC);
$pkFound = false;
diff --git a/src/LightQL/Entities/EntityManager.php b/src/LightQL/Entities/EntityManager.php
index c238470..152a729 100644
--- a/src/LightQL/Entities/EntityManager.php
+++ b/src/LightQL/Entities/EntityManager.php
@@ -115,7 +115,7 @@ public function find(string $entityClass, $id): array
if ($id instanceof IPrimaryKey) {
$pkClass = new \ReflectionClass($id);
- $properties = $pkClass->getProperties();
+ $properties = $pkClass->getProperties(T_PUBLIC);
/** @var \ReflectionProperty $property */
foreach ($properties as $property) {
@@ -263,14 +263,14 @@ public function merge(Entity &$entity)
$where = array();
$entityReflection = new \ReflectionClass($entity);
- $entityProperties = $entityReflection->getProperties();
+ $entityProperties = $entityReflection->getProperties(T_PUBLIC);
/** @var \ReflectionProperty $property */
foreach ($entityProperties as $property) {
$id = $entity->{$property->getName()};
if ($id instanceof IPrimaryKey) {
$propertyReflection = new \ReflectionClass($id);
- $propertyProperties = $propertyReflection->getProperties();
+ $propertyProperties = $propertyReflection->getProperties(T_PUBLIC);
foreach ($propertyProperties as $key) {
$name = Annotations::ofProperty($id, $key->getName(), "@column")[0]->name;
@@ -334,14 +334,14 @@ public function delete(Entity &$entity)
$pk = array();
$entityReflection = new \ReflectionClass($entity);
- $entityProperties = $entityReflection->getProperties();
+ $entityProperties = $entityReflection->getProperties(T_PUBLIC);
/** @var \ReflectionProperty $property */
foreach ($entityProperties as $property) {
$id = $entity->{$property->getName()};
if ($id instanceof IPrimaryKey) {
$propertyReflection = new \ReflectionClass($id);
- $propertyProperties = $propertyReflection->getProperties();
+ $propertyProperties = $propertyReflection->getProperties(T_PUBLIC);
foreach ($propertyProperties as $key) {
$name = Annotations::ofProperty($id, $key->getName(), "@column")[0]->name;
diff --git a/src/LightQL/Sessions/Facade.php b/src/LightQL/Sessions/Facade.php
index 0b2a69f..af9440f 100644
--- a/src/LightQL/Sessions/Facade.php
+++ b/src/LightQL/Sessions/Facade.php
@@ -624,7 +624,7 @@ private function _parseRawEntity($rawEntity, $annotations): Entity
$entity = $this->_class->newInstance($rawEntity);
if ($annotations[0]->fetchMode === Entity::FETCH_EAGER) {
- $properties = $this->_class->getProperties();
+ $properties = $this->_class->getProperties(T_PUBLIC);
foreach ($properties as $property) {
if (Annotations::propertyHasAnnotation($entity, $property->name, "@manyToMany")) {
From 245ccb9db0d876674c82e98918e03f6e464cc45a Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Mon, 8 Apr 2019 03:07:24 +0200
Subject: [PATCH 23/42] Use LightQL::parseValue insteadof LigthQL::quote
---
src/LightQL/Entities/EntityManager.php | 20 ++++++++++----------
src/LightQL/LightQL.php | 2 +-
src/LightQL/Sessions/Facade.php | 8 ++++----
src/LightQL/Sessions/GenericFacade.php | 6 +++---
4 files changed, 18 insertions(+), 18 deletions(-)
diff --git a/src/LightQL/Entities/EntityManager.php b/src/LightQL/Entities/EntityManager.php
index 152a729..7f0e4d6 100644
--- a/src/LightQL/Entities/EntityManager.php
+++ b/src/LightQL/Entities/EntityManager.php
@@ -121,14 +121,14 @@ public function find(string $entityClass, $id): array
foreach ($properties as $property) {
if (Annotations::propertyHasAnnotation($id, $property->getName(), "@id") && Annotations::propertyHasAnnotation($id, $property->getName(), "@column")) {
$name = Annotations::ofProperty($id, $property->getName(), "@column")[0]->name;
- $where[$name] = $this->_lightql->quote($id->{$property->getName()});
+ $where[$name] = $this->_lightql->parseValue($id->{$property->getName()});
}
}
} else {
foreach ($columns as $property => $column) {
if (count($where) === 0) {
if ($column->isPrimaryKey) {
- $where[$column->getName()] = $this->_lightql->quote($id);
+ $where[$column->getName()] = $this->_lightql->parseValue($id);
}
} else break;
}
@@ -210,14 +210,14 @@ public function persist(Entity &$entity)
/** @var Column $column */
foreach ($columns as $property => $column) {
- $value = $this->_lightql->quote($entity->get($column->getName()));
+ $value = $this->_lightql->parseValue($entity->get($column->getName()));
if ($valueValidator !== null && !$valueValidator->validate($entity, $property)) {
throw new ValueValidatorException($property);
}
if ($valueTransformer !== null) {
- $value = $this->_lightql->quote($valueTransformer->toDatabaseValue($entity, $property));
+ $value = $this->_lightql->parseValue($valueTransformer->toDatabaseValue($entity, $property));
}
$fieldAndValues[$column->getName()] = $value;
@@ -274,7 +274,7 @@ public function merge(Entity &$entity)
foreach ($propertyProperties as $key) {
$name = Annotations::ofProperty($id, $key->getName(), "@column")[0]->name;
- $where[$name] = $this->_lightql->quote($id->{$key->getName()});
+ $where[$name] = $this->_lightql->parseValue($id->{$key->getName()});
}
break;
@@ -283,20 +283,20 @@ public function merge(Entity &$entity)
/** @var Column $column */
foreach ($columns as $property => $column) {
- $value = $this->_lightql->quote($entity->get($column->getName()));
+ $value = $this->_lightql->parseValue($entity->get($column->getName()));
if ($valueValidator !== null && !$valueValidator->validate($entity, $property)) {
throw new ValueValidatorException($property);
}
if ($valueTransformer !== null) {
- $value = $this->_lightql->quote($valueTransformer->toDatabaseValue($entity, $property));
+ $value = $this->_lightql->parseValue($valueTransformer->toDatabaseValue($entity, $property));
}
$fieldAndValues[$column->getName()] = $value;
if ($column->isPrimaryKey) {
- $where[$column->getName()] = $this->_lightql->quote($entity->get($column->getName()));
+ $where[$column->getName()] = $this->_lightql->parseValue($entity->get($column->getName()));
}
}
@@ -345,7 +345,7 @@ public function delete(Entity &$entity)
foreach ($propertyProperties as $key) {
$name = Annotations::ofProperty($id, $key->getName(), "@column")[0]->name;
- $where[$name] = $this->_lightql->quote($id->{$key->getName()});
+ $where[$name] = $this->_lightql->parseValue($id->{$key->getName()});
$pk[] = $property->getName();
}
@@ -355,7 +355,7 @@ public function delete(Entity &$entity)
foreach ($columns as $property => $column) {
if ($column->isPrimaryKey) {
- $where[$column->getName()] = $this->_lightql->quote($entity->get($column->getName()));
+ $where[$column->getName()] = $this->_lightql->parseValue($entity->get($column->getName()));
$pk[] = $property;
}
}
diff --git a/src/LightQL/LightQL.php b/src/LightQL/LightQL.php
index 4001eec..962c1cd 100644
--- a/src/LightQL/LightQL.php
+++ b/src/LightQL/LightQL.php
@@ -1060,7 +1060,7 @@ public function parseValue($value): string
} elseif (is_bool($value)) {
return $value ? "1" : "0";
} else {
- return strval($value);
+ return $this->_pdo->quote(strval($value));
}
}
}
diff --git a/src/LightQL/Sessions/Facade.php b/src/LightQL/Sessions/Facade.php
index af9440f..cf0e318 100644
--- a/src/LightQL/Sessions/Facade.php
+++ b/src/LightQL/Sessions/Facade.php
@@ -401,7 +401,7 @@ private function _fetchManyToMany(&$entity, $property)
$results = $lightql
->from($manyToMany[0]->crossTable)
- ->where(array("{$manyToMany[0]->crossTable}.{$manyToMany[0]->referencedColumn}" => $lightql->quote($entity->get($column[0]->name))))
+ ->where(array("{$manyToMany[0]->crossTable}.{$manyToMany[0]->referencedColumn}" => $lightql->parseValue($entity->get($column[0]->name))))
->joinArray(
"{$referencedEntityAnnotations[0]->table}.*",
array(
@@ -445,7 +445,7 @@ private function _fetchOneToMany(&$entity, $property)
$result = $lightql
->from($referencedEntityAnnotations[0]->table)
- ->where(array("{$referencedEntityAnnotations[0]->table}.{$oneToMany[0]->referencedColumn}" => $lightql->quote($entity->get($column[0]->name))))
+ ->where(array("{$referencedEntityAnnotations[0]->table}.{$oneToMany[0]->referencedColumn}" => $lightql->parseValue($entity->get($column[0]->name))))
->selectFirst("{$referencedEntityAnnotations[0]->table}.*");
$className = $oneToMany[0]->entity;
@@ -483,7 +483,7 @@ private function _fetchManyToOne(&$entity, $property)
$results = $lightql
->from($referencedEntityAnnotations[0]->table)
- ->where(array("{$referencedEntityAnnotations[0]->table}.{$manyToOne[0]->referencedColumn}" => $lightql->quote($entity->get($column[0]->name))))
+ ->where(array("{$referencedEntityAnnotations[0]->table}.{$manyToOne[0]->referencedColumn}" => $lightql->parseValue($entity->get($column[0]->name))))
->selectArray("{$referencedEntityAnnotations[0]->table}.*");
$entity->{$property} = array_map(function($item) use ($manyToOne, $entity, $mappedPropertyName) {
@@ -520,7 +520,7 @@ private function _fetchOneToOne(&$entity, $property)
$result = $lightql
->from($referencedEntityAnnotations[0]->table)
- ->where(array("{$referencedEntityAnnotations[0]->table}.{$oneToOne[0]->referencedColumn}" => $lightql->quote($entity->get($column[0]->name))))
+ ->where(array("{$referencedEntityAnnotations[0]->table}.{$oneToOne[0]->referencedColumn}" => $lightql->parseValue($entity->get($column[0]->name))))
->selectFirst("{$referencedEntityAnnotations[0]->table}.*");
$className = $oneToOne[0]->entity;
diff --git a/src/LightQL/Sessions/GenericFacade.php b/src/LightQL/Sessions/GenericFacade.php
index 3f54c98..d00efbd 100644
--- a/src/LightQL/Sessions/GenericFacade.php
+++ b/src/LightQL/Sessions/GenericFacade.php
@@ -123,7 +123,7 @@ public function edit(IEntity &$entity)
$this->_lightql
->from($entity->getName())
->where(array(
- $entity->getPk() => $this->_lightql->quote($entity->get($entity->getPk()))
+ $entity->getPk() => $this->_lightql->parseValue($entity->get($entity->getPk()))
))
->update($entity->getData());
@@ -153,7 +153,7 @@ function delete(IEntity &$entity)
$this->_lightql
->from($entity->getName())
->where(array(
- $entity->getPk() => $this->_lightql->quote($entity->get($entity->getPk()))
+ $entity->getPk() => $this->_lightql->parseValue($entity->get($entity->getPk()))
))
->delete();
@@ -199,7 +199,7 @@ public function findGeneric(string $table, string $pk, $id): IEntity
{
$raw = $this->_lightql
->from($table)
- ->where(array($pk => $this->_lightql->quote($id)))
+ ->where(array($pk => $this->_lightql->parseValue($id)))
->selectFirst();
return new GenericEntity($table, $pk, $raw);
From 2ce45dc6fb86867df6ae1da548700a5f85e9dd5f Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Mon, 8 Apr 2019 03:24:42 +0200
Subject: [PATCH 24/42] Removes the use of parseValue in LightQL.php Now raw
ORM methods accepts only already SQL parsed values.
---
src/LightQL/LightQL.php | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/LightQL/LightQL.php b/src/LightQL/LightQL.php
index 962c1cd..12c5c7c 100644
--- a/src/LightQL/LightQL.php
+++ b/src/LightQL/LightQL.php
@@ -451,7 +451,7 @@ public function where($condition): LightQL
if (is_int($column)) {
$this->_where .= $value;
} else {
- $parts = explode(" ", $this->parseValue($value));
+ $parts = explode(" ", $value);
foreach (self::$_operators as $operator) {
if (in_array($operator, $parts, true) && $parts[0] === $operator) {
$operand = $operator;
@@ -840,7 +840,7 @@ public function insert(array $fieldsAndValues): bool
foreach ($fieldsAndValues as $column => $value) {
$columns[] = $column;
- $values[] = $this->parseValue($value);
+ $values[] = $value;
}
$column = implode(",", $columns);
@@ -907,7 +907,7 @@ public function update(array $fieldsAndValues): bool
if (is_array($fieldsAndValues)) {
foreach ($fieldsAndValues as $column => $value) {
$count--;
- $updates .= "{$column} = " . $this->parseValue($value);
+ $updates .= "{$column} = " . $value;
$updates .= ($count != 0) ? ", " : "";
}
} else {
@@ -1060,7 +1060,7 @@ public function parseValue($value): string
} elseif (is_bool($value)) {
return $value ? "1" : "0";
} else {
- return $this->_pdo->quote(strval($value));
+ return $this->quote(strval($value));
}
}
}
From 61ba49e31d8256b2ec190f5bb8cc95592b991df8 Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Mon, 8 Apr 2019 14:59:36 +0200
Subject: [PATCH 25/42] Add a method used to get the raw array of values from
database
---
src/LightQL/Entities/Entity.php | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/src/LightQL/Entities/Entity.php b/src/LightQL/Entities/Entity.php
index 0577feb..43d95b9 100644
--- a/src/LightQL/Entities/Entity.php
+++ b/src/LightQL/Entities/Entity.php
@@ -222,6 +222,16 @@ public function getColumns(): array
return $this->_columns;
}
+ /**
+ * Gets the entity as array.
+ *
+ * @return array
+ */
+ public function getRawEntity(): array
+ {
+ return $this->raw;
+ }
+
/**
* Checks if a property has the given annotation.
*
From cc85eafc1533b15dd8d503a0677f9aaba52e6701 Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Fri, 12 Apr 2019 15:41:07 +0200
Subject: [PATCH 26/42] Facades return null when no entity can be found by ID
---
src/LightQL/Entities/EntityManager.php | 22 ++++++------
src/LightQL/Sessions/Facade.php | 50 ++++++++++++++------------
src/LightQL/Sessions/GenericFacade.php | 22 +++++++-----
src/LightQL/Sessions/IFacade.php | 4 +--
4 files changed, 55 insertions(+), 43 deletions(-)
diff --git a/src/LightQL/Entities/EntityManager.php b/src/LightQL/Entities/EntityManager.php
index 7f0e4d6..cde7a68 100644
--- a/src/LightQL/Entities/EntityManager.php
+++ b/src/LightQL/Entities/EntityManager.php
@@ -33,7 +33,9 @@
namespace ElementaryFramework\LightQL\Entities;
use ElementaryFramework\Annotations\Annotations;
+use ElementaryFramework\Annotations\Exceptions\AnnotationException;
use ElementaryFramework\LightQL\Exceptions\EntityException;
+use ElementaryFramework\LightQL\Exceptions\LightQLException;
use ElementaryFramework\LightQL\Exceptions\ValueValidatorException;
use ElementaryFramework\LightQL\LightQL;
use ElementaryFramework\LightQL\Persistence\PersistenceUnit;
@@ -71,7 +73,7 @@ final class EntityManager
*
* @param PersistenceUnit $persistenceUnit The persistence unit to use in this manager.
*
- * @throws \ElementaryFramework\LightQL\Exceptions\LightQLException
+ * @throws LightQLException
*/
public function __construct(PersistenceUnit $persistenceUnit)
{
@@ -97,13 +99,13 @@ public function __construct(PersistenceUnit $persistenceUnit)
* @param string $entityClass The class name of the entity to find.
* @param mixed $id The value of the primary key.
*
- * @return array Raw data from database.
+ * @return array|null Raw data from database or null if no data.
*
- * @throws \ElementaryFramework\Annotations\Exceptions\AnnotationException
- * @throws \ElementaryFramework\LightQL\Exceptions\LightQLException
+ * @throws AnnotationException
+ * @throws LightQLException
* @throws \ReflectionException
*/
- public function find(string $entityClass, $id): array
+ public function find(string $entityClass, $id): ?array
{
$entityAnnotation = Annotations::ofClass($entityClass, "@entity");
@@ -149,7 +151,7 @@ public function find(string $entityClass, $id): array
*
* @throws EntityException
* @throws ValueValidatorException
- * @throws \ElementaryFramework\Annotations\Exceptions\AnnotationException
+ * @throws AnnotationException
* @throws \ReflectionException
*/
public function persist(Entity &$entity)
@@ -248,7 +250,7 @@ public function persist(Entity &$entity)
*
* @throws EntityException
* @throws ValueValidatorException
- * @throws \ElementaryFramework\Annotations\Exceptions\AnnotationException
+ * @throws AnnotationException
* @throws \ReflectionException
*/
public function merge(Entity &$entity)
@@ -321,7 +323,7 @@ public function merge(Entity &$entity)
* @param Entity &$entity The entity to delete.
*
* @throws EntityException
- * @throws \ElementaryFramework\Annotations\Exceptions\AnnotationException
+ * @throws AnnotationException
* @throws \ReflectionException
*/
public function delete(Entity &$entity)
@@ -396,7 +398,7 @@ public function getLightQL(): LightQL
* @param $entity
* @return IValueValidator
* @throws EntityException
- * @throws \ElementaryFramework\Annotations\Exceptions\AnnotationException
+ * @throws AnnotationException
* @throws \ReflectionException
*/
private function _getValueValidatorOfEntity($entity)
@@ -420,7 +422,7 @@ private function _getValueValidatorOfEntity($entity)
* @param $entity
* @return IValueTransformer
* @throws EntityException
- * @throws \ElementaryFramework\Annotations\Exceptions\AnnotationException
+ * @throws AnnotationException
* @throws \ReflectionException
*/
private function _getValueTransformerOfEntity($entity)
diff --git a/src/LightQL/Sessions/Facade.php b/src/LightQL/Sessions/Facade.php
index cf0e318..37dae83 100644
--- a/src/LightQL/Sessions/Facade.php
+++ b/src/LightQL/Sessions/Facade.php
@@ -43,7 +43,10 @@
use ElementaryFramework\LightQL\Entities\Query;
use ElementaryFramework\LightQL\Exceptions\EntityException;
use ElementaryFramework\LightQL\Exceptions\FacadeException;
+use ElementaryFramework\LightQL\Exceptions\LightQLException;
use ElementaryFramework\LightQL\Exceptions\OperationCancelledException;
+use ElementaryFramework\LightQL\Exceptions\PersistenceUnitException;
+use ElementaryFramework\LightQL\Exceptions\ValueValidatorException;
use ElementaryFramework\LightQL\Persistence\PersistenceUnit;
/**
@@ -88,8 +91,8 @@ abstract class Facade implements IFacade
* @throws AnnotationException When the Facade is unable to read an annotation.
* @throws EntityException When the entity class or object doesn't have an @entity annotation.
* @throws FacadeException When the entity class or object doesn't inherit from the Entity class.
- * @throws \ElementaryFramework\LightQL\Exceptions\LightQLException
- * @throws \ElementaryFramework\LightQL\Exceptions\PersistenceUnitException
+ * @throws LightQLException
+ * @throws PersistenceUnitException
* @throws \ReflectionException
*/
public function __construct($class)
@@ -167,7 +170,7 @@ public function create(IEntity &$entity)
* @throws EntityException
* @throws FacadeException When the facade is unable to edit the entity
* @throws OperationCancelledException When the operation has been cancelled by a listener
- * @throws \ElementaryFramework\LightQL\Exceptions\ValueValidatorException
+ * @throws ValueValidatorException
* @throws \ReflectionException
*/
public function edit(IEntity &$entity)
@@ -194,7 +197,7 @@ public function edit(IEntity &$entity)
* @throws EntityException
* @throws FacadeException When the facade is unable to delete the entity
* @throws OperationCancelledException When the operation has been cancelled by a listener
- * @throws \ElementaryFramework\LightQL\Exceptions\ValueValidatorException
+ * @throws ValueValidatorException
* @throws \ReflectionException
*/
public function delete(IEntity &$entity)
@@ -218,14 +221,14 @@ public function delete(IEntity &$entity)
*
* @param mixed $id The id of the entity to find
*
- * @return IEntity
+ * @return IEntity|null
*
* @throws AnnotationException
* @throws EntityException
- * @throws \ElementaryFramework\LightQL\Exceptions\LightQLException
+ * @throws LightQLException
* @throws \ReflectionException
*/
- public function find($id): IEntity
+ public function find($id): ?IEntity
{
$annotations = Annotations::ofClass($this->getEntityClassName(), "@entity");
@@ -242,7 +245,7 @@ public function find($id): IEntity
*
* @throws AnnotationException
* @throws EntityException
- * @throws \ElementaryFramework\LightQL\Exceptions\LightQLException
+ * @throws LightQLException
* @throws \ReflectionException
*/
public function findAll(): array
@@ -267,7 +270,7 @@ public function findAll(): array
*
* @throws AnnotationException
* @throws EntityException
- * @throws \ElementaryFramework\LightQL\Exceptions\LightQLException
+ * @throws LightQLException
* @throws \ReflectionException
*/
public function findRange(int $start, int $length): array
@@ -288,8 +291,8 @@ public function findRange(int $start, int $length): array
*
* @return int
*
- * @throws \ElementaryFramework\Annotations\Exceptions\AnnotationException
- * @throws \ElementaryFramework\LightQL\Exceptions\LightQLException
+ * @throws AnnotationException
+ * @throws LightQLException
*/
public function count(): int
{
@@ -329,7 +332,7 @@ public function getEntityManager(): EntityManager
* @return Query
*
* @throws FacadeException
- * @throws \ElementaryFramework\Annotations\Exceptions\AnnotationException
+ * @throws AnnotationException
*/
public function getNamedQuery(string $name): Query
{
@@ -366,8 +369,8 @@ public function getNamedQuery(string $name): Query
* @param string $property The property in many-to-many relation.
*
* @throws EntityException
- * @throws \ElementaryFramework\Annotations\Exceptions\AnnotationException
- * @throws \ElementaryFramework\LightQL\Exceptions\LightQLException
+ * @throws AnnotationException
+ * @throws LightQLException
*/
private function _fetchManyToMany(&$entity, $property)
{
@@ -427,7 +430,7 @@ private function _fetchManyToMany(&$entity, $property)
*
* @throws AnnotationException
* @throws EntityException
- * @throws \ElementaryFramework\LightQL\Exceptions\LightQLException
+ * @throws LightQLException
*/
private function _fetchOneToMany(&$entity, $property)
{
@@ -465,7 +468,7 @@ private function _fetchOneToMany(&$entity, $property)
*
* @throws AnnotationException
* @throws EntityException
- * @throws \ElementaryFramework\LightQL\Exceptions\LightQLException
+ * @throws LightQLException
*/
private function _fetchManyToOne(&$entity, $property)
{
@@ -502,7 +505,7 @@ private function _fetchManyToOne(&$entity, $property)
*
* @throws AnnotationException
* @throws EntityException
- * @throws \ElementaryFramework\LightQL\Exceptions\LightQLException
+ * @throws LightQLException
*/
private function _fetchOneToOne(&$entity, $property)
{
@@ -569,7 +572,7 @@ private function _resolveMappedPropertyName(string $entityClass, string $check,
*
* @throws AnnotationException
* @throws EntityException
- * @throws \ElementaryFramework\LightQL\Exceptions\LightQLException
+ * @throws LightQLException
* @throws \ReflectionException
*/
private function _parseRawEntities($rawEntities, $annotations): array
@@ -586,18 +589,21 @@ private function _parseRawEntities($rawEntities, $annotations): array
/**
* Parses raw data to Entity.
*
- * @param array $rawEntity Raw entity data provided from database.
+ * @param array|null $rawEntity Raw entity data provided from database.
* @param EntityAnnotation[] $annotations The set of entity annotations.
*
- * @return Entity
+ * @return Entity|null
*
* @throws AnnotationException
* @throws EntityException
- * @throws \ElementaryFramework\LightQL\Exceptions\LightQLException
+ * @throws LightQLException
* @throws \ReflectionException
*/
- private function _parseRawEntity($rawEntity, $annotations): Entity
+ private function _parseRawEntity($rawEntity, $annotations): ?Entity
{
+ if ($rawEntity === null)
+ return null;
+
/** @var IValueTransformer $valueTransformer */
$valueTransformer = null;
diff --git a/src/LightQL/Sessions/GenericFacade.php b/src/LightQL/Sessions/GenericFacade.php
index d00efbd..8736c8e 100644
--- a/src/LightQL/Sessions/GenericFacade.php
+++ b/src/LightQL/Sessions/GenericFacade.php
@@ -36,6 +36,7 @@
use ElementaryFramework\LightQL\Entities\IEntity;
use ElementaryFramework\LightQL\Exceptions\EntityException;
use ElementaryFramework\LightQL\Exceptions\FacadeException;
+use ElementaryFramework\LightQL\Exceptions\LightQLException;
use ElementaryFramework\LightQL\LightQL;
use ElementaryFramework\LightQL\Persistence\PersistenceUnit;
@@ -60,7 +61,7 @@ final class GenericFacade implements IFacade
*
* @param PersistenceUnit $persistenceUnit The persistence unit to use with this GenericFacade.
*
- * @throws \ElementaryFramework\LightQL\Exceptions\LightQLException
+ * @throws LightQLException
*/
public function __construct(PersistenceUnit $persistenceUnit)
{
@@ -175,11 +176,11 @@ function delete(IEntity &$entity)
*
* @param mixed $id The id of the entity to find
*
- * @return IEntity
+ * @return IEntity|null
*
* @throws FacadeException
*/
- public function find($id): IEntity
+ public function find($id): ?IEntity
{
throw new FacadeException("The \"find\" method is unavailable in GenericFacade, use \"findGeneric\" instead.");
}
@@ -191,17 +192,20 @@ public function find($id): IEntity
* @param string $pk The name of the column with primary key property
* @param mixed $id The pk value of the entity to find
*
- * @return IEntity
+ * @return IEntity|null
*
- * @throws \ElementaryFramework\LightQL\Exceptions\LightQLException
+ * @throws LightQLException
*/
- public function findGeneric(string $table, string $pk, $id): IEntity
+ public function findGeneric(string $table, string $pk, $id): ?IEntity
{
$raw = $this->_lightql
->from($table)
->where(array($pk => $this->_lightql->parseValue($id)))
->selectFirst();
+ if ($raw === null)
+ return null;
+
return new GenericEntity($table, $pk, $raw);
}
@@ -227,7 +231,7 @@ public function findAll(): array
*
* @return IEntity[]
*
- * @throws \ElementaryFramework\LightQL\Exceptions\LightQLException
+ * @throws LightQLException
*/
public function findAllGeneric(string $table, string $pk): array
{
@@ -267,7 +271,7 @@ public function findRange(int $start, int $length): array
*
* @return IEntity[]
*
- * @throws \ElementaryFramework\LightQL\Exceptions\LightQLException
+ * @throws LightQLException
*/
public function findRangeGeneric(string $table, string $pk, int $start, int $length): array
{
@@ -302,7 +306,7 @@ public function count(): int
*
* @return int
*
- * @throws \ElementaryFramework\LightQL\Exceptions\LightQLException
+ * @throws LightQLException
*/
public function countGeneric(string $table): int
{
diff --git a/src/LightQL/Sessions/IFacade.php b/src/LightQL/Sessions/IFacade.php
index f090b6f..98eea81 100644
--- a/src/LightQL/Sessions/IFacade.php
+++ b/src/LightQL/Sessions/IFacade.php
@@ -72,9 +72,9 @@ function delete(IEntity &$entity);
*
* @param mixed $id The id of the entity to find
*
- * @return IEntity
+ * @return IEntity|null
*/
- function find($id): IEntity;
+ function find($id): ?IEntity;
/**
* Find all entities.
From 231dc999928ba1bfc5356a82a3a8a25402d1ab2b Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Sat, 13 Apr 2019 15:03:17 +0200
Subject: [PATCH 27/42] Prioritize the property value and fallback to the raw
value
---
src/LightQL/Entities/Entity.php | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/src/LightQL/Entities/Entity.php b/src/LightQL/Entities/Entity.php
index 43d95b9..4188a7e 100644
--- a/src/LightQL/Entities/Entity.php
+++ b/src/LightQL/Entities/Entity.php
@@ -167,11 +167,6 @@ public function hydrate(array $data)
*/
public function get(string $column)
{
- // Try to get the raw value
- if ($this->_exists($column)) {
- return $this->raw[$column];
- }
-
// Try to get the property value
/** @var Column $c */
foreach ($this->_columns as $property => $c) {
@@ -196,6 +191,11 @@ public function get(string $column)
}
}
+ // Try to get the raw value
+ if ($this->_exists($column)) {
+ return $this->raw[$column];
+ }
+
// The value definitively doesn't exist
return null;
}
From 5caacdc1ae115a6662e22824a3e0da9eb2fc8784 Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Sat, 13 Apr 2019 15:04:08 +0200
Subject: [PATCH 28/42] Fix return type
---
src/LightQL/Sessions/Facade.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/LightQL/Sessions/Facade.php b/src/LightQL/Sessions/Facade.php
index 37dae83..4406e57 100644
--- a/src/LightQL/Sessions/Facade.php
+++ b/src/LightQL/Sessions/Facade.php
@@ -545,7 +545,7 @@ private function _fetchOneToOne(&$entity, $property)
*
* @return string|null
*/
- private function _resolveMappedPropertyName(string $entityClass, string $check, string $column): string
+ private function _resolveMappedPropertyName(string $entityClass, string $check, string $column): ?string
{
$mappedPropertyName = null;
From b071d1ba1d741960a748d1242ff5c18aeb5caa83 Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Sat, 13 Apr 2019 17:00:29 +0200
Subject: [PATCH 29/42] Fallback on the raw value when the property value is
null
---
src/LightQL/Entities/Entity.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/LightQL/Entities/Entity.php b/src/LightQL/Entities/Entity.php
index 4188a7e..6ab1dd9 100644
--- a/src/LightQL/Entities/Entity.php
+++ b/src/LightQL/Entities/Entity.php
@@ -185,7 +185,7 @@ public function get(string $column)
$referencedColumn = $this->_getMetadata($property, "@oneToOne", "referencedColumn");
return $this->{$property}->get($referencedColumn);
}
- } else {
+ } elseif ($this->{$property} !== null) {
return $this->{$property};
}
}
From a2e7f6b5bcf9e62774d49a731ba9f94095177396 Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Mon, 15 Apr 2019 12:44:37 +0100
Subject: [PATCH 30/42] Manage complexes primary key (IPrimaryKey) when
persisting an entity into the database
---
src/LightQL/Annotations/PkClassAnnotation.php | 108 ++++++++++++++++++
src/LightQL/Entities/EntityManager.php | 27 +++++
src/LightQL/LightQL.php | 2 +
3 files changed, 137 insertions(+)
create mode 100644 src/LightQL/Annotations/PkClassAnnotation.php
diff --git a/src/LightQL/Annotations/PkClassAnnotation.php b/src/LightQL/Annotations/PkClassAnnotation.php
new file mode 100644
index 0000000..60c416a
--- /dev/null
+++ b/src/LightQL/Annotations/PkClassAnnotation.php
@@ -0,0 +1,108 @@
+
+ * @copyright 2018 Aliens Group, Inc.
+ * @license MIT
+ * @version 1.0.0
+ * @link http://lightql.na2axl.tk
+ */
+
+namespace ElementaryFramework\LightQL\Annotations;
+
+use ElementaryFramework\Annotations\Annotation;
+use ElementaryFramework\Annotations\AnnotationFile;
+use ElementaryFramework\Annotations\Exceptions\AnnotationException;
+use ElementaryFramework\Annotations\IAnnotationFileAware;
+
+/**
+ * Pk Class Annotation
+ *
+ * Used to define the primary key class of an entity.
+ *
+ * This annotation have to be associated with the @entity
+ * annotation to take effect.
+ *
+ * The entity class with this annotation must have a
+ * property with the @id annotation.
+ *
+ * @usage('class' => true, 'inherited' => true)
+ *
+ * @category Annotations
+ * @package LightQL
+ * @author Nana Axel
+ * @link http://lightql.na2axl.tk/docs/api/LightQL/Annotations/PkClassAnnotation
+ */
+class PkClassAnnotation extends Annotation implements IAnnotationFileAware
+{
+ /**
+ * Specify the class name to use as the primary key class
+ * of the current entity.
+ *
+ * @var string
+ */
+ public $name;
+
+ /**
+ * Annotation file.
+ *
+ * @var AnnotationFile
+ */
+ protected $file;
+
+ /**
+ * Initialize the annotation.
+ *
+ * @param array $properties The array of annotation properties
+ *
+ * @throws AnnotationException
+ *
+ * @return void
+ */
+ public function initAnnotation(array $properties)
+ {
+ $this->map($properties, array("name"));
+
+ parent::initAnnotation($properties);
+
+ if (!isset($this->name)) {
+ throw new AnnotationException(self::class . " must have a \"name\" property");
+ }
+
+ $this->name = $this->file->resolveType($this->name);
+ }
+
+ /**
+ * Provides information about file, that contains this annotation.
+ *
+ * @param AnnotationFile $file Annotation file.
+ *
+ * @return void
+ */
+ public function setAnnotationFile(AnnotationFile $file)
+ {
+ $this->file = $file;
+ }
+}
diff --git a/src/LightQL/Entities/EntityManager.php b/src/LightQL/Entities/EntityManager.php
index cde7a68..a60d861 100644
--- a/src/LightQL/Entities/EntityManager.php
+++ b/src/LightQL/Entities/EntityManager.php
@@ -177,6 +177,19 @@ public function persist(Entity &$entity)
}
}
+ if ($idProperty === null) {
+ $entityReflection = new \ReflectionClass($entity);
+ $entityProperties = $entityReflection->getProperties(T_PUBLIC);
+
+ /** @var \ReflectionProperty $property */
+ foreach ($entityProperties as $property) {
+ if (Annotations::propertyHasAnnotation($entity, $property->name, "@id")) {
+ $idProperty = $property->name;
+ break;
+ }
+ }
+ }
+
if ($idProperty !== null && ($autoIncrementProperty === null || $autoIncrementProperty !== $idProperty)) {
// We have a non auto incremented primary key...
// Check if the value is null or not set
@@ -210,6 +223,20 @@ public function persist(Entity &$entity)
}
}
+ if (Annotations::classHasAnnotation($entity, "@pkClass")) {
+ $pkClassName = Annotations::ofClass($entity, "@pkClass")[0]->name;
+ $pkClassReflection = new \ReflectionClass($pkClassName);
+ $pkClassProperties = $pkClassReflection->getProperties(T_PUBLIC);
+
+ /** @var \ReflectionProperty $property */
+ foreach ($pkClassProperties as $property) {
+ if (Annotations::propertyHasAnnotation($pkClassName, $property->name, "@column")) {
+ $columnAnnotations = Annotations::ofProperty($pkClassName, $property->name, "@column");
+ $fieldAndValues[$columnAnnotations[0]->name] = $entity->{$idProperty}->{$property->name};
+ }
+ }
+ }
+
/** @var Column $column */
foreach ($columns as $property => $column) {
$value = $this->_lightql->parseValue($entity->get($column->getName()));
diff --git a/src/LightQL/LightQL.php b/src/LightQL/LightQL.php
index 12c5c7c..eb21804 100644
--- a/src/LightQL/LightQL.php
+++ b/src/LightQL/LightQL.php
@@ -46,6 +46,7 @@
use ElementaryFramework\LightQL\Annotations\OneToManyAnnotation;
use ElementaryFramework\LightQL\Annotations\OneToOneAnnotation;
use ElementaryFramework\LightQL\Annotations\PersistenceUnitAnnotation;
+use ElementaryFramework\LightQL\Annotations\PkClassAnnotation;
use ElementaryFramework\LightQL\Annotations\SizeAnnotation;
use ElementaryFramework\LightQL\Annotations\TransformerAnnotation;
use ElementaryFramework\LightQL\Annotations\UniqueAnnotation;
@@ -89,6 +90,7 @@ public static function registerAnnotations()
$manager->registerAnnotation("oneToMany", OneToManyAnnotation::class);
$manager->registerAnnotation("oneToOne", OneToOneAnnotation::class);
$manager->registerAnnotation("persistenceUnit", PersistenceUnitAnnotation::class);
+ $manager->registerAnnotation("pkClass", PkClassAnnotation::class);
$manager->registerAnnotation("size", SizeAnnotation::class);
$manager->registerAnnotation("transformer", TransformerAnnotation::class);
$manager->registerAnnotation("unique", UniqueAnnotation::class);
From 957c1712b693d7bde8b356e56b67205b11729c69 Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Mon, 15 Apr 2019 12:49:45 +0100
Subject: [PATCH 31/42] Add a way to better manage and recognize the
IPrimaryKey implementation of an entity
---
src/LightQL/Sessions/Facade.php | 32 +++++++++++++++++++++++++++++++-
1 file changed, 31 insertions(+), 1 deletion(-)
diff --git a/src/LightQL/Sessions/Facade.php b/src/LightQL/Sessions/Facade.php
index 4406e57..4e76cd9 100644
--- a/src/LightQL/Sessions/Facade.php
+++ b/src/LightQL/Sessions/Facade.php
@@ -39,6 +39,7 @@
use ElementaryFramework\LightQL\Entities\Entity;
use ElementaryFramework\LightQL\Entities\EntityManager;
use ElementaryFramework\LightQL\Entities\IEntity;
+use ElementaryFramework\LightQL\Entities\IPrimaryKey;
use ElementaryFramework\LightQL\Entities\IValueTransformer;
use ElementaryFramework\LightQL\Entities\Query;
use ElementaryFramework\LightQL\Exceptions\EntityException;
@@ -626,6 +627,33 @@ private function _parseRawEntity($rawEntity, $annotations): ?Entity
unset($value);
}
+ /** @var IPrimaryKey $pkClass */
+ $pkClassReflection = null;
+ $pkClass = null;
+
+ if (Annotations::classHasAnnotation($this->getEntityClassName(), "@pkClass")) {
+ $pkClassAnnotation = Annotations::ofClass($this->getEntityClassName(), "@pkClass");
+
+ if (\is_subclass_of($pkClassAnnotation[0]->name, IPrimaryKey::class)) {
+ $pkClassReflection = new \ReflectionClass($pkClassAnnotation[0]->name);
+
+ $pkClass = $pkClassReflection->newInstance();
+ } else {
+ throw new EntityException("The primary key class of this entity doesn't implement the IPrimaryKey interface");
+ }
+ }
+
+ if ($pkClass !== null) {
+ $properties = $pkClassReflection->getProperties(T_PUBLIC);
+
+ foreach ($properties as $property) {
+ if (Annotations::propertyHasAnnotation($pkClass, $property->name, "@column")) {
+ $columnAnnotations = Annotations::ofProperty($pkClass, $property->name, "@column");
+ $pkClass->{$property->name} = $rawEntity[$columnAnnotations[0]->name];
+ }
+ }
+ }
+
/** @var Entity $entity */
$entity = $this->_class->newInstance($rawEntity);
@@ -633,7 +661,9 @@ private function _parseRawEntity($rawEntity, $annotations): ?Entity
$properties = $this->_class->getProperties(T_PUBLIC);
foreach ($properties as $property) {
- if (Annotations::propertyHasAnnotation($entity, $property->name, "@manyToMany")) {
+ if (Annotations::propertyHasAnnotation($entity, $property->name, "@id") && $pkClass !== null) {
+ $entity->{$property->name} = $pkClass;
+ } elseif (Annotations::propertyHasAnnotation($entity, $property->name, "@manyToMany")) {
$this->_fetchManyToMany($entity, $property->name);
} elseif (Annotations::propertyHasAnnotation($entity, $property->name, "@oneToMany")) {
$this->_fetchOneToMany($entity, $property->name);
From 6122e4818d71d857eeed5b59e1cda2c8052689ed Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Tue, 16 Apr 2019 10:03:23 +0100
Subject: [PATCH 32/42] Add a type check to ensure that Facade instances are
used only with Entity instances
---
src/LightQL/Sessions/Facade.php | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/src/LightQL/Sessions/Facade.php b/src/LightQL/Sessions/Facade.php
index 4e76cd9..91c221f 100644
--- a/src/LightQL/Sessions/Facade.php
+++ b/src/LightQL/Sessions/Facade.php
@@ -132,6 +132,10 @@ public function __construct($class)
*/
public function create(IEntity &$entity)
{
+ if (!($entity instanceof Entity)) {
+ throw new EntityException("The Facade class works only with classes extending Entity.");
+ }
+
if (!$this->_class->isInstance($entity)) {
throw new FacadeException("Cannot create entity. The type of the entity is not valid for this facade.");
}
@@ -176,6 +180,10 @@ public function create(IEntity &$entity)
*/
public function edit(IEntity &$entity)
{
+ if (!($entity instanceof Entity)) {
+ throw new EntityException("The Facade class works only with classes extending Entity.");
+ }
+
if (!$this->_class->isInstance($entity)) {
throw new FacadeException("Cannot edit entity. The type of the entity is not valid for this facade.");
}
@@ -203,6 +211,10 @@ public function edit(IEntity &$entity)
*/
public function delete(IEntity &$entity)
{
+ if (!($entity instanceof Entity)) {
+ throw new EntityException("The Facade class works only with classes extending Entity.");
+ }
+
if (!$this->_class->isInstance($entity)) {
throw new FacadeException("Cannot delete entity. The type of the entity is not valid for this facade.");
}
@@ -394,7 +406,7 @@ private function _fetchManyToMany(&$entity, $property)
unset($referencedEntity);
if ($mappedPropertyName === null) {
- throw new EntityException("Unable to find a suitable property with a @manyToMany annotation in the entity \"$manyToMany[0]->entity\".");
+ throw new EntityException("Unable to find a suitable property with a @manyToMany annotation in the entity \"{$manyToMany[0]->entity}\".");
}
$mappedPropertyManyToManyAnnotation = Annotations::ofProperty($manyToMany[0]->entity, $mappedPropertyName, "@manyToMany");
From 723fb8e31422ae68480ee78596f1cc36a6c026e8 Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Wed, 17 Apr 2019 10:53:48 +0100
Subject: [PATCH 33/42] Skip the creation of transaction when another already
exists.
This allow the user to create and manage their own transactions without
get blocked by LightQL.
---
src/LightQL/Entities/EntityManager.php | 45 ++++++++++++++++++++------
src/LightQL/LightQL.php | 12 +++++++
2 files changed, 48 insertions(+), 9 deletions(-)
diff --git a/src/LightQL/Entities/EntityManager.php b/src/LightQL/Entities/EntityManager.php
index a60d861..5e66f14 100644
--- a/src/LightQL/Entities/EntityManager.php
+++ b/src/LightQL/Entities/EntityManager.php
@@ -252,7 +252,12 @@ public function persist(Entity &$entity)
$fieldAndValues[$column->getName()] = $value;
}
- $this->_lightql->beginTransaction();
+ $inTransaction = $this->_lightql->inTransaction();
+
+ if (!$inTransaction) {
+ $this->_lightql->beginTransaction();
+ }
+
try {
$this->_lightql
->from($entityAnnotation[0]->table)
@@ -262,9 +267,13 @@ public function persist(Entity &$entity)
$entity->{$autoIncrementProperty} = $this->_lightql->lastInsertID();
}
- $this->_lightql->commit();
+ if (!$inTransaction) {
+ $this->_lightql->commit();
+ }
} catch (\Exception $e) {
- $this->_lightql->rollback();
+ if (!$inTransaction) {
+ $this->_lightql->rollback();
+ }
throw new EntityException($e->getMessage());
}
@@ -329,16 +338,25 @@ public function merge(Entity &$entity)
}
}
- $this->_lightql->beginTransaction();
+ $inTransaction = $this->_lightql->inTransaction();
+
+ if (!$inTransaction) {
+ $this->_lightql->beginTransaction();
+ }
+
try {
$this->_lightql
->from($entityAnnotation[0]->table)
->where($where)
->update($fieldAndValues);
- $this->_lightql->commit();
+ if (!$inTransaction) {
+ $this->_lightql->commit();
+ }
} catch (\Exception $e) {
- $this->_lightql->rollback();
+ if (!$inTransaction) {
+ $this->_lightql->rollback();
+ }
throw new EntityException($e->getMessage());
}
@@ -389,7 +407,12 @@ public function delete(Entity &$entity)
}
}
- $this->_lightql->beginTransaction();
+ $inTransaction = $this->_lightql->inTransaction();
+
+ if (!$inTransaction) {
+ $this->_lightql->beginTransaction();
+ }
+
try {
$this->_lightql
->from($entityAnnotation[0]->table)
@@ -402,9 +425,13 @@ public function delete(Entity &$entity)
}
}
- $this->_lightql->commit();
+ if (!$inTransaction) {
+ $this->_lightql->commit();
+ }
} catch (\Exception $e) {
- $this->_lightql->rollback();
+ if (!$inTransaction) {
+ $this->_lightql->rollback();
+ }
throw new EntityException($e->getMessage());
}
diff --git a/src/LightQL/LightQL.php b/src/LightQL/LightQL.php
index eb21804..21fe123 100644
--- a/src/LightQL/LightQL.php
+++ b/src/LightQL/LightQL.php
@@ -1012,6 +1012,18 @@ public function quote($value): string
return $this->_pdo->quote($value);
}
+ /**
+ * Checks if we are currently inside a transaction.
+ *
+ * @uses \PDO::inTransaction()
+ *
+ * @return bool
+ */
+ public function inTransaction(): bool
+ {
+ return $this->_pdo->inTransaction();
+ }
+
/**
* Disable auto commit mode and start a transaction.
*
From 12757bc40237b25014c38fcd107674fe20463c55 Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Wed, 17 Apr 2019 12:21:54 +0100
Subject: [PATCH 34/42] Skip many-to-one properties
---
src/LightQL/Entities/Entity.php | 2 +-
src/LightQL/Entities/EntityManager.php | 8 ++++++++
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/src/LightQL/Entities/Entity.php b/src/LightQL/Entities/Entity.php
index 6ab1dd9..9e36dce 100644
--- a/src/LightQL/Entities/Entity.php
+++ b/src/LightQL/Entities/Entity.php
@@ -185,7 +185,7 @@ public function get(string $column)
$referencedColumn = $this->_getMetadata($property, "@oneToOne", "referencedColumn");
return $this->{$property}->get($referencedColumn);
}
- } elseif ($this->{$property} !== null) {
+ } elseif (!$c->isManyToOne && $this->{$property} !== null) {
return $this->{$property};
}
}
diff --git a/src/LightQL/Entities/EntityManager.php b/src/LightQL/Entities/EntityManager.php
index 5e66f14..4de0374 100644
--- a/src/LightQL/Entities/EntityManager.php
+++ b/src/LightQL/Entities/EntityManager.php
@@ -239,6 +239,10 @@ public function persist(Entity &$entity)
/** @var Column $column */
foreach ($columns as $property => $column) {
+ if ($column->isManyToOne) {
+ continue;
+ }
+
$value = $this->_lightql->parseValue($entity->get($column->getName()));
if ($valueValidator !== null && !$valueValidator->validate($entity, $property)) {
@@ -321,6 +325,10 @@ public function merge(Entity &$entity)
/** @var Column $column */
foreach ($columns as $property => $column) {
+ if ($column->isManyToOne) {
+ continue;
+ }
+
$value = $this->_lightql->parseValue($entity->get($column->getName()));
if ($valueValidator !== null && !$valueValidator->validate($entity, $property)) {
From 0b2c3408817918f379a2a419294db4e09f77e819 Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Thu, 18 Apr 2019 11:57:44 +0100
Subject: [PATCH 35/42] Ensure only one connection is opened for each
persistence unit keys.
This reduce considerably the load time and the number of connections per
queries.
---
src/LightQL/Entities/EntityManager.php | 35 ++++++++++++++-------
src/LightQL/Persistence/PersistenceUnit.php | 19 +++++++++++
2 files changed, 43 insertions(+), 11 deletions(-)
diff --git a/src/LightQL/Entities/EntityManager.php b/src/LightQL/Entities/EntityManager.php
index 4de0374..b8d3348 100644
--- a/src/LightQL/Entities/EntityManager.php
+++ b/src/LightQL/Entities/EntityManager.php
@@ -52,6 +52,13 @@
*/
final class EntityManager
{
+ /**
+ * The array of existing LightQL connections.
+ *
+ * @var array
+ */
+ private static $_connections = array();
+
/**
* The persistence unit of this entity
* manager.
@@ -80,17 +87,23 @@ public function __construct(PersistenceUnit $persistenceUnit)
// Save the persistence unit
$this->_persistenceUnit = $persistenceUnit;
- // Create a LightQL instance
- $this->_lightql = new LightQL(
- array(
- "dbms" => $this->_persistenceUnit->getDbms(),
- "database" => $this->_persistenceUnit->getDatabase(),
- "hostname" => $this->_persistenceUnit->getHostname(),
- "username" => $this->_persistenceUnit->getUsername(),
- "password" => $this->_persistenceUnit->getPassword(),
- "port" => $this->_persistenceUnit->getPort()
- )
- );
+ if (!array_key_exists($persistenceUnit->getKey(), static::$_connections)) {
+ // Create a LightQL instance
+ $this->_lightql = new LightQL(
+ array(
+ "dbms" => $this->_persistenceUnit->getDbms(),
+ "database" => $this->_persistenceUnit->getDatabase(),
+ "hostname" => $this->_persistenceUnit->getHostname(),
+ "username" => $this->_persistenceUnit->getUsername(),
+ "password" => $this->_persistenceUnit->getPassword(),
+ "port" => $this->_persistenceUnit->getPort()
+ )
+ );
+
+ static::$_connections[$persistenceUnit->getKey()] = $this->_lightql;
+ } else {
+ $this->_lightql = static::$_connections[$persistenceUnit->getKey()];
+ }
}
/**
diff --git a/src/LightQL/Persistence/PersistenceUnit.php b/src/LightQL/Persistence/PersistenceUnit.php
index 9a1cdd9..c1a182e 100644
--- a/src/LightQL/Persistence/PersistenceUnit.php
+++ b/src/LightQL/Persistence/PersistenceUnit.php
@@ -46,6 +46,13 @@
*/
class PersistenceUnit
{
+ /**
+ * The key used to create this persistence unit.
+ *
+ * @var string
+ */
+ private $_key;
+
/**
* The DBMS.
*
@@ -130,6 +137,8 @@ public static function purge()
private function __construct(string $key)
{
if (array_key_exists($key, self::$_registry)) {
+ $this->_key = $key;
+
$filepath = self::$_registry[$key];
if (!file_exists($filepath)) {
@@ -227,6 +236,16 @@ public static function create(string $key): PersistenceUnit
}
}
+ /**
+ * Returns the persistence unit key.
+ *
+ * @return string
+ */
+ public function getKey(): string
+ {
+ return $this->_key;
+ }
+
/**
* Returns the DBMS.
*
From 2e742cc3ef498d62abe879625cc4d310ee4c3646 Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Thu, 16 May 2019 12:48:35 +0200
Subject: [PATCH 36/42] Update values of the created entity with database
values.
This will maintain the entity synchronized with the database at the
creation time before any modification.
---
src/LightQL/Entities/Entity.php | 3 ++
src/LightQL/Entities/EntityManager.php | 60 +++++++++++++++-------
src/LightQL/Exceptions/EntityException.php | 6 +++
src/LightQL/Sessions/Facade.php | 19 ++-----
4 files changed, 55 insertions(+), 33 deletions(-)
diff --git a/src/LightQL/Entities/Entity.php b/src/LightQL/Entities/Entity.php
index 9e36dce..b74bcc7 100644
--- a/src/LightQL/Entities/Entity.php
+++ b/src/LightQL/Entities/Entity.php
@@ -153,6 +153,9 @@ public function hydrate(array $data)
} elseif (\is_null($this->{$property}) || $this->{$property} === null) {
$this->{$property} = $column->getDefault();
}
+ } elseif (array_key_exists($column->getName(), $this->raw)) {
+ // TODO: Find a way to fill the property with the good value
+ $this->{$property} = null;
}
}
}
diff --git a/src/LightQL/Entities/EntityManager.php b/src/LightQL/Entities/EntityManager.php
index b8d3348..aca05ee 100644
--- a/src/LightQL/Entities/EntityManager.php
+++ b/src/LightQL/Entities/EntityManager.php
@@ -162,6 +162,8 @@ public function find(string $entityClass, $id): ?array
*
* @param Entity &$entity The entity to create.
*
+ * @return array
+ *
* @throws EntityException
* @throws ValueValidatorException
* @throws AnnotationException
@@ -236,20 +238,6 @@ public function persist(Entity &$entity)
}
}
- if (Annotations::classHasAnnotation($entity, "@pkClass")) {
- $pkClassName = Annotations::ofClass($entity, "@pkClass")[0]->name;
- $pkClassReflection = new \ReflectionClass($pkClassName);
- $pkClassProperties = $pkClassReflection->getProperties(T_PUBLIC);
-
- /** @var \ReflectionProperty $property */
- foreach ($pkClassProperties as $property) {
- if (Annotations::propertyHasAnnotation($pkClassName, $property->name, "@column")) {
- $columnAnnotations = Annotations::ofProperty($pkClassName, $property->name, "@column");
- $fieldAndValues[$columnAnnotations[0]->name] = $entity->{$idProperty}->{$property->name};
- }
- }
- }
-
/** @var Column $column */
foreach ($columns as $property => $column) {
if ($column->isManyToOne) {
@@ -269,6 +257,20 @@ public function persist(Entity &$entity)
$fieldAndValues[$column->getName()] = $value;
}
+ if (Annotations::classHasAnnotation($entity, "@pkClass")) {
+ $pkClassName = Annotations::ofClass($entity, "@pkClass")[0]->name;
+ $pkClassReflection = new \ReflectionClass($pkClassName);
+ $pkClassProperties = $pkClassReflection->getProperties(T_PUBLIC);
+
+ /** @var \ReflectionProperty $property */
+ foreach ($pkClassProperties as $property) {
+ if (Annotations::propertyHasAnnotation($pkClassName, $property->name, "@column")) {
+ $columnAnnotations = Annotations::ofProperty($pkClassName, $property->name, "@column");
+ $fieldAndValues[$columnAnnotations[0]->name] = $this->_lightql->parseValue($entity->{$idProperty}->{$property->name});
+ }
+ }
+ }
+
$inTransaction = $this->_lightql->inTransaction();
if (!$inTransaction) {
@@ -280,10 +282,6 @@ public function persist(Entity &$entity)
->from($entityAnnotation[0]->table)
->insert($fieldAndValues);
- if ($autoIncrementProperty !== null) {
- $entity->{$autoIncrementProperty} = $this->_lightql->lastInsertID();
- }
-
if (!$inTransaction) {
$this->_lightql->commit();
}
@@ -292,8 +290,32 @@ public function persist(Entity &$entity)
$this->_lightql->rollback();
}
- throw new EntityException($e->getMessage());
+ throw new EntityException("Unable to persist the entity. See internal exception to learn more.", 0, $e);
+ }
+
+ $where = array();
+ if ($autoIncrementProperty !== null) {
+ $where[$columns[$autoIncrementProperty]->getName()] = $this->_lightql->lastInsertID();
+ } elseif ($entity->{$idProperty} instanceof IPrimaryKey) {
+ $pkClassName = Annotations::ofClass($entity, "@pkClass")[0]->name;
+ $pkClassReflection = new \ReflectionClass($pkClassName);
+ $pkClassProperties = $pkClassReflection->getProperties(T_PUBLIC);
+
+ /** @var \ReflectionProperty $property */
+ foreach ($pkClassProperties as $property) {
+ if (Annotations::propertyHasAnnotation($pkClassName, $property->name, "@column")) {
+ $columnAnnotations = Annotations::ofProperty($pkClassName, $property->name, "@column");
+ $where[$columnAnnotations[0]->name] = $this->_lightql->parseValue($entity->{$idProperty}->{$property->name});
+ }
+ }
+ } else {
+ $where[$columns[$idProperty]->getName()] = $this->_lightql->parseValue($entity->{$idProperty});
}
+
+ return $this->_lightql
+ ->from($entityAnnotation[0]->table)
+ ->where($where)
+ ->selectFirst();
}
/**
diff --git a/src/LightQL/Exceptions/EntityException.php b/src/LightQL/Exceptions/EntityException.php
index 6e6863c..0f9ad94 100644
--- a/src/LightQL/Exceptions/EntityException.php
+++ b/src/LightQL/Exceptions/EntityException.php
@@ -32,6 +32,8 @@
namespace ElementaryFramework\LightQL\Exceptions;
+use Throwable;
+
/**
* Entity Exception
*
@@ -42,4 +44,8 @@
*/
class EntityException extends \Exception
{
+ public function __construct($message = "", $code = 0, Throwable $previous = null)
+ {
+ parent::__construct($message, $code, $previous);
+ }
}
diff --git a/src/LightQL/Sessions/Facade.php b/src/LightQL/Sessions/Facade.php
index 91c221f..8080833 100644
--- a/src/LightQL/Sessions/Facade.php
+++ b/src/LightQL/Sessions/Facade.php
@@ -129,6 +129,7 @@ public function __construct($class)
* @throws AnnotationException
* @throws FacadeException When the facade is unable to create the entity.
* @throws OperationCancelledException When the operation has been cancelled by a listener
+ * @throws EntityException When the $entity object is not an instance of Entity class
*/
public function create(IEntity &$entity)
{
@@ -145,20 +146,10 @@ public function create(IEntity &$entity)
}
try {
- $this->entityManager->persist($entity);
-
- $columns = $entity->getColumns();
- foreach ($columns as $property => $column) {
- if ($column->isOneToMany) {
- $this->_fetchOneToMany($entity, $property);
- } elseif ($column->isManyToOne) {
- $this->_fetchManyToOne($entity, $property);
- } elseif ($column->isManyToMany) {
- $this->_fetchManyToMany($entity, $property);
- } elseif ($column->isOneToOne) {
- $this->_fetchOneToOne($entity, $property);
- }
- }
+ $entity = $this->_parseRawEntity(
+ $this->entityManager->persist($entity),
+ Annotations::ofClass($this->getEntityClassName(), "@entity")
+ );
$this->_listener instanceof IFacadeListener && $this->_listener->onCreate($entity);
} catch (\Exception $e) {
From fbc1151330d26a147e675c387c8dab1c142b4995 Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Mon, 29 Jul 2019 11:24:23 +0100
Subject: [PATCH 37/42] Make LightQL transform values at hydrate time
---
src/LightQL/Entities/Entity.php | 77 +++++++++++++++++++++++++-
src/LightQL/Entities/EntityManager.php | 56 ++-----------------
src/LightQL/Sessions/Facade.php | 22 --------
3 files changed, 80 insertions(+), 75 deletions(-)
diff --git a/src/LightQL/Entities/Entity.php b/src/LightQL/Entities/Entity.php
index b74bcc7..bf328b5 100644
--- a/src/LightQL/Entities/Entity.php
+++ b/src/LightQL/Entities/Entity.php
@@ -66,6 +66,20 @@ abstract class Entity implements IEntity
*/
protected $raw = array();
+ /**
+ * The raw data with the value transformer applied.
+ *
+ * @var array
+ */
+ private $_polished = array();
+
+ /**
+ * The name of the table managed by the current entity.
+ *
+ * @var string
+ */
+ private $_table;
+
/**
* The reflection class of this entity.
*
@@ -95,6 +109,8 @@ public function __construct(array $data = array())
throw new EntityException("Cannot create an entity without the @entity annotation.");
}
+ $this->_table = Annotations::ofClass($this, "@entity")[0]->table;
+
$this->_reflection = new \ReflectionClass($this);
$properties = $this->_reflection->getProperties(T_PUBLIC);
@@ -139,9 +155,20 @@ public function __construct(array $data = array())
*/
public function hydrate(array $data)
{
+ /** @var IValueTransformer $valueTransformer */
+ $valueTransformer = Entity::getValueTransformerOfEntity($this);
+
// Merge values
foreach ($data as $name => $value) {
+ // Save the raw value
$this->raw[$name] = $value;
+
+ // Save the polished value
+ if (is_string($name)) {
+ $this->_polished[$name] = $valueTransformer !== null
+ ? $valueTransformer->toEntityValue($this->_table, $name, $value)
+ : $value;
+ }
}
// Populate @column properties
@@ -196,7 +223,7 @@ public function get(string $column)
// Try to get the raw value
if ($this->_exists($column)) {
- return $this->raw[$column];
+ return $this->_polished[$column];
}
// The value definitively doesn't exist
@@ -291,4 +318,52 @@ private function _exists(string $column): bool
{
return array_key_exists($column, $this->raw);
}
+
+ /**
+ * @param $entity
+ * @return IValueValidator|null
+ * @throws AnnotationException
+ * @throws EntityException
+ * @throws \ReflectionException
+ */
+ public static function getValueValidatorOfEntity($entity)
+ {
+ if (Annotations::classHasAnnotation($entity, "@validator")) {
+ $validatorAnnotation = Annotations::ofClass($entity, "@validator");
+
+ if (\is_subclass_of($validatorAnnotation[0]->validator, IValueValidator::class)) {
+ $validatorClass = new \ReflectionClass($validatorAnnotation[0]->validator);
+
+ return $validatorClass->newInstance();
+ } else {
+ throw new EntityException("The value validator of this entity doesn't implement the IValueValidator interface.");
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @param $entity
+ * @return IValueTransformer|null
+ * @throws AnnotationException
+ * @throws EntityException
+ * @throws \ReflectionException
+ */
+ public static function getValueTransformerOfEntity($entity)
+ {
+ if (Annotations::classHasAnnotation($entity, "@transformer")) {
+ $transformerAnnotation = Annotations::ofClass($entity, "@transformer");
+
+ if (\is_subclass_of($transformerAnnotation[0]->transformer, IValueTransformer::class)) {
+ $transformerClass = new \ReflectionClass($transformerAnnotation[0]->transformer);
+
+ return $transformerClass->newInstance();
+ } else {
+ throw new EntityException("The value transformer of this entity doesn't implement the IValueTransformer interface.");
+ }
+ }
+
+ return null;
+ }
}
diff --git a/src/LightQL/Entities/EntityManager.php b/src/LightQL/Entities/EntityManager.php
index aca05ee..8e17f76 100644
--- a/src/LightQL/Entities/EntityManager.php
+++ b/src/LightQL/Entities/EntityManager.php
@@ -178,8 +178,8 @@ public function persist(Entity &$entity)
$autoIncrementProperty = null;
$idProperty = null;
- $valueValidator = $this->_getValueValidatorOfEntity($entity);
- $valueTransformer = $this->_getValueTransformerOfEntity($entity);
+ $valueValidator = Entity::getValueValidatorOfEntity($entity);
+ $valueTransformer = Entity::getValueTransformerOfEntity($entity);
/** @var Column $column */
foreach ($columns as $property => $column) {
@@ -334,8 +334,8 @@ public function merge(Entity &$entity)
$columns = $entity->getColumns();
$fieldAndValues = array();
- $valueValidator = $this->_getValueValidatorOfEntity($entity);
- $valueTransformer = $this->_getValueTransformerOfEntity($entity);
+ $valueValidator = Entity::getValueValidatorOfEntity($entity);
+ $valueTransformer = Entity::getValueTransformerOfEntity($entity);
$where = array();
@@ -490,52 +490,4 @@ public function getLightQL(): LightQL
{
return $this->_lightql;
}
-
- /**
- * @param $entity
- * @return IValueValidator
- * @throws EntityException
- * @throws AnnotationException
- * @throws \ReflectionException
- */
- private function _getValueValidatorOfEntity($entity)
- {
- if (Annotations::classHasAnnotation($entity, "@validator")) {
- $validatorAnnotation = Annotations::ofClass($entity, "@validator");
-
- if (\is_subclass_of($validatorAnnotation[0]->validator, IValueValidator::class)) {
- $validatorClass = new \ReflectionClass($validatorAnnotation[0]->validator);
-
- return $validatorClass->newInstance();
- } else {
- throw new EntityException("The value validator of this entity doesn't implement the IValueValidator interface.");
- }
- }
-
- return null;
- }
-
- /**
- * @param $entity
- * @return IValueTransformer
- * @throws EntityException
- * @throws AnnotationException
- * @throws \ReflectionException
- */
- private function _getValueTransformerOfEntity($entity)
- {
- if (Annotations::classHasAnnotation($entity, "@transformer")) {
- $transformerAnnotation = Annotations::ofClass($entity, "@transformer");
-
- if (\is_subclass_of($transformerAnnotation[0]->transformer, IValueTransformer::class)) {
- $transformerClass = new \ReflectionClass($transformerAnnotation[0]->transformer);
-
- return $transformerClass->newInstance();
- } else {
- throw new EntityException("The value transformer of this entity doesn't implement the IValueTransformer interface.");
- }
- }
-
- return null;
- }
}
diff --git a/src/LightQL/Sessions/Facade.php b/src/LightQL/Sessions/Facade.php
index 8080833..d7acca8 100644
--- a/src/LightQL/Sessions/Facade.php
+++ b/src/LightQL/Sessions/Facade.php
@@ -608,28 +608,6 @@ private function _parseRawEntity($rawEntity, $annotations): ?Entity
if ($rawEntity === null)
return null;
- /** @var IValueTransformer $valueTransformer */
- $valueTransformer = null;
-
- if (Annotations::classHasAnnotation($this->getEntityClassName(), "@transformer")) {
- $transformerAnnotation = Annotations::ofClass($this->getEntityClassName(), "@transformer");
-
- if (\is_subclass_of($transformerAnnotation[0]->transformer, IValueTransformer::class)) {
- $transformerClass = new \ReflectionClass($transformerAnnotation[0]->transformer);
-
- $valueTransformer = $transformerClass->newInstance();
- } else {
- throw new EntityException("The value transformer of this entity doesn't implement the IValueTransformer interface.");
- }
- }
-
- if ($valueTransformer !== null) {
- foreach ($rawEntity as $column => &$value) {
- $value = $valueTransformer->toEntityValue($annotations[0]->table, $column, $value);
- }
- unset($value);
- }
-
/** @var IPrimaryKey $pkClass */
$pkClassReflection = null;
$pkClass = null;
From 30b3e43be070a222782ade8533400649c1eda3c8 Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Thu, 1 Aug 2019 10:51:28 +0200
Subject: [PATCH 38/42] Little fixes
---
src/LightQL/LightQL.php | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/LightQL/LightQL.php b/src/LightQL/LightQL.php
index 21fe123..b192ef3 100644
--- a/src/LightQL/LightQL.php
+++ b/src/LightQL/LightQL.php
@@ -360,7 +360,7 @@ public function __construct(array $options = null)
$stack[] = is_int($key) ? $value : "{$key}={$value}";
}
- $this->_dsn = $this->_driver . ":" . implode($stack, ";");
+ $this->_dsn = $this->_driver . ":" . implode(";", $stack);
if (in_array($this->_dbms, ['mariadb', 'mysql', 'pgsql', 'sybase', 'mssql']) && isset($options['charset'])) {
$commands[] = "SET NAMES '{$options['charset']}'";
@@ -496,7 +496,7 @@ public function order(string $column, string $mode = "ASC"): LightQL
*/
public function limit(int $offset, int $count): LightQL
{
- $this->_limit = " LIMIT {$offset}, {$count} ";
+ $this->_limit = " LIMIT {$count} OFFSET {$offset}";
return $this;
}
From 842f577e4f4b8021e5f0d519ef6f4bc9cae10a33 Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Wed, 7 Aug 2019 13:33:51 +0100
Subject: [PATCH 39/42] More depth with eager selection
---
src/LightQL/Sessions/Facade.php | 55 ++++++++++++++++++++-------------
1 file changed, 33 insertions(+), 22 deletions(-)
diff --git a/src/LightQL/Sessions/Facade.php b/src/LightQL/Sessions/Facade.php
index d7acca8..7fcbf13 100644
--- a/src/LightQL/Sessions/Facade.php
+++ b/src/LightQL/Sessions/Facade.php
@@ -148,6 +148,7 @@ public function create(IEntity &$entity)
try {
$entity = $this->_parseRawEntity(
$this->entityManager->persist($entity),
+ $this->getEntityClassName(),
Annotations::ofClass($this->getEntityClassName(), "@entity")
);
@@ -234,11 +235,10 @@ public function delete(IEntity &$entity)
*/
public function find($id): ?IEntity
{
- $annotations = Annotations::ofClass($this->getEntityClassName(), "@entity");
-
return $this->_parseRawEntity(
$this->entityManager->find($this->getEntityClassName(), $id),
- $annotations
+ $this->getEntityClassName(),
+ Annotations::ofClass($this->getEntityClassName(), "@entity")
);
}
@@ -261,7 +261,7 @@ public function findAll(): array
->from($annotations[0]->table)
->selectArray();
- return $this->_parseRawEntities($rawEntities, $annotations);
+ return $this->_parseRawEntities($rawEntities, $this->getEntityClassName(), $annotations);
}
/**
@@ -287,7 +287,7 @@ public function findRange(int $start, int $length): array
->limit($start, $length)
->selectArray();
- return $this->_parseRawEntities($rawEntities, $annotations);
+ return $this->_parseRawEntities($rawEntities, $this->getEntityClassName(), $annotations);
}
/**
@@ -421,9 +421,12 @@ private function _fetchManyToMany(&$entity, $property)
);
$className = $manyToMany[0]->entity;
- $entity->{$property} = array_map(function($item) use ($manyToMany, $className) {
- return new $className($item);
- }, $results);
+
+ $entity->{$property} = $this->_parseRawEntities(
+ $results,
+ $manyToMany[0]->entity,
+ $referencedEntityAnnotations
+ );
}
/**
@@ -460,7 +463,11 @@ private function _fetchOneToMany(&$entity, $property)
$entity->{$property} = $result;
if ($result !== null) {
- $entity->{$property} = new $className($result);
+ $entity->{$property} = $this->_parseRawEntity(
+ $result,
+ $oneToMany[0]->entity,
+ $referencedEntityAnnotations
+ );
}
}
@@ -493,8 +500,9 @@ private function _fetchManyToOne(&$entity, $property)
->where(array("{$referencedEntityAnnotations[0]->table}.{$manyToOne[0]->referencedColumn}" => $lightql->parseValue($entity->get($column[0]->name))))
->selectArray("{$referencedEntityAnnotations[0]->table}.*");
- $entity->{$property} = array_map(function($item) use ($manyToOne, $entity, $mappedPropertyName) {
- $className = $manyToOne[0]->entity;
+ $className = $manyToOne[0]->entity;
+
+ $entity->{$property} = array_map(function($item) use ($entity, $mappedPropertyName, $className) {
$e = new $className($item);
$e->{$mappedPropertyName} = &$entity;
return $e;
@@ -535,8 +543,7 @@ private function _fetchOneToOne(&$entity, $property)
$entity->{$property} = $result;
if ($result !== null) {
- $entity->{$property} = new $className($result);
- $entity->{$property}->{$mappedPropertyName} = &$entity;
+ $entity->{$property} = $this->_parseRawEntity($result, $className, $referencedEntityAnnotations);
}
}
@@ -570,6 +577,7 @@ private function _resolveMappedPropertyName(string $entityClass, string $check,
* Parse a set of raw data to a set of Entities.
*
* @param array $rawEntities The set of raw entities data provided fromm database.
+ * @param string $className The name of the entity class.
* @param array $annotations The set of entity annotations.
*
* @return Entity[]
@@ -579,12 +587,12 @@ private function _resolveMappedPropertyName(string $entityClass, string $check,
* @throws LightQLException
* @throws \ReflectionException
*/
- private function _parseRawEntities($rawEntities, $annotations): array
+ private function _parseRawEntities($rawEntities, $className, $annotations): array
{
$entities = array();
foreach ($rawEntities as $rawEntity) {
- array_push($entities, $this->_parseRawEntity($rawEntity, $annotations));
+ array_push($entities, $this->_parseRawEntity($rawEntity, $className, $annotations));
}
return $entities;
@@ -594,6 +602,7 @@ private function _parseRawEntities($rawEntities, $annotations): array
* Parses raw data to Entity.
*
* @param array|null $rawEntity Raw entity data provided from database.
+ * @param string $className The name of the entity class.
* @param EntityAnnotation[] $annotations The set of entity annotations.
*
* @return Entity|null
@@ -603,7 +612,7 @@ private function _parseRawEntities($rawEntities, $annotations): array
* @throws LightQLException
* @throws \ReflectionException
*/
- private function _parseRawEntity($rawEntity, $annotations): ?Entity
+ private function _parseRawEntity($rawEntity, $className, $annotations): ?Entity
{
if ($rawEntity === null)
return null;
@@ -635,22 +644,24 @@ private function _parseRawEntity($rawEntity, $annotations): ?Entity
}
}
+ $reflection = new \ReflectionClass($className);
+
/** @var Entity $entity */
- $entity = $this->_class->newInstance($rawEntity);
+ $entity = $reflection->newInstance($rawEntity);
if ($annotations[0]->fetchMode === Entity::FETCH_EAGER) {
- $properties = $this->_class->getProperties(T_PUBLIC);
+ $properties = $reflection->getProperties(T_PUBLIC);
foreach ($properties as $property) {
if (Annotations::propertyHasAnnotation($entity, $property->name, "@id") && $pkClass !== null) {
$entity->{$property->name} = $pkClass;
- } elseif (Annotations::propertyHasAnnotation($entity, $property->name, "@manyToMany")) {
+ } elseif (Annotations::propertyHasAnnotation($entity, $property->name, "@manyToMany") && !is_array($property->getValue($entity))) {
$this->_fetchManyToMany($entity, $property->name);
- } elseif (Annotations::propertyHasAnnotation($entity, $property->name, "@oneToMany")) {
+ } elseif (Annotations::propertyHasAnnotation($entity, $property->name, "@oneToMany") && !($property->getValue($entity) instanceof IEntity)) {
$this->_fetchOneToMany($entity, $property->name);
- } elseif (Annotations::propertyHasAnnotation($entity, $property->name, "@manyToOne")) {
+ } elseif (Annotations::propertyHasAnnotation($entity, $property->name, "@manyToOne") && !is_array($property->getValue($entity))) {
$this->_fetchManyToOne($entity, $property->name);
- } elseif (Annotations::propertyHasAnnotation($entity, $property->name, "@oneToOne")) {
+ } elseif (Annotations::propertyHasAnnotation($entity, $property->name, "@oneToOne") && !($property->getValue($entity) instanceof IEntity)) {
$this->_fetchOneToOne($entity, $property->name);
}
}
From b91b5549de0b92cd76e6477a738bfcc06c8a8f7f Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Mon, 16 Sep 2019 11:47:41 +0200
Subject: [PATCH 40/42] Use the Facade instead of the EntityManager in th Query
This allow the Query to fetch data for many-to-many, one-to-many, many-to-one and one-to-one relations.
---
src/LightQL/Entities/Query.php | 340 ++++----
src/LightQL/Sessions/Facade.php | 1343 +++++++++++++++----------------
2 files changed, 837 insertions(+), 846 deletions(-)
diff --git a/src/LightQL/Entities/Query.php b/src/LightQL/Entities/Query.php
index e718042..9940a35 100644
--- a/src/LightQL/Entities/Query.php
+++ b/src/LightQL/Entities/Query.php
@@ -1,174 +1,166 @@
-
- * @copyright 2018 Aliens Group, Inc.
- * @license MIT
- * @version 1.0.0
- * @link http://lightql.na2axl.tk
- */
-
-namespace ElementaryFramework\LightQL\Entities;
-
-use ElementaryFramework\LightQL\Exceptions\QueryException;
-
-/**
- * Query
- *
- * Manage, run and get results from named queries.
- *
- * @category Entities
- * @package LightQL
- * @author Nana Axel
- * @link http://lightql.na2axl.tk/docs/api/LightQL/Entities/Query
- */
-class Query
-{
- /**
- * The entity manager running this query.
- *
- * @var EntityManager
- */
- private $_entityManager;
-
- /**
- * The reflection class of the managed entity.
- *
- * @var \ReflectionClass
- */
- private $_entityReflection;
-
- /**
- * The named query string.
- *
- * @var string
- */
- private $_namedQuery;
-
- /**
- * Query parameters.
- *
- * @var array
- */
- private $_parameters = array();
-
- /**
- * The query executed by this instance.
- *
- * @var \PDOStatement
- */
- private $_query = null;
-
- /**
- * Query constructor.
- *
- * @param EntityManager $manager
- */
- public function __construct(EntityManager $manager)
- {
- $this->_entityManager = $manager;
- }
-
- /**
- * Sets the reflection class of the managed entity.
- *
- * @param \ReflectionClass $entity The managed entity reflection class instance.
- */
- public function setEntity(\ReflectionClass $entity)
- {
- $this->_entityReflection = $entity;
- }
-
- /**
- * Sets the named query to execute.
- *
- * @param string $query The named query.
- */
- public function setQuery(string $query)
- {
- $this->_namedQuery = $query;
- }
-
- /**
- * Defines the value of one of query parameters.
- *
- * @param string $name The name of the parameter in the query.
- * @param mixed $value The value of this parameter.
- */
- public function setParam(string $name, $value)
- {
- $this->_parameters[$name] = $value;
- }
-
- /**
- * Executes the query.
- *
- * @return bool
- */
- public function run(): bool
- {
- try {
- $this->_query = $this->_entityManager->getLightQL()->prepare($this->_namedQuery);
-
- foreach ($this->_parameters as $name => $value) {
- $this->_query->bindValue($name, $value);
- }
-
- return $this->_query->execute();
- } catch (\Exception $e) {
- throw new QueryException($e->getMessage());
- }
- }
-
- /**
- * Returns the set of results after the execution of the query.
- *
- * @return Entity[]
- */
- public function getResults(): array
- {
- if ($this->_query === null) {
- throw new QueryException("Cannot get results, have you ran the query?");
- }
-
- $results = array_map(function ($item) {
- return $this->_entityReflection->newInstance($item);
- }, $this->_query->fetchAll());
-
- return $results;
- }
-
- /**
- * Returns the first result of the set after the execution
- * of the query.
- *
- * @return IEntity|null
- */
- public function getFirstResult(): ?IEntity
- {
- $results = $this->getResults();
- return count($results) > 0 ? $results[0] : null;
- }
-}
+
+ * @copyright 2018 Aliens Group, Inc.
+ * @license MIT
+ * @version 1.0.0
+ * @link http://lightql.na2axl.tk
+ */
+
+namespace ElementaryFramework\LightQL\Entities;
+
+use ElementaryFramework\Annotations\Annotations;
+use ElementaryFramework\LightQL\Exceptions\QueryException;
+use ElementaryFramework\LightQL\Sessions\Facade;
+
+/**
+ * Query
+ *
+ * Manage, run and get results from named queries.
+ *
+ * @category Entities
+ * @package LightQL
+ * @author Nana Axel
+ * @link http://lightql.na2axl.tk/docs/api/LightQL/Entities/Query
+ */
+class Query
+{
+ /**
+ * The facade running this query.
+ *
+ * @var Facade
+ */
+ private $_facade;
+
+ /**
+ * The named query string.
+ *
+ * @var string
+ */
+ private $_namedQuery;
+
+ /**
+ * Query parameters.
+ *
+ * @var array
+ */
+ private $_parameters = array();
+
+ /**
+ * The query executed by this instance.
+ *
+ * @var \PDOStatement
+ */
+ private $_query = null;
+
+ /**
+ * Query constructor.
+ *
+ * @param Facade $facade
+ */
+ public function __construct(Facade $facade)
+ {
+ $this->_facade = $facade;
+ }
+
+ /**
+ * Sets the named query to execute.
+ *
+ * @param string $query The named query.
+ */
+ public function setQuery(string $query)
+ {
+ $this->_namedQuery = $query;
+ }
+
+ /**
+ * Defines the value of one of query parameters.
+ *
+ * @param string $name The name of the parameter in the query.
+ * @param mixed $value The value of this parameter.
+ */
+ public function setParam(string $name, $value)
+ {
+ $this->_parameters[$name] = $value;
+ }
+
+ /**
+ * Executes the query.
+ *
+ * @return bool
+ */
+ public function run(): bool
+ {
+ try {
+ $this->_query = $this->_facade
+ ->getEntityManager()
+ ->getLightQL()
+ ->prepare($this->_namedQuery);
+
+ foreach ($this->_parameters as $name => $value) {
+ $this->_query->bindValue($name, $value);
+ }
+
+ return $this->_query->execute();
+ } catch (\Exception $e) {
+ throw new QueryException($e->getMessage());
+ }
+ }
+
+ /**
+ * Returns the set of results after the execution of the query.
+ *
+ * @return Entity[]
+ */
+ public function getResults(): array
+ {
+ if ($this->_query === null) {
+ throw new QueryException("Cannot get results, have you ran the query?");
+ }
+
+ $className = $this->_facade->getEntityClassName();
+
+ $results = $this->_facade->_parseRawEntities(
+ $this->_query->fetchAll(),
+ $className,
+ Annotations::ofClass($className, "@entity")
+ );
+
+ return $results;
+ }
+
+ /**
+ * Returns the first result of the set after the execution
+ * of the query.
+ *
+ * @return IEntity|null
+ */
+ public function getFirstResult(): ?IEntity
+ {
+ $results = $this->getResults();
+ return count($results) > 0 ? $results[0] : null;
+ }
+}
diff --git a/src/LightQL/Sessions/Facade.php b/src/LightQL/Sessions/Facade.php
index 7fcbf13..17a50d4 100644
--- a/src/LightQL/Sessions/Facade.php
+++ b/src/LightQL/Sessions/Facade.php
@@ -1,672 +1,671 @@
-
- * @copyright 2018 Aliens Group, Inc.
- * @license MIT
- * @version 1.0.0
- * @link http://lightql.na2axl.tk
- */
-
-namespace ElementaryFramework\LightQL\Sessions;
-
-use ElementaryFramework\Annotations\Annotations;
-use ElementaryFramework\Annotations\Exceptions\AnnotationException;
-use ElementaryFramework\LightQL\Annotations\EntityAnnotation;
-use ElementaryFramework\LightQL\Annotations\NamedQueryAnnotation;
-use ElementaryFramework\LightQL\Entities\Entity;
-use ElementaryFramework\LightQL\Entities\EntityManager;
-use ElementaryFramework\LightQL\Entities\IEntity;
-use ElementaryFramework\LightQL\Entities\IPrimaryKey;
-use ElementaryFramework\LightQL\Entities\IValueTransformer;
-use ElementaryFramework\LightQL\Entities\Query;
-use ElementaryFramework\LightQL\Exceptions\EntityException;
-use ElementaryFramework\LightQL\Exceptions\FacadeException;
-use ElementaryFramework\LightQL\Exceptions\LightQLException;
-use ElementaryFramework\LightQL\Exceptions\OperationCancelledException;
-use ElementaryFramework\LightQL\Exceptions\PersistenceUnitException;
-use ElementaryFramework\LightQL\Exceptions\ValueValidatorException;
-use ElementaryFramework\LightQL\Persistence\PersistenceUnit;
-
-/**
- * Facade
- *
- * Base class for all entity facades.
- *
- * @abstract
- * @category Sessions
- * @package LightQL
- * @author Nana Axel
- * @link http://lightql.na2axl.tk/docs/api/LightQL/Sessions/Facade
- */
-abstract class Facade implements IFacade
-{
- /**
- * The entity manager of this facade.
- *
- * @var EntityManager
- */
- protected $entityManager;
-
- /**
- * The entity class name managed by this facade.
- *
- * @var \ReflectionClass
- */
- private $_class;
-
- /**
- * The listener of this facade.
- *
- * @var IFacadeListener
- */
- private $_listener;
-
- /**
- * Facade constructor.
- *
- * @param string $class The entity class name managed by this facade.
- *
- * @throws AnnotationException When the Facade is unable to read an annotation.
- * @throws EntityException When the entity class or object doesn't have an @entity annotation.
- * @throws FacadeException When the entity class or object doesn't inherit from the Entity class.
- * @throws LightQLException
- * @throws PersistenceUnitException
- * @throws \ReflectionException
- */
- public function __construct($class)
- {
- if (!Annotations::propertyHasAnnotation($this, "entityManager", "@persistenceUnit")) {
- throw new FacadeException("Cannot create the entity facade. The property \"entityManager\" has no @persistenceUnit annotation.");
- }
-
- if (!Annotations::classHasAnnotation($class, "@entity")) {
- throw new EntityException("Cannot create an entity without the @entity annotation.");
- }
-
- if (!is_subclass_of($class, Entity::class)) {
- throw new FacadeException("Unable to create a facade. The entity class or object need to inherit directly from the class Entity.");
- }
-
- if (Annotations::classHasAnnotation($this, "@listener")) {
- $listenerReflection = new \ReflectionClass(Annotations::ofClass($this, "@listener")[0]->listener);
- $this->_listener = $listenerReflection->newInstance();
- }
-
- $this->_class = new \ReflectionClass($class);
-
- $annotations = Annotations::ofProperty($this, "entityManager", "@persistenceUnit");
- $this->entityManager = new EntityManager(PersistenceUnit::create($annotations[0]->name));
- }
-
- /**
- * Creates an entity.
- *
- * @param IEntity $entity The entity to create.
- *
- * @throws AnnotationException
- * @throws FacadeException When the facade is unable to create the entity.
- * @throws OperationCancelledException When the operation has been cancelled by a listener
- * @throws EntityException When the $entity object is not an instance of Entity class
- */
- public function create(IEntity &$entity)
- {
- if (!($entity instanceof Entity)) {
- throw new EntityException("The Facade class works only with classes extending Entity.");
- }
-
- if (!$this->_class->isInstance($entity)) {
- throw new FacadeException("Cannot create entity. The type of the entity is not valid for this facade.");
- }
-
- if ($this->_listener instanceof IFacadeListener && !$this->_listener->beforeCreate($entity)) {
- throw new OperationCancelledException(Annotations::ofClass($this, "@listener")[0]->listener);
- }
-
- try {
- $entity = $this->_parseRawEntity(
- $this->entityManager->persist($entity),
- $this->getEntityClassName(),
- Annotations::ofClass($this->getEntityClassName(), "@entity")
- );
-
- $this->_listener instanceof IFacadeListener && $this->_listener->onCreate($entity);
- } catch (\Exception $e) {
- throw new FacadeException("Unable to create the entity. See internal exception for more details.", 0, $e);
- }
- }
-
- /**
- * Edit an entity.
- *
- * @param IEntity $entity The entity to edit.
- *
- * @throws AnnotationException
- * @throws EntityException
- * @throws FacadeException When the facade is unable to edit the entity
- * @throws OperationCancelledException When the operation has been cancelled by a listener
- * @throws ValueValidatorException
- * @throws \ReflectionException
- */
- public function edit(IEntity &$entity)
- {
- if (!($entity instanceof Entity)) {
- throw new EntityException("The Facade class works only with classes extending Entity.");
- }
-
- if (!$this->_class->isInstance($entity)) {
- throw new FacadeException("Cannot edit entity. The type of the entity is not valid for this facade.");
- }
-
- if ($this->_listener instanceof IFacadeListener && !$this->_listener->beforeEdit($entity)) {
- throw new OperationCancelledException(Annotations::ofClass($this, "@listener")[0]->listener);
- }
-
- $this->entityManager->merge($entity);
-
- $this->_listener instanceof IFacadeListener && $this->_listener->onEdit($entity);
- }
-
- /**
- * Delete an entity.
- *
- * @param IEntity $entity The entity to delete.
- *
- * @throws AnnotationException
- * @throws EntityException
- * @throws FacadeException When the facade is unable to delete the entity
- * @throws OperationCancelledException When the operation has been cancelled by a listener
- * @throws ValueValidatorException
- * @throws \ReflectionException
- */
- public function delete(IEntity &$entity)
- {
- if (!($entity instanceof Entity)) {
- throw new EntityException("The Facade class works only with classes extending Entity.");
- }
-
- if (!$this->_class->isInstance($entity)) {
- throw new FacadeException("Cannot delete entity. The type of the entity is not valid for this facade.");
- }
-
- if ($this->_listener instanceof IFacadeListener && !$this->_listener->beforeDelete($entity)) {
- throw new OperationCancelledException(Annotations::ofClass($this, "@listener")[0]->listener);
- }
-
- $this->entityManager->merge($entity);
- $this->entityManager->delete($entity);
-
- $this->_listener instanceof IFacadeListener && $this->_listener->onDelete($entity);
- }
-
- /**
- * Find an entity.
- *
- * @param mixed $id The id of the entity to find
- *
- * @return IEntity|null
- *
- * @throws AnnotationException
- * @throws EntityException
- * @throws LightQLException
- * @throws \ReflectionException
- */
- public function find($id): ?IEntity
- {
- return $this->_parseRawEntity(
- $this->entityManager->find($this->getEntityClassName(), $id),
- $this->getEntityClassName(),
- Annotations::ofClass($this->getEntityClassName(), "@entity")
- );
- }
-
- /**
- * Find all entities.
- *
- * @return Entity[]
- *
- * @throws AnnotationException
- * @throws EntityException
- * @throws LightQLException
- * @throws \ReflectionException
- */
- public function findAll(): array
- {
- $annotations = Annotations::ofClass($this->getEntityClassName(), "@entity");
-
- $rawEntities = $this->entityManager
- ->getLightQL()
- ->from($annotations[0]->table)
- ->selectArray();
-
- return $this->_parseRawEntities($rawEntities, $this->getEntityClassName(), $annotations);
- }
-
- /**
- * Find all entities in the given range.
- *
- * @param int $start The starting offset.
- * @param int $length The number of entities to find.
- *
- * @return Entity[]
- *
- * @throws AnnotationException
- * @throws EntityException
- * @throws LightQLException
- * @throws \ReflectionException
- */
- public function findRange(int $start, int $length): array
- {
- $annotations = Annotations::ofClass($this->getEntityClassName(), "@entity");
-
- $rawEntities = $this->entityManager
- ->getLightQL()
- ->from($annotations[0]->table)
- ->limit($start, $length)
- ->selectArray();
-
- return $this->_parseRawEntities($rawEntities, $this->getEntityClassName(), $annotations);
- }
-
- /**
- * Count the number of entities.
- *
- * @return int
- *
- * @throws AnnotationException
- * @throws LightQLException
- */
- public function count(): int
- {
- $annotations = Annotations::ofClass($this->getEntityClassName(), "@entity");
-
- return $this->entityManager
- ->getLightQL()
- ->from($annotations[0]->table)
- ->count();
- }
-
- /**
- * Returns the entity class name of this facade.
- *
- * @return string
- */
- public function getEntityClassName(): string
- {
- return $this->_class->getName();
- }
-
- /**
- * Returns the entity manager for this facade.
- *
- * @return EntityManager
- */
- public function getEntityManager(): EntityManager
- {
- return $this->entityManager;
- }
-
- /**
- * Get a named query.
- *
- * @param string $name The name of the query.
- *
- * @return Query
- *
- * @throws FacadeException
- * @throws AnnotationException
- */
- public function getNamedQuery(string $name): Query
- {
- if (!Annotations::classHasAnnotation($this->_class->name, "@namedQuery")) {
- throw new FacadeException("The {$this->_class->name} has no @namedQuery annotation.");
- }
-
- $namedQueries = Annotations::ofClass($this->getEntityClassName(), "@namedQuery");
- $query = null;
-
- /** @var NamedQueryAnnotation $namedQuery */
- foreach ($namedQueries as $namedQuery) {
- if ($namedQuery->name === $name) {
- $query = $namedQuery->query;
- break;
- }
- }
-
- if ($query === null) {
- throw new FacadeException("The {$this->_class->name} has no @namedQuery annotation with the name {$name}.");
- }
-
- $q = new Query($this->entityManager);
- $q->setEntity($this->_class);
- $q->setQuery($query);
-
- return $q;
- }
-
- /**
- * Fetch data for a many-to-many relation.
- *
- * @param IEntity $entity The managed entity.
- * @param string $property The property in many-to-many relation.
- *
- * @throws EntityException
- * @throws AnnotationException
- * @throws LightQLException
- */
- private function _fetchManyToMany(&$entity, $property)
- {
- $manyToMany = Annotations::ofProperty($entity, $property, "@manyToMany");
- $column = Annotations::ofProperty($entity, $property, "@column");
-
- $mappedPropertyName = null;
-
- /** @var Entity $referencedEntity */
- $referencedEntity = new $manyToMany[0]->entity;
- foreach ($referencedEntity->getColumns() as $p => $c) {
- if ($c->isManyToMany) {
- $mappedManyToMany = Annotations::ofProperty($referencedEntity, $p, "@manyToMany");
- if ($mappedManyToMany[0]->crossTable === $manyToMany[0]->crossTable) {
- $mappedPropertyName = $p;
- break;
- }
- }
- }
- unset($referencedEntity);
-
- if ($mappedPropertyName === null) {
- throw new EntityException("Unable to find a suitable property with a @manyToMany annotation in the entity \"{$manyToMany[0]->entity}\".");
- }
-
- $mappedPropertyManyToManyAnnotation = Annotations::ofProperty($manyToMany[0]->entity, $mappedPropertyName, "@manyToMany");
- $mappedPropertyColumnAnnotation = Annotations::ofProperty($manyToMany[0]->entity, $mappedPropertyName, "@column");
- $referencedEntityAnnotations = Annotations::ofClass($manyToMany[0]->entity, "@entity");
-
- $lightql = $this->entityManager->getLightQL();
-
- $results = $lightql
- ->from($manyToMany[0]->crossTable)
- ->where(array("{$manyToMany[0]->crossTable}.{$manyToMany[0]->referencedColumn}" => $lightql->parseValue($entity->get($column[0]->name))))
- ->joinArray(
- "{$referencedEntityAnnotations[0]->table}.*",
- array(
- array(
- "side" => "LEFT",
- "table" => $referencedEntityAnnotations[0]->table,
- "cond" => "{$manyToMany[0]->crossTable}.{$mappedPropertyManyToManyAnnotation[0]->referencedColumn} = {$referencedEntityAnnotations[0]->table}.{$mappedPropertyColumnAnnotation[0]->name}"
- )
- )
- );
-
- $className = $manyToMany[0]->entity;
-
- $entity->{$property} = $this->_parseRawEntities(
- $results,
- $manyToMany[0]->entity,
- $referencedEntityAnnotations
- );
- }
-
- /**
- * Fetch data for a one-to-many relation.
- *
- * @param IEntity $entity The managed entity.
- * @param string $property The property in one-to-many relation.
- *
- * @throws AnnotationException
- * @throws EntityException
- * @throws LightQLException
- */
- private function _fetchOneToMany(&$entity, $property)
- {
- $oneToMany = Annotations::ofProperty($entity, $property, "@oneToMany");
- $column = Annotations::ofProperty($entity, $property, "@column");
- $referencedEntityAnnotations = Annotations::ofClass($oneToMany[0]->entity, "@entity");
-
- $mappedPropertyName = $this->_resolveMappedPropertyName($oneToMany[0]->entity, "ManyToOne", $oneToMany[0]->referencedColumn);
-
- if ($mappedPropertyName === null) {
- throw new EntityException("Unable to find a suitable property with @manyToOne annotation in the entity \"{$oneToMany[0]->entity}\".");
- }
-
- $lightql = $this->entityManager->getLightQL();
-
- $result = $lightql
- ->from($referencedEntityAnnotations[0]->table)
- ->where(array("{$referencedEntityAnnotations[0]->table}.{$oneToMany[0]->referencedColumn}" => $lightql->parseValue($entity->get($column[0]->name))))
- ->selectFirst("{$referencedEntityAnnotations[0]->table}.*");
-
- $className = $oneToMany[0]->entity;
-
- $entity->{$property} = $result;
-
- if ($result !== null) {
- $entity->{$property} = $this->_parseRawEntity(
- $result,
- $oneToMany[0]->entity,
- $referencedEntityAnnotations
- );
- }
- }
-
- /**
- * Fetch data for a many-to-one relation.
- *
- * @param IEntity $entity The managed entity.
- * @param string $property The property in many-to-one relation.
- *
- * @throws AnnotationException
- * @throws EntityException
- * @throws LightQLException
- */
- private function _fetchManyToOne(&$entity, $property)
- {
- $manyToOne = Annotations::ofProperty($entity, $property, "@manyToOne");
- $column = Annotations::ofProperty($entity, $property, "@column");
- $referencedEntityAnnotations = Annotations::ofClass($manyToOne[0]->entity, "@entity");
-
- $mappedPropertyName = $this->_resolveMappedPropertyName($manyToOne[0]->entity, "OneToMany", $manyToOne[0]->referencedColumn);
-
- if ($mappedPropertyName === null) {
- throw new EntityException("Unable to find a suitable property with @oneToMany annotation in the entity \"{$manyToOne[0]->entity}\".");
- }
-
- $lightql = $this->entityManager->getLightQL();
-
- $results = $lightql
- ->from($referencedEntityAnnotations[0]->table)
- ->where(array("{$referencedEntityAnnotations[0]->table}.{$manyToOne[0]->referencedColumn}" => $lightql->parseValue($entity->get($column[0]->name))))
- ->selectArray("{$referencedEntityAnnotations[0]->table}.*");
-
- $className = $manyToOne[0]->entity;
-
- $entity->{$property} = array_map(function($item) use ($entity, $mappedPropertyName, $className) {
- $e = new $className($item);
- $e->{$mappedPropertyName} = &$entity;
- return $e;
- }, $results);
- }
-
- /**
- * Fetch data for a one-to-one relation.
- *
- * @param IEntity $entity The managed entity.
- * @param string $property The property in one-to-one relation.
- *
- * @throws AnnotationException
- * @throws EntityException
- * @throws LightQLException
- */
- private function _fetchOneToOne(&$entity, $property)
- {
- $oneToOne = Annotations::ofProperty($entity, $property, "@oneToOne");
- $column = Annotations::ofProperty($entity, $property, "@column");
- $referencedEntityAnnotations = Annotations::ofClass($oneToOne[0]->entity, "@entity");
-
- $mappedPropertyName = $this->_resolveMappedPropertyName($oneToOne[0]->entity, "OneToOne", $oneToOne[0]->referencedColumn);
-
- if ($mappedPropertyName === null) {
- throw new EntityException("Unable to find a suitable property with @oneToOne annotation in the entity \"{$oneToOne[0]->entity}\".");
- }
-
- $lightql = $this->entityManager->getLightQL();
-
- $result = $lightql
- ->from($referencedEntityAnnotations[0]->table)
- ->where(array("{$referencedEntityAnnotations[0]->table}.{$oneToOne[0]->referencedColumn}" => $lightql->parseValue($entity->get($column[0]->name))))
- ->selectFirst("{$referencedEntityAnnotations[0]->table}.*");
-
- $className = $oneToOne[0]->entity;
-
- $entity->{$property} = $result;
-
- if ($result !== null) {
- $entity->{$property} = $this->_parseRawEntity($result, $className, $referencedEntityAnnotations);
- }
- }
-
- /**
- * Resolve the name of a property mapped by an annotation.
- *
- * @param string $entityClass The class name of the mapped property.
- * @param string $check The type of annotation to find.
- * @param string $column The mapped column name.
- *
- * @return string|null
- */
- private function _resolveMappedPropertyName(string $entityClass, string $check, string $column): ?string
- {
- $mappedPropertyName = null;
-
- /** @var Entity $referencedEntity */
- $referencedEntity = new $entityClass;
- foreach ($referencedEntity->getColumns() as $p => $c) {
- if ($c->{"is{$check}"} && $c->getName() === $column) {
- $mappedPropertyName = $p;
- break;
- }
- }
- unset($referencedEntity);
-
- return $mappedPropertyName;
- }
-
- /**
- * Parse a set of raw data to a set of Entities.
- *
- * @param array $rawEntities The set of raw entities data provided fromm database.
- * @param string $className The name of the entity class.
- * @param array $annotations The set of entity annotations.
- *
- * @return Entity[]
- *
- * @throws AnnotationException
- * @throws EntityException
- * @throws LightQLException
- * @throws \ReflectionException
- */
- private function _parseRawEntities($rawEntities, $className, $annotations): array
- {
- $entities = array();
-
- foreach ($rawEntities as $rawEntity) {
- array_push($entities, $this->_parseRawEntity($rawEntity, $className, $annotations));
- }
-
- return $entities;
- }
-
- /**
- * Parses raw data to Entity.
- *
- * @param array|null $rawEntity Raw entity data provided from database.
- * @param string $className The name of the entity class.
- * @param EntityAnnotation[] $annotations The set of entity annotations.
- *
- * @return Entity|null
- *
- * @throws AnnotationException
- * @throws EntityException
- * @throws LightQLException
- * @throws \ReflectionException
- */
- private function _parseRawEntity($rawEntity, $className, $annotations): ?Entity
- {
- if ($rawEntity === null)
- return null;
-
- /** @var IPrimaryKey $pkClass */
- $pkClassReflection = null;
- $pkClass = null;
-
- if (Annotations::classHasAnnotation($this->getEntityClassName(), "@pkClass")) {
- $pkClassAnnotation = Annotations::ofClass($this->getEntityClassName(), "@pkClass");
-
- if (\is_subclass_of($pkClassAnnotation[0]->name, IPrimaryKey::class)) {
- $pkClassReflection = new \ReflectionClass($pkClassAnnotation[0]->name);
-
- $pkClass = $pkClassReflection->newInstance();
- } else {
- throw new EntityException("The primary key class of this entity doesn't implement the IPrimaryKey interface");
- }
- }
-
- if ($pkClass !== null) {
- $properties = $pkClassReflection->getProperties(T_PUBLIC);
-
- foreach ($properties as $property) {
- if (Annotations::propertyHasAnnotation($pkClass, $property->name, "@column")) {
- $columnAnnotations = Annotations::ofProperty($pkClass, $property->name, "@column");
- $pkClass->{$property->name} = $rawEntity[$columnAnnotations[0]->name];
- }
- }
- }
-
- $reflection = new \ReflectionClass($className);
-
- /** @var Entity $entity */
- $entity = $reflection->newInstance($rawEntity);
-
- if ($annotations[0]->fetchMode === Entity::FETCH_EAGER) {
- $properties = $reflection->getProperties(T_PUBLIC);
-
- foreach ($properties as $property) {
- if (Annotations::propertyHasAnnotation($entity, $property->name, "@id") && $pkClass !== null) {
- $entity->{$property->name} = $pkClass;
- } elseif (Annotations::propertyHasAnnotation($entity, $property->name, "@manyToMany") && !is_array($property->getValue($entity))) {
- $this->_fetchManyToMany($entity, $property->name);
- } elseif (Annotations::propertyHasAnnotation($entity, $property->name, "@oneToMany") && !($property->getValue($entity) instanceof IEntity)) {
- $this->_fetchOneToMany($entity, $property->name);
- } elseif (Annotations::propertyHasAnnotation($entity, $property->name, "@manyToOne") && !is_array($property->getValue($entity))) {
- $this->_fetchManyToOne($entity, $property->name);
- } elseif (Annotations::propertyHasAnnotation($entity, $property->name, "@oneToOne") && !($property->getValue($entity) instanceof IEntity)) {
- $this->_fetchOneToOne($entity, $property->name);
- }
- }
- }
-
- return $entity;
- }
-}
+
+ * @copyright 2018 Aliens Group, Inc.
+ * @license MIT
+ * @version 1.0.0
+ * @link http://lightql.na2axl.tk
+ */
+
+namespace ElementaryFramework\LightQL\Sessions;
+
+use ElementaryFramework\Annotations\Annotations;
+use ElementaryFramework\Annotations\Exceptions\AnnotationException;
+use ElementaryFramework\LightQL\Annotations\EntityAnnotation;
+use ElementaryFramework\LightQL\Annotations\NamedQueryAnnotation;
+use ElementaryFramework\LightQL\Entities\Entity;
+use ElementaryFramework\LightQL\Entities\EntityManager;
+use ElementaryFramework\LightQL\Entities\IEntity;
+use ElementaryFramework\LightQL\Entities\IPrimaryKey;
+use ElementaryFramework\LightQL\Entities\IValueTransformer;
+use ElementaryFramework\LightQL\Entities\Query;
+use ElementaryFramework\LightQL\Exceptions\EntityException;
+use ElementaryFramework\LightQL\Exceptions\FacadeException;
+use ElementaryFramework\LightQL\Exceptions\LightQLException;
+use ElementaryFramework\LightQL\Exceptions\OperationCancelledException;
+use ElementaryFramework\LightQL\Exceptions\PersistenceUnitException;
+use ElementaryFramework\LightQL\Exceptions\ValueValidatorException;
+use ElementaryFramework\LightQL\Persistence\PersistenceUnit;
+
+/**
+ * Facade
+ *
+ * Base class for all entity facades.
+ *
+ * @abstract
+ * @category Sessions
+ * @package LightQL
+ * @author Nana Axel
+ * @link http://lightql.na2axl.tk/docs/api/LightQL/Sessions/Facade
+ */
+abstract class Facade implements IFacade
+{
+ /**
+ * The entity manager of this facade.
+ *
+ * @var EntityManager
+ */
+ protected $entityManager;
+
+ /**
+ * The entity class name managed by this facade.
+ *
+ * @var \ReflectionClass
+ */
+ private $_class;
+
+ /**
+ * The listener of this facade.
+ *
+ * @var IFacadeListener
+ */
+ private $_listener;
+
+ /**
+ * Facade constructor.
+ *
+ * @param string $class The entity class name managed by this facade.
+ *
+ * @throws AnnotationException When the Facade is unable to read an annotation.
+ * @throws EntityException When the entity class or object doesn't have an @entity annotation.
+ * @throws FacadeException When the entity class or object doesn't inherit from the Entity class.
+ * @throws LightQLException
+ * @throws PersistenceUnitException
+ * @throws \ReflectionException
+ */
+ public function __construct($class)
+ {
+ if (!Annotations::propertyHasAnnotation($this, "entityManager", "@persistenceUnit")) {
+ throw new FacadeException("Cannot create the entity facade. The property \"entityManager\" has no @persistenceUnit annotation.");
+ }
+
+ if (!Annotations::classHasAnnotation($class, "@entity")) {
+ throw new EntityException("Cannot create an entity without the @entity annotation.");
+ }
+
+ if (!is_subclass_of($class, Entity::class)) {
+ throw new FacadeException("Unable to create a facade. The entity class or object need to inherit directly from the class Entity.");
+ }
+
+ if (Annotations::classHasAnnotation($this, "@listener")) {
+ $listenerReflection = new \ReflectionClass(Annotations::ofClass($this, "@listener")[0]->listener);
+ $this->_listener = $listenerReflection->newInstance();
+ }
+
+ $this->_class = new \ReflectionClass($class);
+
+ $annotations = Annotations::ofProperty($this, "entityManager", "@persistenceUnit");
+ $this->entityManager = new EntityManager(PersistenceUnit::create($annotations[0]->name));
+ }
+
+ /**
+ * Creates an entity.
+ *
+ * @param IEntity $entity The entity to create.
+ *
+ * @throws AnnotationException
+ * @throws FacadeException When the facade is unable to create the entity.
+ * @throws OperationCancelledException When the operation has been cancelled by a listener
+ * @throws EntityException When the $entity object is not an instance of Entity class
+ */
+ public function create(IEntity &$entity)
+ {
+ if (!($entity instanceof Entity)) {
+ throw new EntityException("The Facade class works only with classes extending Entity.");
+ }
+
+ if (!$this->_class->isInstance($entity)) {
+ throw new FacadeException("Cannot create entity. The type of the entity is not valid for this facade.");
+ }
+
+ if ($this->_listener instanceof IFacadeListener && !$this->_listener->beforeCreate($entity)) {
+ throw new OperationCancelledException(Annotations::ofClass($this, "@listener")[0]->listener);
+ }
+
+ try {
+ $entity = $this->_parseRawEntity(
+ $this->entityManager->persist($entity),
+ $this->getEntityClassName(),
+ Annotations::ofClass($this->getEntityClassName(), "@entity")
+ );
+
+ $this->_listener instanceof IFacadeListener && $this->_listener->onCreate($entity);
+ } catch (\Exception $e) {
+ throw new FacadeException("Unable to create the entity. See internal exception for more details.", 0, $e);
+ }
+ }
+
+ /**
+ * Edit an entity.
+ *
+ * @param IEntity $entity The entity to edit.
+ *
+ * @throws AnnotationException
+ * @throws EntityException
+ * @throws FacadeException When the facade is unable to edit the entity
+ * @throws OperationCancelledException When the operation has been cancelled by a listener
+ * @throws ValueValidatorException
+ * @throws \ReflectionException
+ */
+ public function edit(IEntity &$entity)
+ {
+ if (!($entity instanceof Entity)) {
+ throw new EntityException("The Facade class works only with classes extending Entity.");
+ }
+
+ if (!$this->_class->isInstance($entity)) {
+ throw new FacadeException("Cannot edit entity. The type of the entity is not valid for this facade.");
+ }
+
+ if ($this->_listener instanceof IFacadeListener && !$this->_listener->beforeEdit($entity)) {
+ throw new OperationCancelledException(Annotations::ofClass($this, "@listener")[0]->listener);
+ }
+
+ $this->entityManager->merge($entity);
+
+ $this->_listener instanceof IFacadeListener && $this->_listener->onEdit($entity);
+ }
+
+ /**
+ * Delete an entity.
+ *
+ * @param IEntity $entity The entity to delete.
+ *
+ * @throws AnnotationException
+ * @throws EntityException
+ * @throws FacadeException When the facade is unable to delete the entity
+ * @throws OperationCancelledException When the operation has been cancelled by a listener
+ * @throws ValueValidatorException
+ * @throws \ReflectionException
+ */
+ public function delete(IEntity &$entity)
+ {
+ if (!($entity instanceof Entity)) {
+ throw new EntityException("The Facade class works only with classes extending Entity.");
+ }
+
+ if (!$this->_class->isInstance($entity)) {
+ throw new FacadeException("Cannot delete entity. The type of the entity is not valid for this facade.");
+ }
+
+ if ($this->_listener instanceof IFacadeListener && !$this->_listener->beforeDelete($entity)) {
+ throw new OperationCancelledException(Annotations::ofClass($this, "@listener")[0]->listener);
+ }
+
+ $this->entityManager->merge($entity);
+ $this->entityManager->delete($entity);
+
+ $this->_listener instanceof IFacadeListener && $this->_listener->onDelete($entity);
+ }
+
+ /**
+ * Find an entity.
+ *
+ * @param mixed $id The id of the entity to find
+ *
+ * @return IEntity|null
+ *
+ * @throws AnnotationException
+ * @throws EntityException
+ * @throws LightQLException
+ * @throws \ReflectionException
+ */
+ public function find($id): ?IEntity
+ {
+ return $this->_parseRawEntity(
+ $this->entityManager->find($this->getEntityClassName(), $id),
+ $this->getEntityClassName(),
+ Annotations::ofClass($this->getEntityClassName(), "@entity")
+ );
+ }
+
+ /**
+ * Find all entities.
+ *
+ * @return Entity[]
+ *
+ * @throws AnnotationException
+ * @throws EntityException
+ * @throws LightQLException
+ * @throws \ReflectionException
+ */
+ public function findAll(): array
+ {
+ $annotations = Annotations::ofClass($this->getEntityClassName(), "@entity");
+
+ $rawEntities = $this->entityManager
+ ->getLightQL()
+ ->from($annotations[0]->table)
+ ->selectArray();
+
+ return $this->_parseRawEntities($rawEntities, $this->getEntityClassName(), $annotations);
+ }
+
+ /**
+ * Find all entities in the given range.
+ *
+ * @param int $start The starting offset.
+ * @param int $length The number of entities to find.
+ *
+ * @return Entity[]
+ *
+ * @throws AnnotationException
+ * @throws EntityException
+ * @throws LightQLException
+ * @throws \ReflectionException
+ */
+ public function findRange(int $start, int $length): array
+ {
+ $annotations = Annotations::ofClass($this->getEntityClassName(), "@entity");
+
+ $rawEntities = $this->entityManager
+ ->getLightQL()
+ ->from($annotations[0]->table)
+ ->limit($start, $length)
+ ->selectArray();
+
+ return $this->_parseRawEntities($rawEntities, $this->getEntityClassName(), $annotations);
+ }
+
+ /**
+ * Count the number of entities.
+ *
+ * @return int
+ *
+ * @throws AnnotationException
+ * @throws LightQLException
+ */
+ public function count(): int
+ {
+ $annotations = Annotations::ofClass($this->getEntityClassName(), "@entity");
+
+ return $this->entityManager
+ ->getLightQL()
+ ->from($annotations[0]->table)
+ ->count();
+ }
+
+ /**
+ * Returns the entity class name of this facade.
+ *
+ * @return string
+ */
+ public function getEntityClassName(): string
+ {
+ return $this->_class->getName();
+ }
+
+ /**
+ * Returns the entity manager for this facade.
+ *
+ * @return EntityManager
+ */
+ public function getEntityManager(): EntityManager
+ {
+ return $this->entityManager;
+ }
+
+ /**
+ * Get a named query.
+ *
+ * @param string $name The name of the query.
+ *
+ * @return Query
+ *
+ * @throws FacadeException
+ * @throws AnnotationException
+ */
+ public function getNamedQuery(string $name): Query
+ {
+ if (!Annotations::classHasAnnotation($this->_class->name, "@namedQuery")) {
+ throw new FacadeException("The {$this->_class->name} has no @namedQuery annotation.");
+ }
+
+ $namedQueries = Annotations::ofClass($this->getEntityClassName(), "@namedQuery");
+ $query = null;
+
+ /** @var NamedQueryAnnotation $namedQuery */
+ foreach ($namedQueries as $namedQuery) {
+ if ($namedQuery->name === $name) {
+ $query = $namedQuery->query;
+ break;
+ }
+ }
+
+ if ($query === null) {
+ throw new FacadeException("The {$this->_class->name} has no @namedQuery annotation with the name {$name}.");
+ }
+
+ $q = new Query($this);
+ $q->setQuery($query);
+
+ return $q;
+ }
+
+ /**
+ * Fetch data for a many-to-many relation.
+ *
+ * @param IEntity $entity The managed entity.
+ * @param string $property The property in many-to-many relation.
+ *
+ * @throws EntityException
+ * @throws AnnotationException
+ * @throws LightQLException
+ */
+ private function _fetchManyToMany(&$entity, $property)
+ {
+ $manyToMany = Annotations::ofProperty($entity, $property, "@manyToMany");
+ $column = Annotations::ofProperty($entity, $property, "@column");
+
+ $mappedPropertyName = null;
+
+ /** @var Entity $referencedEntity */
+ $referencedEntity = new $manyToMany[0]->entity;
+ foreach ($referencedEntity->getColumns() as $p => $c) {
+ if ($c->isManyToMany) {
+ $mappedManyToMany = Annotations::ofProperty($referencedEntity, $p, "@manyToMany");
+ if ($mappedManyToMany[0]->crossTable === $manyToMany[0]->crossTable) {
+ $mappedPropertyName = $p;
+ break;
+ }
+ }
+ }
+ unset($referencedEntity);
+
+ if ($mappedPropertyName === null) {
+ throw new EntityException("Unable to find a suitable property with a @manyToMany annotation in the entity \"{$manyToMany[0]->entity}\".");
+ }
+
+ $mappedPropertyManyToManyAnnotation = Annotations::ofProperty($manyToMany[0]->entity, $mappedPropertyName, "@manyToMany");
+ $mappedPropertyColumnAnnotation = Annotations::ofProperty($manyToMany[0]->entity, $mappedPropertyName, "@column");
+ $referencedEntityAnnotations = Annotations::ofClass($manyToMany[0]->entity, "@entity");
+
+ $lightql = $this->entityManager->getLightQL();
+
+ $results = $lightql
+ ->from($manyToMany[0]->crossTable)
+ ->where(array("{$manyToMany[0]->crossTable}.{$manyToMany[0]->referencedColumn}" => $lightql->parseValue($entity->get($column[0]->name))))
+ ->joinArray(
+ "{$referencedEntityAnnotations[0]->table}.*",
+ array(
+ array(
+ "side" => "LEFT",
+ "table" => $referencedEntityAnnotations[0]->table,
+ "cond" => "{$manyToMany[0]->crossTable}.{$mappedPropertyManyToManyAnnotation[0]->referencedColumn} = {$referencedEntityAnnotations[0]->table}.{$mappedPropertyColumnAnnotation[0]->name}"
+ )
+ )
+ );
+
+ $entity->{$property} = $this->_parseRawEntities(
+ $results,
+ $manyToMany[0]->entity,
+ $referencedEntityAnnotations
+ );
+ }
+
+ /**
+ * Fetch data for a one-to-many relation.
+ *
+ * @param IEntity $entity The managed entity.
+ * @param string $property The property in one-to-many relation.
+ *
+ * @throws AnnotationException
+ * @throws EntityException
+ * @throws LightQLException
+ */
+ private function _fetchOneToMany(&$entity, $property)
+ {
+ $oneToMany = Annotations::ofProperty($entity, $property, "@oneToMany");
+ $column = Annotations::ofProperty($entity, $property, "@column");
+ $referencedEntityAnnotations = Annotations::ofClass($oneToMany[0]->entity, "@entity");
+
+ $mappedPropertyName = $this->_resolveMappedPropertyName($oneToMany[0]->entity, "ManyToOne", $oneToMany[0]->referencedColumn);
+
+ if ($mappedPropertyName === null) {
+ throw new EntityException("Unable to find a suitable property with @manyToOne annotation in the entity \"{$oneToMany[0]->entity}\".");
+ }
+
+ $lightql = $this->entityManager->getLightQL();
+
+ $result = $lightql
+ ->from($referencedEntityAnnotations[0]->table)
+ ->where(array("{$referencedEntityAnnotations[0]->table}.{$oneToMany[0]->referencedColumn}" => $lightql->parseValue($entity->get($column[0]->name))))
+ ->selectFirst("{$referencedEntityAnnotations[0]->table}.*");
+
+ $className = $oneToMany[0]->entity;
+
+ $entity->{$property} = $result;
+
+ if ($result !== null) {
+ $entity->{$property} = $this->_parseRawEntity(
+ $result,
+ $oneToMany[0]->entity,
+ $referencedEntityAnnotations
+ );
+ }
+ }
+
+ /**
+ * Fetch data for a many-to-one relation.
+ *
+ * @param IEntity $entity The managed entity.
+ * @param string $property The property in many-to-one relation.
+ *
+ * @throws AnnotationException
+ * @throws EntityException
+ * @throws LightQLException
+ */
+ private function _fetchManyToOne(&$entity, $property)
+ {
+ $manyToOne = Annotations::ofProperty($entity, $property, "@manyToOne");
+ $column = Annotations::ofProperty($entity, $property, "@column");
+ $referencedEntityAnnotations = Annotations::ofClass($manyToOne[0]->entity, "@entity");
+
+ $mappedPropertyName = $this->_resolveMappedPropertyName($manyToOne[0]->entity, "OneToMany", $manyToOne[0]->referencedColumn);
+
+ if ($mappedPropertyName === null) {
+ throw new EntityException("Unable to find a suitable property with @oneToMany annotation in the entity \"{$manyToOne[0]->entity}\".");
+ }
+
+ $lightql = $this->entityManager->getLightQL();
+
+ $results = $lightql
+ ->from($referencedEntityAnnotations[0]->table)
+ ->where(array("{$referencedEntityAnnotations[0]->table}.{$manyToOne[0]->referencedColumn}" => $lightql->parseValue($entity->get($column[0]->name))))
+ ->selectArray("{$referencedEntityAnnotations[0]->table}.*");
+
+ $className = $manyToOne[0]->entity;
+
+ $entity->{$property} = array_map(function($item) use ($entity, $mappedPropertyName, $className) {
+ $e = new $className($item);
+ $e->{$mappedPropertyName} = &$entity;
+ return $e;
+ }, $results);
+ }
+
+ /**
+ * Fetch data for a one-to-one relation.
+ *
+ * @param IEntity $entity The managed entity.
+ * @param string $property The property in one-to-one relation.
+ *
+ * @throws AnnotationException
+ * @throws EntityException
+ * @throws LightQLException
+ */
+ private function _fetchOneToOne(&$entity, $property)
+ {
+ $oneToOne = Annotations::ofProperty($entity, $property, "@oneToOne");
+ $column = Annotations::ofProperty($entity, $property, "@column");
+ $referencedEntityAnnotations = Annotations::ofClass($oneToOne[0]->entity, "@entity");
+
+ $mappedPropertyName = $this->_resolveMappedPropertyName($oneToOne[0]->entity, "OneToOne", $oneToOne[0]->referencedColumn);
+
+ if ($mappedPropertyName === null) {
+ throw new EntityException("Unable to find a suitable property with @oneToOne annotation in the entity \"{$oneToOne[0]->entity}\".");
+ }
+
+ $lightql = $this->entityManager->getLightQL();
+
+ $result = $lightql
+ ->from($referencedEntityAnnotations[0]->table)
+ ->where(array("{$referencedEntityAnnotations[0]->table}.{$oneToOne[0]->referencedColumn}" => $lightql->parseValue($entity->get($column[0]->name))))
+ ->selectFirst("{$referencedEntityAnnotations[0]->table}.*");
+
+ $className = $oneToOne[0]->entity;
+
+ $entity->{$property} = $result;
+
+ if ($result !== null) {
+ $entity->{$property} = $this->_parseRawEntity($result, $className, $referencedEntityAnnotations);
+ }
+ }
+
+ /**
+ * Resolve the name of a property mapped by an annotation.
+ *
+ * @param string $entityClass The class name of the mapped property.
+ * @param string $check The type of annotation to find.
+ * @param string $column The mapped column name.
+ *
+ * @return string|null
+ */
+ private function _resolveMappedPropertyName(string $entityClass, string $check, string $column): ?string
+ {
+ $mappedPropertyName = null;
+
+ /** @var Entity $referencedEntity */
+ $referencedEntity = new $entityClass;
+ foreach ($referencedEntity->getColumns() as $p => $c) {
+ if ($c->{"is{$check}"} && $c->getName() === $column) {
+ $mappedPropertyName = $p;
+ break;
+ }
+ }
+ unset($referencedEntity);
+
+ return $mappedPropertyName;
+ }
+
+ /**
+ * Parse a set of raw data to a set of Entities.
+ *
+ * @param array $rawEntities The set of raw entities data provided fromm database.
+ * @param string $className The name of the entity class.
+ * @param array $annotations The set of entity annotations.
+ *
+ * @return Entity[]
+ *
+ * @throws AnnotationException
+ * @throws EntityException
+ * @throws LightQLException
+ * @throws \ReflectionException
+ *
+ * @internal This method is intended to be used only internally by LightQL itself.
+ */
+ public function _parseRawEntities($rawEntities, $className, $annotations): array
+ {
+ $entities = array();
+
+ foreach ($rawEntities as $rawEntity) {
+ array_push($entities, $this->_parseRawEntity($rawEntity, $className, $annotations));
+ }
+
+ return $entities;
+ }
+
+ /**
+ * Parses raw data to Entity.
+ *
+ * @param array|null $rawEntity Raw entity data provided from database.
+ * @param string $className The name of the entity class.
+ * @param EntityAnnotation[] $annotations The set of entity annotations.
+ *
+ * @return Entity|null
+ *
+ * @throws AnnotationException
+ * @throws EntityException
+ * @throws LightQLException
+ * @throws \ReflectionException
+ */
+ private function _parseRawEntity($rawEntity, $className, $annotations): ?Entity
+ {
+ if ($rawEntity === null)
+ return null;
+
+ /** @var IPrimaryKey $pkClass */
+ $pkClassReflection = null;
+ $pkClass = null;
+
+ if (Annotations::classHasAnnotation($className, "@pkClass")) {
+ $pkClassAnnotation = Annotations::ofClass($className, "@pkClass");
+
+ if (\is_subclass_of($pkClassAnnotation[0]->name, IPrimaryKey::class)) {
+ $pkClassReflection = new \ReflectionClass($pkClassAnnotation[0]->name);
+
+ $pkClass = $pkClassReflection->newInstance();
+ } else {
+ throw new EntityException("The primary key class of this entity doesn't implement the IPrimaryKey interface");
+ }
+ }
+
+ if ($pkClass !== null) {
+ $properties = $pkClassReflection->getProperties(T_PUBLIC);
+
+ foreach ($properties as $property) {
+ if (Annotations::propertyHasAnnotation($pkClass, $property->name, "@column")) {
+ $columnAnnotations = Annotations::ofProperty($pkClass, $property->name, "@column");
+ $pkClass->{$property->name} = $rawEntity[$columnAnnotations[0]->name];
+ }
+ }
+ }
+
+ $reflection = new \ReflectionClass($className);
+
+ /** @var Entity $entity */
+ $entity = $reflection->newInstance($rawEntity);
+
+ if ($annotations[0]->fetchMode === Entity::FETCH_EAGER) {
+ $properties = $reflection->getProperties(T_PUBLIC);
+
+ foreach ($properties as $property) {
+ if (Annotations::propertyHasAnnotation($entity, $property->name, "@id") && $pkClass !== null) {
+ $entity->{$property->name} = $pkClass;
+ } elseif (Annotations::propertyHasAnnotation($entity, $property->name, "@manyToMany") && !is_array($property->getValue($entity))) {
+ $this->_fetchManyToMany($entity, $property->name);
+ } elseif (Annotations::propertyHasAnnotation($entity, $property->name, "@oneToMany") && !($property->getValue($entity) instanceof IEntity)) {
+ $this->_fetchOneToMany($entity, $property->name);
+ } elseif (Annotations::propertyHasAnnotation($entity, $property->name, "@manyToOne") && !is_array($property->getValue($entity))) {
+ $this->_fetchManyToOne($entity, $property->name);
+ } elseif (Annotations::propertyHasAnnotation($entity, $property->name, "@oneToOne") && !($property->getValue($entity) instanceof IEntity)) {
+ $this->_fetchOneToOne($entity, $property->name);
+ }
+ }
+ }
+
+ return $entity;
+ }
+}
From 4901fcb13fa7fdfee2a3f25c871a527934c4c740 Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Tue, 17 Sep 2019 17:22:41 +0200
Subject: [PATCH 41/42] Add an EntitiesCollection class for query results
---
src/LightQL/Entities/EntitiesCollection.php | 273 ++++++++++++++++++++
src/LightQL/Entities/Query.php | 6 +-
src/LightQL/Sessions/Facade.php | 21 +-
src/LightQL/Sessions/IFacade.php | 9 +-
4 files changed, 298 insertions(+), 11 deletions(-)
create mode 100644 src/LightQL/Entities/EntitiesCollection.php
diff --git a/src/LightQL/Entities/EntitiesCollection.php b/src/LightQL/Entities/EntitiesCollection.php
new file mode 100644
index 0000000..cbdb473
--- /dev/null
+++ b/src/LightQL/Entities/EntitiesCollection.php
@@ -0,0 +1,273 @@
+
+ * @copyright 2018 Aliens Group, Inc.
+ * @license MIT
+ * @version 1.0.0
+ * @link http://lightql.na2axl.tk
+ */
+
+namespace ElementaryFramework\LightQL\Entities;
+
+use ArgumentCountError;
+use Exception;
+
+/**
+ * Entities Collection
+ *
+ * A collection of entities which allow ordering, filtering and more.
+ *
+ * @category Entities
+ * @package LightQL
+ * @author Nana Axel
+ * @link http://lightql.na2axl.tk/docs/api/LightQL/Entities/EntitiesCollection
+ */
+class EntitiesCollection implements \ArrayAccess, \SeekableIterator, \Countable
+{
+ /**
+ * The list of entities
+ *
+ * @var IEntity[]
+ */
+ private $_entities;
+
+ /**
+ * The current position in the iterator
+ *
+ * @var int
+ */
+ private $_position = 0;
+
+ public function __construct(array $entities)
+ {
+ $this->_entities = $entities;
+ }
+
+ public function order(...$params) : self
+ {
+ if (count($params) === 1) {
+ if (is_callable($params[0])) {
+ $this->_sort($params[0]);
+ } elseif (is_string($params[0])) {
+ $this->_sort(function($current, $next) use ($params) {
+ return ($current->get($params[0]) > $next->get($params[0]));
+ });
+ }
+ } elseif (count($params) === 2 && is_string($params[0]) && is_string($params[1])) {
+ if (strtolower($params[1]) !== 'asc' && strtolower($params[1]) !== 'desc') {
+ throw new Exception("The second parameter must be 'asc' or 'desc'");
+ }
+
+ $this->_sort(function($current, $next) use ($params) {
+ if (strtolower($params[1]) === 'asc') {
+ return ($current->get($params[0]) > $next->get($params[0]));
+ } elseif (strtolower($params[1]) === 'desc') {
+ return ($current->get($params[0]) < $next->get($params[0]));
+ }
+ });
+ } else {
+ throw new ArgumentCountError();
+ }
+
+ return $this;
+ }
+
+ public function filter(callable $func) : self
+ {
+ $result = array();
+
+ foreach ($this->_entities as $entity) {
+ if ($func($entity) === true) {
+ array_push($result, $entity);
+ }
+ }
+
+ $this->_entities = array_values($result);
+
+ return $this;
+ }
+
+ public function toArray()
+ {
+ return $this->_entities;
+ }
+
+ /**
+ * Whether a offset exists
+ * @link http://php.net/manual/en/arrayaccess.offsetexists.php
+ * @param mixed $offset
+ * An offset to check for.
+ *
+ * @return bool true on success or false on failure.
+ *
+ *
+ * The return value will be casted to boolean if non-boolean was returned.
+ * @since 5.0.0
+ */
+ public function offsetExists($offset)
+ {
+ return isset($this->_entities[$offset]);
+ }
+
+ /**
+ * Offset to retrieve
+ * @link http://php.net/manual/en/arrayaccess.offsetget.php
+ * @param mixed $offset
+ * The offset to retrieve.
+ *
+ * @return mixed Can return all value types.
+ * @since 5.0.0
+ */
+ public function offsetGet($offset)
+ {
+ return $this->offsetExists($offset) ? $this->_entities[$offset] : null;
+ }
+
+ /**
+ * Offset to set
+ * @link http://php.net/manual/en/arrayaccess.offsetset.php
+ * @param mixed $offset
+ * The offset to assign the value to.
+ *
+ * @param mixed $value
+ * The value to set.
+ *
+ * @return void
+ * @since 5.0.0
+ */
+ public function offsetSet($offset, $value)
+ {
+ $this->_entities[$offset] = $value;
+ }
+
+ /**
+ * Offset to unset
+ * @link http://php.net/manual/en/arrayaccess.offsetunset.php
+ * @param mixed $offset
+ * The offset to unset.
+ *
+ * @return void
+ * @since 5.0.0
+ */
+ public function offsetUnset($offset)
+ {
+ if ($this->offsetExists($offset)) {
+ unset($this->_entities[$offset]);
+ }
+ }
+
+ /**
+ * Return the current element
+ * @link http://php.net/manual/en/iterator.current.php
+ * @return mixed Can return any type.
+ * @since 5.0.0
+ */
+ public function current()
+ {
+ $values = array_values($this->_entities);
+
+ return $values[$this->_position];
+ }
+
+ /**
+ * Move forward to next element
+ * @link http://php.net/manual/en/iterator.next.php
+ * @return void Any returned value is ignored.
+ * @since 5.0.0
+ */
+ public function next()
+ {
+ $this->_position++;
+ }
+
+ /**
+ * Return the key of the current element
+ * @link http://php.net/manual/en/iterator.key.php
+ * @return mixed scalar on success, or null on failure.
+ * @since 5.0.0
+ */
+ public function key()
+ {
+ $keys = array_keys($this->_entities);
+
+ return $keys[$this->_position];
+ }
+
+ /**
+ * Checks if current position is valid
+ * @link http://php.net/manual/en/iterator.valid.php
+ * @return bool The return value will be casted to boolean and then evaluated.
+ * Returns true on success or false on failure.
+ * @since 5.0.0
+ */
+ public function valid()
+ {
+ return $this->_position < count($this->_entities);
+ }
+
+ /**
+ * Rewind the Iterator to the first element
+ * @link http://php.net/manual/en/iterator.rewind.php
+ * @return void Any returned value is ignored.
+ * @since 5.0.0
+ */
+ public function rewind()
+ {
+ $this->_position = 0;
+ }
+
+ /**
+ * Seeks to a position
+ * @link http://php.net/manual/en/seekableiterator.seek.php
+ * @param int $position
+ * The position to seek to.
+ *
+ * @return void
+ * @since 5.1.0
+ */
+ public function seek($position)
+ {
+ $this->_position = $position;
+ }
+
+ public function count()
+ {
+ return count($this->_entities);
+ }
+
+ private function _sort(callable $func)
+ {
+ for ($i = 0, $l = count($this->_entities); $i < $l - 1; $i++) {
+ for ($j = $i+1; $j < $l; $j++) {
+ if ($func($this->_entities[$i], $this->_entities[$j])) {
+ $t = $this->_entities[$i];
+ $this->_entities[$i] = $this->_entities[$j];
+ $this->_entities[$j] = $t;
+ }
+ }
+ }
+ }
+}
diff --git a/src/LightQL/Entities/Query.php b/src/LightQL/Entities/Query.php
index 9940a35..19e7789 100644
--- a/src/LightQL/Entities/Query.php
+++ b/src/LightQL/Entities/Query.php
@@ -133,9 +133,9 @@ public function run(): bool
/**
* Returns the set of results after the execution of the query.
*
- * @return Entity[]
+ * @return EntitiesCollection
*/
- public function getResults(): array
+ public function getResults(): EntitiesCollection
{
if ($this->_query === null) {
throw new QueryException("Cannot get results, have you ran the query?");
@@ -149,7 +149,7 @@ public function getResults(): array
Annotations::ofClass($className, "@entity")
);
- return $results;
+ return new EntitiesCollection($results);
}
/**
diff --git a/src/LightQL/Sessions/Facade.php b/src/LightQL/Sessions/Facade.php
index 17a50d4..3907bf0 100644
--- a/src/LightQL/Sessions/Facade.php
+++ b/src/LightQL/Sessions/Facade.php
@@ -36,6 +36,7 @@
use ElementaryFramework\Annotations\Exceptions\AnnotationException;
use ElementaryFramework\LightQL\Annotations\EntityAnnotation;
use ElementaryFramework\LightQL\Annotations\NamedQueryAnnotation;
+use ElementaryFramework\LightQL\Entities\EntitiesCollection;
use ElementaryFramework\LightQL\Entities\Entity;
use ElementaryFramework\LightQL\Entities\EntityManager;
use ElementaryFramework\LightQL\Entities\IEntity;
@@ -252,7 +253,7 @@ public function find($id): ?IEntity
* @throws LightQLException
* @throws \ReflectionException
*/
- public function findAll(): array
+ public function findAll(): EntitiesCollection
{
$annotations = Annotations::ofClass($this->getEntityClassName(), "@entity");
@@ -261,7 +262,13 @@ public function findAll(): array
->from($annotations[0]->table)
->selectArray();
- return $this->_parseRawEntities($rawEntities, $this->getEntityClassName(), $annotations);
+ return new EntitiesCollection(
+ $this->_parseRawEntities(
+ $rawEntities,
+ $this->getEntityClassName(),
+ $annotations
+ )
+ );
}
/**
@@ -277,7 +284,7 @@ public function findAll(): array
* @throws LightQLException
* @throws \ReflectionException
*/
- public function findRange(int $start, int $length): array
+ public function findRange(int $start, int $length): EntitiesCollection
{
$annotations = Annotations::ofClass($this->getEntityClassName(), "@entity");
@@ -287,7 +294,13 @@ public function findRange(int $start, int $length): array
->limit($start, $length)
->selectArray();
- return $this->_parseRawEntities($rawEntities, $this->getEntityClassName(), $annotations);
+ return new EntitiesCollection(
+ $this->_parseRawEntities(
+ $rawEntities,
+ $this->getEntityClassName(),
+ $annotations
+ )
+ );
}
/**
diff --git a/src/LightQL/Sessions/IFacade.php b/src/LightQL/Sessions/IFacade.php
index 98eea81..789eb5a 100644
--- a/src/LightQL/Sessions/IFacade.php
+++ b/src/LightQL/Sessions/IFacade.php
@@ -32,6 +32,7 @@
namespace ElementaryFramework\LightQL\Sessions;
+use ElementaryFramework\LightQL\Entities\EntitiesCollection;
use ElementaryFramework\LightQL\Entities\IEntity;
/**
@@ -79,9 +80,9 @@ function find($id): ?IEntity;
/**
* Find all entities.
*
- * @return IEntity[]
+ * @return EntitiesCollection
*/
- function findAll(): array;
+ function findAll(): EntitiesCollection;
/**
* Find all entities in the given range.
@@ -89,9 +90,9 @@ function findAll(): array;
* @param int $start The starting offset.
* @param int $length The number of entities to find.
*
- * @return IEntity[]
+ * @return EntitiesCollection
*/
- function findRange(int $start, int $length): array;
+ function findRange(int $start, int $length): EntitiesCollection;
/**
* Count the number of entities.
From 01fce6c209b6902959e5075535d82e2dfbb78b53 Mon Sep 17 00:00:00 2001
From: Nana Axel
Date: Sun, 13 Oct 2019 18:12:03 +0100
Subject: [PATCH 42/42] Update GenericEntity to use EntitiesCollection
---
src/LightQL/Sessions/GenericFacade.php | 33 +++++++++++++++-----------
1 file changed, 19 insertions(+), 14 deletions(-)
diff --git a/src/LightQL/Sessions/GenericFacade.php b/src/LightQL/Sessions/GenericFacade.php
index 8736c8e..7a7ce58 100644
--- a/src/LightQL/Sessions/GenericFacade.php
+++ b/src/LightQL/Sessions/GenericFacade.php
@@ -32,6 +32,7 @@
namespace ElementaryFramework\LightQL\Sessions;
+use ElementaryFramework\LightQL\Entities\EntitiesCollection;
use ElementaryFramework\LightQL\Entities\GenericEntity;
use ElementaryFramework\LightQL\Entities\IEntity;
use ElementaryFramework\LightQL\Exceptions\EntityException;
@@ -214,11 +215,11 @@ public function findGeneric(string $table, string $pk, $id): ?IEntity
*
* This method is unavailable. Use findGeneric instead.
*
- * @return IEntity[]
+ * @return EntitiesCollection
*
* @throws FacadeException
*/
- public function findAll(): array
+ public function findAll(): EntitiesCollection
{
throw new FacadeException("The \"findAll\" method is unavailable in GenericFacade, use \"findAllGeneric\" instead.");
}
@@ -229,19 +230,21 @@ public function findAll(): array
* @param string $table The name of the table
* @param string $pk The name of the column with primary key property
*
- * @return IEntity[]
+ * @return EntitiesCollection
*
* @throws LightQLException
*/
- public function findAllGeneric(string $table, string $pk): array
+ public function findAllGeneric(string $table, string $pk): EntitiesCollection
{
$rawEntities = $this->_lightql
->from($table)
->selectArray();
- return array_map(function ($raw) use ($table, $pk) {
- return new GenericEntity($table, $pk, $raw);
- }, $rawEntities);
+ return new EntitiesCollection(
+ array_map(function ($raw) use ($table, $pk) {
+ return new GenericEntity($table, $pk, $raw);
+ }, $rawEntities)
+ );
}
/**
@@ -252,11 +255,11 @@ public function findAllGeneric(string $table, string $pk): array
* @param int $start The starting offset.
* @param int $length The number of entities to find.
*
- * @return IEntity[]
+ * @return EntitiesCollection
*
* @throws FacadeException
*/
- public function findRange(int $start, int $length): array
+ public function findRange(int $start, int $length): EntitiesCollection
{
throw new FacadeException("The \"findRange\" method is unavailable in GenericFacade, use \"findRangeGeneric\" instead.");
}
@@ -269,20 +272,22 @@ public function findRange(int $start, int $length): array
* @param int $start The starting offset.
* @param int $length The number of entities to find.
*
- * @return IEntity[]
+ * @return EntitiesCollection
*
* @throws LightQLException
*/
- public function findRangeGeneric(string $table, string $pk, int $start, int $length): array
+ public function findRangeGeneric(string $table, string $pk, int $start, int $length): EntitiesCollection
{
$rawEntities = $this->_lightql
->from($table)
->limit($start, $length)
->selectArray();
- return array_map(function ($raw) use ($table, $pk) {
- return new GenericEntity($table, $pk, $raw);
- }, $rawEntities);
+ return new EntitiesCollection(
+ array_map(function ($raw) use ($table, $pk) {
+ return new GenericEntity($table, $pk, $raw);
+ }, $rawEntities)
+ );
}
/**