- WIP on attributes ; on testing phase
This commit is contained in:
parent
5295a335b3
commit
e6d0279c1a
|
@ -6,9 +6,9 @@ class Join implements \Notes\Annotation {
|
||||||
|
|
||||||
public string $type;
|
public string $type;
|
||||||
|
|
||||||
public /*string|Stringable*/ $key;
|
public string|Stringable $key;
|
||||||
|
|
||||||
public /*string|Stringable*/ $foreignKey;
|
public string|Stringable $foreignKey;
|
||||||
|
|
||||||
public string $entity;
|
public string $entity;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Attribute;
|
||||||
|
|
||||||
|
use Ulmus\Common\EntityField;
|
||||||
|
|
||||||
|
class Attribute
|
||||||
|
{
|
||||||
|
public static function handleArrayField(null|\Stringable|string|array $field) : mixed
|
||||||
|
{
|
||||||
|
if ( is_array($field) ) {
|
||||||
|
$class = array_shift($field);
|
||||||
|
|
||||||
|
return $class::field(...$field);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $field;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Attribute\Obj;
|
||||||
|
|
||||||
|
#[\Attribute(\Attribute::TARGET_CLASS)]
|
||||||
|
class Collation {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Attribute\Obj;
|
||||||
|
|
||||||
|
#[\Attribute(\Attribute::TARGET_CLASS)]
|
||||||
|
class Method {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Attribute\Obj;
|
||||||
|
|
||||||
|
#[\Attribute(\Attribute::TARGET_CLASS)]
|
||||||
|
class Table {
|
||||||
|
public function __construct(
|
||||||
|
public string $name,
|
||||||
|
public ? string $database = null,
|
||||||
|
public ? string $schema = null,
|
||||||
|
public ? string $adapter = null,
|
||||||
|
public ? string $engine = null,
|
||||||
|
) {}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Attribute\Property;
|
||||||
|
|
||||||
|
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
||||||
|
class Field {
|
||||||
|
public function __construct(
|
||||||
|
public ? string $name = null,
|
||||||
|
public ? string $type = null,
|
||||||
|
public ? int $length = null,
|
||||||
|
public ? int $precision = null,
|
||||||
|
public array $attributes = [],
|
||||||
|
public bool $nullable = false,
|
||||||
|
public mixed $default = null,
|
||||||
|
public bool $readonly = false,
|
||||||
|
) {}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Attribute\Property\Field;
|
||||||
|
|
||||||
|
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
||||||
|
class Bigint extends \Ulmus\Attribute\Property\Field
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public ? string $name = null,
|
||||||
|
public ? string $type = "bigint",
|
||||||
|
public ? int $length = null,
|
||||||
|
public ? int $precision = null,
|
||||||
|
public array $attributes = [],
|
||||||
|
public bool $nullable = false,
|
||||||
|
public mixed $default = null,
|
||||||
|
public bool $readonly = false,
|
||||||
|
) {}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Attribute\Property\Field;
|
||||||
|
|
||||||
|
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
||||||
|
class Blob extends \Ulmus\Attribute\Property\Field
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public ? string $name = null,
|
||||||
|
public ? string $type = "blob",
|
||||||
|
public ? int $length = null,
|
||||||
|
public ? int $precision = null,
|
||||||
|
public array $attributes = [],
|
||||||
|
public bool $nullable = false,
|
||||||
|
public mixed $default = null,
|
||||||
|
public bool $readonly = false,
|
||||||
|
) {}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Attribute\Property\Field;
|
||||||
|
|
||||||
|
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
||||||
|
class CreatedAt extends \Ulmus\Attribute\Property\Field {
|
||||||
|
public function __construct(
|
||||||
|
public ? string $name = null,
|
||||||
|
public ? string $type = "timestamp",
|
||||||
|
public array $attributes = [
|
||||||
|
'default' => "CURRENT_TIMESTAMP",
|
||||||
|
],
|
||||||
|
public bool $nullable = false,
|
||||||
|
public mixed $default = null,
|
||||||
|
public bool $readonly = false,
|
||||||
|
) {}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Attribute\Property\Field;
|
||||||
|
|
||||||
|
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
||||||
|
class Date extends \Ulmus\Attribute\Property\Field {
|
||||||
|
public function __construct(
|
||||||
|
public ? string $name = null,
|
||||||
|
public ? string $type = "date",
|
||||||
|
public array $attributes = [],
|
||||||
|
public bool $nullable = false,
|
||||||
|
public mixed $default = null,
|
||||||
|
public bool $readonly = false,
|
||||||
|
) {}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Attribute\Property\Field;
|
||||||
|
|
||||||
|
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
||||||
|
class Datetime extends \Ulmus\Attribute\Property\Field {
|
||||||
|
public function __construct(
|
||||||
|
public ? string $name = null,
|
||||||
|
public ? string $type = "datetime",
|
||||||
|
public array $attributes = [],
|
||||||
|
public bool $nullable = false,
|
||||||
|
public mixed $default = null,
|
||||||
|
public bool $readonly = false,
|
||||||
|
) {}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Attribute\Property\Field;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Since we need consistancy between the declaration of our ID and FK fields, it
|
||||||
|
* was decided to extend the PK class instead of Field for this case.
|
||||||
|
*/
|
||||||
|
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
||||||
|
class ForeignKey extends PrimaryKey {
|
||||||
|
public function __construct(
|
||||||
|
public ? string $name = null,
|
||||||
|
public ? string $type = 'bigint',
|
||||||
|
public ? int $length = null,
|
||||||
|
public ? int $precision = null,
|
||||||
|
public array $attributes = [
|
||||||
|
'primary_key' => false,
|
||||||
|
'auto_increment' => false
|
||||||
|
],
|
||||||
|
public bool $nullable = false,
|
||||||
|
public mixed $default = null,
|
||||||
|
public bool $readonly = false,
|
||||||
|
) {}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Attribute\Property\Field;
|
||||||
|
|
||||||
|
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
||||||
|
class Id extends \Ulmus\Attribute\Property\Field {
|
||||||
|
public function __construct(
|
||||||
|
public ? string $name = null,
|
||||||
|
public ? string $type = 'bigint',
|
||||||
|
public ? int $length = null,
|
||||||
|
public ? int $precision = null,
|
||||||
|
public array $attributes = [
|
||||||
|
'unsigned' => true,
|
||||||
|
'auto_increment' => true,
|
||||||
|
'primary_key' => true,
|
||||||
|
],
|
||||||
|
public bool $nullable = false,
|
||||||
|
public mixed $default = null,
|
||||||
|
public bool $readonly = false,
|
||||||
|
) {}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Attribute\Property\Field;
|
||||||
|
|
||||||
|
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
||||||
|
class Longblob extends \Ulmus\Attribute\Property\Field
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public ? string $name = null,
|
||||||
|
public ? string $type = "longblob",
|
||||||
|
public ? int $length = null,
|
||||||
|
public ? int $precision = null,
|
||||||
|
public array $attributes = [],
|
||||||
|
public bool $nullable = false,
|
||||||
|
public mixed $default = null,
|
||||||
|
public bool $readonly = false,
|
||||||
|
) {}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Attribute\Property\Field;
|
||||||
|
|
||||||
|
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
||||||
|
class Longtext extends \Ulmus\Attribute\Property\Field
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public ? string $name = null,
|
||||||
|
public ? string $type = "longtext",
|
||||||
|
public ? int $length = null,
|
||||||
|
public ? int $precision = null,
|
||||||
|
public array $attributes = [],
|
||||||
|
public bool $nullable = false,
|
||||||
|
public mixed $default = null,
|
||||||
|
public bool $readonly = false,
|
||||||
|
) {}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Attribute\Property\Field;
|
||||||
|
|
||||||
|
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
||||||
|
class Mediumblob extends \Ulmus\Attribute\Property\Field
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public ? string $name = null,
|
||||||
|
public ? string $type = "mediumblob",
|
||||||
|
public ? int $length = null,
|
||||||
|
public ? int $precision = null,
|
||||||
|
public array $attributes = [],
|
||||||
|
public bool $nullable = false,
|
||||||
|
public mixed $default = null,
|
||||||
|
public bool $readonly = false,
|
||||||
|
) {}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Attribute\Property\Field;
|
||||||
|
|
||||||
|
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
||||||
|
class Mediumtext extends \Ulmus\Attribute\Property\Field
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public ? string $name = null,
|
||||||
|
public ? string $type = "mediumtext",
|
||||||
|
public ? int $length = null,
|
||||||
|
public ? int $precision = null,
|
||||||
|
public array $attributes = [],
|
||||||
|
public bool $nullable = false,
|
||||||
|
public mixed $default = null,
|
||||||
|
public bool $readonly = false,
|
||||||
|
) {}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Attribute\Property\Field;
|
||||||
|
|
||||||
|
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
||||||
|
class PrimaryKey extends \Ulmus\Attribute\Property\Field
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public ? string $name = null,
|
||||||
|
public ? string $type = 'bigint',
|
||||||
|
public ? int $length = null,
|
||||||
|
public ? int $precision = null,
|
||||||
|
public array $attributes = [
|
||||||
|
'primary_key' => true,
|
||||||
|
],
|
||||||
|
public bool $nullable = false,
|
||||||
|
public mixed $default = null,
|
||||||
|
public bool $readonly = false,
|
||||||
|
) {}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Attribute\Property\Field;
|
||||||
|
|
||||||
|
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
||||||
|
class Text extends \Ulmus\Attribute\Property\Field
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public ? string $name = null,
|
||||||
|
public ? string $type = "text",
|
||||||
|
public ? int $length = null,
|
||||||
|
public ? int $precision = null,
|
||||||
|
public array $attributes = [],
|
||||||
|
public bool $nullable = false,
|
||||||
|
public mixed $default = null,
|
||||||
|
public bool $readonly = false,
|
||||||
|
) {}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Attribute\Property\Field;
|
||||||
|
|
||||||
|
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
||||||
|
class Time extends \Ulmus\Attribute\Property\Field
|
||||||
|
{
|
||||||
|
public function __construct(? string $type = "time", ? int $length = null)
|
||||||
|
{
|
||||||
|
parent::__construct($type, $length);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Attribute\Property\Field;
|
||||||
|
|
||||||
|
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
||||||
|
class Blob extends \Ulmus\Attribute\Property\Field
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public ? string $name = null,
|
||||||
|
public ? string $type = "tinyblob",
|
||||||
|
public ? int $length = null,
|
||||||
|
public ? int $precision = null,
|
||||||
|
public array $attributes = [],
|
||||||
|
public bool $nullable = false,
|
||||||
|
public mixed $default = null,
|
||||||
|
public bool $readonly = false,
|
||||||
|
) {}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Attribute\Property\Field;
|
||||||
|
|
||||||
|
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
||||||
|
class Tinyint extends \Ulmus\Attribute\Property\Field
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public ? string $name = null,
|
||||||
|
public ? string $type = "tinyint",
|
||||||
|
public ? int $length = null,
|
||||||
|
public ? int $precision = null,
|
||||||
|
public array $attributes = [],
|
||||||
|
public bool $nullable = false,
|
||||||
|
public mixed $default = null,
|
||||||
|
public bool $readonly = false,
|
||||||
|
) {}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Attribute\Property\Field;
|
||||||
|
|
||||||
|
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
||||||
|
class UpdatedAt extends \Ulmus\Attribute\Property\Field
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public ? string $name = null,
|
||||||
|
public ? string $type = "timestamp",
|
||||||
|
public array $attributes = [
|
||||||
|
'update' => "CURRENT_TIMESTAMP",
|
||||||
|
'default' => null,
|
||||||
|
],
|
||||||
|
public bool $nullable = true,
|
||||||
|
public mixed $default = null,
|
||||||
|
public bool $readonly = false,
|
||||||
|
) {}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Attribute\Property;
|
||||||
|
|
||||||
|
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::IS_REPEATABLE)]
|
||||||
|
class Filter {
|
||||||
|
public function __construct(
|
||||||
|
public string $method = ""
|
||||||
|
)
|
||||||
|
{}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Attribute\Property;
|
||||||
|
|
||||||
|
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::IS_REPEATABLE)]
|
||||||
|
class FilterJoin {
|
||||||
|
public function __construct(
|
||||||
|
public string $method = ""
|
||||||
|
) {}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Attribute\Property;
|
||||||
|
|
||||||
|
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::IS_REPEATABLE)]
|
||||||
|
class GroupBy {
|
||||||
|
|
||||||
|
public array $fields = [];
|
||||||
|
|
||||||
|
public function __construct(...$field)
|
||||||
|
{
|
||||||
|
if ( $field ) {
|
||||||
|
$this->fields = $field;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Attribute\Property;
|
||||||
|
|
||||||
|
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::IS_REPEATABLE)]
|
||||||
|
class Having extends Where {}
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Attribute\Property;
|
||||||
|
|
||||||
|
use Ulmus\Attribute\Attribute;
|
||||||
|
|
||||||
|
#[\Attribute]
|
||||||
|
class Join {
|
||||||
|
public function __construct(
|
||||||
|
public string $type,
|
||||||
|
public null|string|\Stringable $key = null,
|
||||||
|
public null|string|\Stringable $foreignKey = null,
|
||||||
|
public null|string $entity = null,
|
||||||
|
public null|string $alias = null,
|
||||||
|
) {
|
||||||
|
$this->key = Attribute::handleArrayField($this->key);
|
||||||
|
$this->foreignKey = Attribute::handleArrayField($this->foreignKey);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Attribute\Property;
|
||||||
|
|
||||||
|
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::IS_REPEATABLE)]
|
||||||
|
class On extends Where {}
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Attribute\Property;
|
||||||
|
|
||||||
|
use Ulmus\Attribute\Attribute;
|
||||||
|
use Ulmus\Query;
|
||||||
|
|
||||||
|
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::IS_REPEATABLE)]
|
||||||
|
class OrWhere extends Where {
|
||||||
|
public function __construct(
|
||||||
|
public string|\Stringable|array $field,
|
||||||
|
public mixed $value = null,
|
||||||
|
public null|string $operator = null,
|
||||||
|
public null|string $condition = Query\Where::CONDITION_OR,
|
||||||
|
) {
|
||||||
|
|
||||||
|
$this->key = Attribute::handleArrayField($this->key);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Attribute\Property;
|
||||||
|
|
||||||
|
use Ulmus\Attribute\Attribute;
|
||||||
|
|
||||||
|
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::IS_REPEATABLE)]
|
||||||
|
class OrderBy {
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
public string|\Stringable|array $field,
|
||||||
|
public string $order = "ASC",
|
||||||
|
) {
|
||||||
|
$this->field = Attribute::handleArrayField($this->field);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Attribute\Property;
|
||||||
|
|
||||||
|
use Ulmus\Attribute\Attribute;
|
||||||
|
|
||||||
|
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
||||||
|
class Relation {
|
||||||
|
public function __construct(
|
||||||
|
public string $type,
|
||||||
|
public \Stringable|string|array $key = "",
|
||||||
|
public null|\Closure $generateKey = null,
|
||||||
|
public null|\Stringable|string|array $foreignKey = null,
|
||||||
|
public null|\Stringable|string|array $foreignField = null,
|
||||||
|
public array $foreignKeys = [],
|
||||||
|
public null|string $bridge = null,
|
||||||
|
public null|\Stringable|string|array $bridgeKey = null,
|
||||||
|
public null|\Stringable|string|array $bridgeField = null,
|
||||||
|
public null|\Stringable|string|array $bridgeForeignKey = null,
|
||||||
|
public null|\Stringable|string|array $field = null,
|
||||||
|
public null|string $entity = null,
|
||||||
|
public null|string $join = null,
|
||||||
|
public string $function = "loadAll",
|
||||||
|
) {
|
||||||
|
$this->key = Attribute::handleArrayField($this->key);
|
||||||
|
$this->foreignKey = Attribute::handleArrayField($this->foreignKey);
|
||||||
|
$this->foreignField = Attribute::handleArrayField($this->foreignField);
|
||||||
|
$this->bridgeKey = Attribute::handleArrayField($this->bridgeKey);
|
||||||
|
$this->bridgeField = Attribute::handleArrayField($this->bridgeField);
|
||||||
|
$this->bridgeForeignKey = Attribute::handleArrayField($this->bridgeForeignKey);
|
||||||
|
$this->field = Attribute::handleArrayField($this->field);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function entity() {
|
||||||
|
try {
|
||||||
|
$e = $this->entity;
|
||||||
|
} catch (\Throwable $ex) {
|
||||||
|
throw new \Exception("Your @Relation annotation seems to be missing an `entity` entry.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new $e();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function bridge() {
|
||||||
|
$e = $this->bridge;
|
||||||
|
|
||||||
|
return new $e();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function normalizeType() : string
|
||||||
|
{
|
||||||
|
return strtolower(str_replace(['-', '_', ' '], '', $this->type));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isOneToOne() : bool
|
||||||
|
{
|
||||||
|
return $this->normalizeType() === 'onetoone';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isOneToMany() : bool
|
||||||
|
{
|
||||||
|
return $this->normalizeType() === 'onetomany';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isManyToMany() : bool
|
||||||
|
{
|
||||||
|
return $this->normalizeType() === 'manytomany';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function function() : string
|
||||||
|
{
|
||||||
|
if ($this->function) {
|
||||||
|
return $this->function;
|
||||||
|
}
|
||||||
|
elseif ($this->isOneToOne()) {
|
||||||
|
return 'load';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'loadAll';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasBridge() : bool
|
||||||
|
{
|
||||||
|
return (bool) $this->bridge;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Attribute\Property\Relation;
|
||||||
|
|
||||||
|
#[\Attribute]
|
||||||
|
class Ignore {}
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Attribute\Property;
|
||||||
|
|
||||||
|
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
||||||
|
class Virtual extends Field {
|
||||||
|
|
||||||
|
public bool $readonly = true;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
public ? string $method = null,
|
||||||
|
) {}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Attribute\Property;
|
||||||
|
|
||||||
|
use Ulmus\Attribute\Attribute;
|
||||||
|
use Ulmus\Query;
|
||||||
|
|
||||||
|
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::IS_REPEATABLE)]
|
||||||
|
class Where {
|
||||||
|
public function __construct(
|
||||||
|
public string|\Stringable|array $field,
|
||||||
|
public mixed $value = null,
|
||||||
|
public string $operator = Query\Where::OPERATOR_EQUAL,
|
||||||
|
public string $condition = Query\Where::CONDITION_AND,
|
||||||
|
) {
|
||||||
|
$this->field = Attribute::handleArrayField($field);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Attribute\Property;
|
||||||
|
|
||||||
|
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::IS_REPEATABLE)]
|
||||||
|
class WithJoin {
|
||||||
|
public function __construct(
|
||||||
|
array $joins = []
|
||||||
|
) {}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@
|
||||||
namespace Ulmus\Common;
|
namespace Ulmus\Common;
|
||||||
|
|
||||||
use Ulmus\Annotation\Annotation;
|
use Ulmus\Annotation\Annotation;
|
||||||
|
use Ulmus\Attribute;
|
||||||
use Ulmus\Migration\FieldDefinition;
|
use Ulmus\Migration\FieldDefinition;
|
||||||
use Ulmus\Ulmus,
|
use Ulmus\Ulmus,
|
||||||
Ulmus\Adapter\AdapterInterface,
|
Ulmus\Adapter\AdapterInterface,
|
||||||
|
@ -29,7 +30,7 @@ class EntityField implements WhereRawParameter
|
||||||
|
|
||||||
public function name($useAlias = true) : string
|
public function name($useAlias = true) : string
|
||||||
{
|
{
|
||||||
$name = $this->entityResolver->searchFieldAnnotation($this->name, new Field() )->name ?? $this->name;
|
$name = $this->entityResolver->searchFieldAnnotation($this->name, [ Attribute\Property\Field::class, Field::class ] )->name ?? $this->name;
|
||||||
|
|
||||||
$name = $this->entityResolver->databaseAdapter()->adapter()->escapeIdentifier($name, AdapterInterface::IDENTIFIER_FIELD);
|
$name = $this->entityResolver->databaseAdapter()->adapter()->escapeIdentifier($name, AdapterInterface::IDENTIFIER_FIELD);
|
||||||
|
|
||||||
|
|
|
@ -2,11 +2,13 @@
|
||||||
|
|
||||||
namespace Ulmus\Common;
|
namespace Ulmus\Common;
|
||||||
|
|
||||||
|
use Psr\SimpleCache\CacheInterface;
|
||||||
use Ulmus\Ulmus,
|
use Ulmus\Ulmus,
|
||||||
Ulmus\Annotation\Classes\Table,
|
Ulmus\Annotation\Classes\Table,
|
||||||
Ulmus\Annotation\Property\Field,
|
Ulmus\Annotation\Property\Field,
|
||||||
Ulmus\Annotation\Property\Virtual,
|
Ulmus\Annotation\Property\Virtual,
|
||||||
Ulmus\Annotation\Property\Relation;
|
Ulmus\Annotation\Property\Relation,
|
||||||
|
Ulmus\Attribute;
|
||||||
|
|
||||||
use Notes\Annotation;
|
use Notes\Annotation;
|
||||||
|
|
||||||
|
@ -32,12 +34,12 @@ class EntityResolver {
|
||||||
|
|
||||||
protected array $fieldList = [];
|
protected array $fieldList = [];
|
||||||
|
|
||||||
public function __construct(string $entityClass)
|
public function __construct(string $entityClass, ? CacheInterface $cache = null)
|
||||||
{
|
{
|
||||||
$this->entityClass = $entityClass;
|
$this->entityClass = $entityClass;
|
||||||
|
|
||||||
list($this->uses, $this->class, $this->methods, $this->properties) = array_values(
|
list($this->uses, $this->class, $this->methods, $this->properties) = array_values(
|
||||||
ObjectReflection::fromClass($entityClass)->read()
|
ObjectReflection::fromClass($entityClass, $cache)->read()
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->resolveAnnotations();
|
$this->resolveAnnotations();
|
||||||
|
@ -57,14 +59,28 @@ class EntityResolver {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function searchField($name) : null|array
|
||||||
|
{
|
||||||
|
try{
|
||||||
|
return $this->field($name, self::KEY_ENTITY_NAME, false) ?: $this->field($name, self::KEY_COLUMN_NAME, false);
|
||||||
|
}
|
||||||
|
catch(\Throwable $e) {
|
||||||
|
if ( $throwException) {
|
||||||
|
throw new \InvalidArgumentException("Can't find entity field's column named `$name` from entity {$this->entityClass}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public function fieldList($fieldKey = self::KEY_ENTITY_NAME, bool $skipVirtual = false) : array
|
public function fieldList($fieldKey = self::KEY_ENTITY_NAME, bool $skipVirtual = false) : array
|
||||||
{
|
{
|
||||||
$fieldList = [];
|
$fieldList = [];
|
||||||
|
|
||||||
foreach($this->properties as $item) {
|
foreach($this->properties as $item) {
|
||||||
foreach($item['tags'] ?? [] as $tag) {
|
foreach($item['tags'] ?? [] as $tag) {
|
||||||
if ( $tag['object'] instanceof Field ) {
|
if ( $tag['object'] instanceof Field or $tag['object'] instanceof Attribute\Property\Field ) {
|
||||||
if ( $skipVirtual && ($tag['object'] instanceof Virtual )) {
|
if ( $skipVirtual && ($tag['object'] instanceof Virtual or $tag['object'] instanceof Attribute\Property\Virtual )) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +117,7 @@ class EntityResolver {
|
||||||
try{
|
try{
|
||||||
if ( null !== ( $this->properties[$name] ?? null ) ) {
|
if ( null !== ( $this->properties[$name] ?? null ) ) {
|
||||||
foreach($this->properties[$name]['tags'] ?? [] as $tag) {
|
foreach($this->properties[$name]['tags'] ?? [] as $tag) {
|
||||||
if ( $tag['object'] instanceof Relation ) {
|
if ( $tag['object'] instanceof Relation or $tag['object'] instanceof Attribute\Property\Relation ) {
|
||||||
return $this->properties[$name];
|
return $this->properties[$name];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,22 +134,25 @@ class EntityResolver {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function searchFieldAnnotation(string $field, Annotation $annotationType, bool $caseSensitive = true) : ? Annotation
|
public function searchFieldAnnotation(string $field, array|object|string $annotationType, bool $caseSensitive = true) : ? object
|
||||||
{
|
{
|
||||||
$found = $this->searchFieldAnnotationList($field, $annotationType, $caseSensitive);
|
return $this->searchFieldAnnotationList($field, $annotationType, $caseSensitive)[0] ?? null;
|
||||||
return $found ? $found[0] : null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function searchFieldAnnotationList(string $field, Annotation $annotationType, bool $caseSensitive = true) : array
|
public function searchFieldAnnotationList(string $field, array|object|string $annotationType, bool $caseSensitive = true) : array
|
||||||
{
|
{
|
||||||
$list = [];
|
$list = [];
|
||||||
|
|
||||||
$search = $caseSensitive ? $this->properties : array_change_key_case($this->properties, \CASE_LOWER);
|
$search = $caseSensitive ? $this->properties : array_change_key_case($this->properties, \CASE_LOWER);
|
||||||
|
|
||||||
|
$annotations = is_array($annotationType) ? $annotationType : [ $annotationType ];
|
||||||
|
|
||||||
if ( null !== ( $search[$field] ?? null ) ) {
|
if ( null !== ( $search[$field] ?? null ) ) {
|
||||||
foreach($search[$field]['tags'] ?? [] as $tag) {
|
foreach($search[$field]['tags'] ?? [] as $tag) {
|
||||||
if ( $tag['object'] instanceof $annotationType ) {
|
foreach($annotations as $annotation) {
|
||||||
$list[] = $tag['object'];
|
if ( $tag['object'] instanceof $annotation ) {
|
||||||
|
$list[] = $tag['object'];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -154,9 +173,9 @@ class EntityResolver {
|
||||||
return $table->name ?? "";
|
return $table->name ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
public function tableAnnotation($required = false) : Table
|
public function tableAnnotation($required = false) : Table|Attribute\Obj\Table
|
||||||
{
|
{
|
||||||
if ( null === $table = $this->getAnnotationFromClassname( Table::class ) ) {
|
if ( null === $table = $this->getTableAttribute() ) {
|
||||||
if ($required) {
|
if ($required) {
|
||||||
throw new \LogicException("Your entity {$this->entityClass} seems to be missing a @Table() annotation");
|
throw new \LogicException("Your entity {$this->entityClass} seems to be missing a @Table() annotation");
|
||||||
}
|
}
|
||||||
|
@ -172,7 +191,7 @@ class EntityResolver {
|
||||||
|
|
||||||
public function sqlAdapter() : \Ulmus\ConnectionAdapter
|
public function sqlAdapter() : \Ulmus\ConnectionAdapter
|
||||||
{
|
{
|
||||||
if ( null !== $table = $this->getAnnotationFromClassname( Table::class ) ) {
|
if ( null !== $table = $this->getTableAttribute() ) {
|
||||||
if ( $table->adapter ?? null ) {
|
if ( $table->adapter ?? null ) {
|
||||||
if ( null === ( $adapter = \Ulmus\Ulmus::$registeredAdapters[$table->adapter] ?? null ) ) {
|
if ( null === ( $adapter = \Ulmus\Ulmus::$registeredAdapters[$table->adapter] ?? null ) ) {
|
||||||
throw new \Exception("Requested database adapter `{$table->adapter}` is not registered.");
|
throw new \Exception("Requested database adapter `{$table->adapter}` is not registered.");
|
||||||
|
@ -196,7 +215,7 @@ class EntityResolver {
|
||||||
|
|
||||||
public function schemaName(bool $required = false) : ? string
|
public function schemaName(bool $required = false) : ? string
|
||||||
{
|
{
|
||||||
if ( null === $table = $this->getAnnotationFromClassname( Table::class ) ) {
|
if ( null === $table = $this->getTableAttribute() ) {
|
||||||
throw new \LogicException("Your entity {$this->entityClass} seems to be missing a @Table() annotation");
|
throw new \LogicException("Your entity {$this->entityClass} seems to be missing a @Table() annotation");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,7 +229,8 @@ class EntityResolver {
|
||||||
public function getPrimaryKeyField() : ? array
|
public function getPrimaryKeyField() : ? array
|
||||||
{
|
{
|
||||||
foreach($this->fieldList() as $key => $value) {
|
foreach($this->fieldList() as $key => $value) {
|
||||||
if ( null !== ( $field = $this->searchFieldAnnotation($key, new Field() ) ) ) {
|
$field = $this->searchFieldAnnotation($key, [ Attribute\Property\Field::class, Field::class ]);
|
||||||
|
if ( null !== $field ) {
|
||||||
if ( false !== ( $field->attributes['primary_key'] ?? false ) ) {
|
if ( false !== ( $field->attributes['primary_key'] ?? false ) ) {
|
||||||
return [ $key => $field ];
|
return [ $key => $field ];
|
||||||
}
|
}
|
||||||
|
@ -230,13 +250,17 @@ class EntityResolver {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function getTableAttribute()
|
||||||
|
{
|
||||||
|
return $this->getAnnotationFromClassname(Attribute\Obj\Table::class, false) ?: $this->getAnnotationFromClassname( Table::class );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transform an annotation into it's object's counterpart
|
* Transform an annotation into it's object's counterpart
|
||||||
*/
|
*/
|
||||||
public function getAnnotationFromClassname(string $className, bool $throwError = true) : ? object
|
public function getAnnotationFromClassname(string $className, bool $throwError = true) : ? object
|
||||||
{
|
{
|
||||||
if ( $name = $this->uses[$className] ?? false ) {
|
if ( $name = $this->uses[$className] ?? false ) {
|
||||||
|
|
||||||
foreach(array_reverse($this->class['tags']) as $item) {
|
foreach(array_reverse($this->class['tags']) as $item) {
|
||||||
if ( $item['tag'] === $name ) {
|
if ( $item['tag'] === $name ) {
|
||||||
return $this->instanciateAnnotationObject($item);
|
return $this->instanciateAnnotationObject($item);
|
||||||
|
@ -270,18 +294,23 @@ class EntityResolver {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function instanciateAnnotationObject(array $tagDefinition) : Annotation
|
public function instanciateAnnotationObject(array|\ReflectionAttribute $tagDefinition) : object
|
||||||
{
|
{
|
||||||
$arguments = $this->extractArguments($tagDefinition['arguments']);
|
if ($tagDefinition instanceof \ReflectionAttribute) {
|
||||||
|
$obj = $tagDefinition->newInstance();
|
||||||
if ( false === $class = array_search($tagDefinition['tag'], $this->uses) ) {
|
|
||||||
throw new \InvalidArgumentException("Annotation class `{$tagDefinition['tag']}` was not found within {$this->entityClass} uses statement (or it's children / traits)");
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
$arguments = $this->extractArguments($tagDefinition['arguments']);
|
||||||
|
|
||||||
$obj = new $class(... $arguments['constructor']);
|
if (false === $class = array_search($tagDefinition['tag'], $this->uses)) {
|
||||||
|
throw new \InvalidArgumentException("Annotation class `{$tagDefinition['tag']}` was not found within {$this->entityClass} uses statement (or it's children / traits)");
|
||||||
|
}
|
||||||
|
|
||||||
foreach($arguments['setter'] as $key => $value) {
|
$obj = new $class(... $arguments['constructor']);
|
||||||
$obj->$key = $value;
|
|
||||||
|
foreach ($arguments['setter'] as $key => $value) {
|
||||||
|
$obj->$key = $value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $obj;
|
return $obj;
|
||||||
|
@ -309,18 +338,18 @@ class EntityResolver {
|
||||||
protected function resolveAnnotations()
|
protected function resolveAnnotations()
|
||||||
{
|
{
|
||||||
foreach($this->class['tags'] as &$tag) {
|
foreach($this->class['tags'] as &$tag) {
|
||||||
$tag['object'] = $this->instanciateAnnotationObject($tag);
|
$tag['object'] ??= $this->instanciateAnnotationObject($tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach($this->properties as &$property) {
|
foreach($this->properties as &$property) {
|
||||||
foreach($property['tags'] as &$tag){
|
foreach($property['tags'] as &$tag){
|
||||||
$tag['object'] = $this->instanciateAnnotationObject($tag);
|
$tag['object'] ??= $this->instanciateAnnotationObject($tag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach($this->methods as &$method) {
|
foreach($this->methods as &$method) {
|
||||||
foreach($method['tags'] as &$tag){
|
foreach($method['tags'] as &$tag){
|
||||||
$tag['object'] = $this->instanciateAnnotationObject($tag);
|
$tag['object'] ??= $this->instanciateAnnotationObject($tag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@ class ConnectionAdapter
|
||||||
|
|
||||||
public function pdo() : PdoObject
|
public function pdo() : PdoObject
|
||||||
{
|
{
|
||||||
return $this->pdo ?? $this->pdo = $this->connect()->pdo;
|
return $this->pdo ?? $this->connect()->pdo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function connector() : object
|
public function connector() : object
|
||||||
|
|
|
@ -66,7 +66,7 @@ trait EntityTrait {
|
||||||
elseif ( EntityField::isScalarType($field['type']) ) {
|
elseif ( EntityField::isScalarType($field['type']) ) {
|
||||||
|
|
||||||
if ( $field['type'] === 'string' ) {
|
if ( $field['type'] === 'string' ) {
|
||||||
$annotation = $entityResolver->searchFieldAnnotation($field['name'], new Field() );
|
$annotation = $entityResolver->searchFieldAnnotation($field['name'], [ Attribute\Property\Field::class, Field::class ] );
|
||||||
|
|
||||||
if ( $annotation->length ?? null ) {
|
if ( $annotation->length ?? null ) {
|
||||||
$value = mb_substr($value, 0, $annotation->length);
|
$value = mb_substr($value, 0, $annotation->length);
|
||||||
|
@ -90,7 +90,6 @@ trait EntityTrait {
|
||||||
}
|
}
|
||||||
catch(\Error $e) {
|
catch(\Error $e) {
|
||||||
$f = $field['type'];
|
$f = $field['type'];
|
||||||
dump($f, $f::from($value));
|
|
||||||
throw new \Error(sprintf("%s for class '%s' on field '%s'", $e->getMessage(), get_class($this), $field['name']));
|
throw new \Error(sprintf("%s for class '%s' on field '%s'", $e->getMessage(), get_class($this), $field['name']));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,17 +125,11 @@ trait EntityTrait {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @Ignore
|
|
||||||
*/
|
|
||||||
public function fromArray(iterable $dataset) : self
|
public function fromArray(iterable $dataset) : self
|
||||||
{
|
{
|
||||||
return $this->entityFillFromDataset($dataset);
|
return $this->entityFillFromDataset($dataset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @Ignore
|
|
||||||
*/
|
|
||||||
public function entityGetDataset(bool $includeRelations = false, bool $returnSource = false) : array
|
public function entityGetDataset(bool $includeRelations = false, bool $returnSource = false) : array
|
||||||
{
|
{
|
||||||
if ( $returnSource ) {
|
if ( $returnSource ) {
|
||||||
|
@ -148,7 +141,7 @@ trait EntityTrait {
|
||||||
$entityResolver = $this->resolveEntity();
|
$entityResolver = $this->resolveEntity();
|
||||||
|
|
||||||
foreach($entityResolver->fieldList(Common\EntityResolver::KEY_ENTITY_NAME, true) as $key => $field) {
|
foreach($entityResolver->fieldList(Common\EntityResolver::KEY_ENTITY_NAME, true) as $key => $field) {
|
||||||
$annotation = $entityResolver->searchFieldAnnotation($key, new Field() );
|
$annotation = $entityResolver->searchFieldAnnotation($key, [ Attribute\Property\Field::class, Field::class ]);
|
||||||
|
|
||||||
if ( isset($this->$key) ) {
|
if ( isset($this->$key) ) {
|
||||||
$dataset[$annotation->name ?? $key] = static::repository()->adapter->adapter()->writableValue($this->$key);
|
$dataset[$annotation->name ?? $key] = static::repository()->adapter->adapter()->writableValue($this->$key);
|
||||||
|
@ -161,7 +154,7 @@ trait EntityTrait {
|
||||||
# @TODO Must fix recursive bug !
|
# @TODO Must fix recursive bug !
|
||||||
if ($includeRelations) {
|
if ($includeRelations) {
|
||||||
foreach($entityResolver->properties as $name => $field){
|
foreach($entityResolver->properties as $name => $field){
|
||||||
$relation = $entityResolver->searchFieldAnnotation($name, new Relation() );
|
$relation = $entityResolver->searchFieldAnnotation($key, [ Attribute\Property\Relation::class. Relation::class ] );
|
||||||
|
|
||||||
if ( $relation && isset($this->$name) && ($relation->entity ?? $relation->bridge) !== static::class ) {
|
if ( $relation && isset($this->$name) && ($relation->entity ?? $relation->bridge) !== static::class ) {
|
||||||
if ( null !== $value = $this->$name ?? null ) {
|
if ( null !== $value = $this->$name ?? null ) {
|
||||||
|
@ -185,9 +178,6 @@ trait EntityTrait {
|
||||||
return $dataset;
|
return $dataset;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @Ignore
|
|
||||||
*/
|
|
||||||
public function toArray($includeRelations = false, array $filterFields = null) : array
|
public function toArray($includeRelations = false, array $filterFields = null) : array
|
||||||
{
|
{
|
||||||
$dataset = $this->entityGetDataset($includeRelations);
|
$dataset = $this->entityGetDataset($includeRelations);
|
||||||
|
@ -195,17 +185,11 @@ trait EntityTrait {
|
||||||
return $filterFields ? array_intersect_key($dataset, array_flip($filterFields)) : $dataset;
|
return $filterFields ? array_intersect_key($dataset, array_flip($filterFields)) : $dataset;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @Ignore
|
|
||||||
*/
|
|
||||||
public function toCollection() : EntityCollection
|
public function toCollection() : EntityCollection
|
||||||
{
|
{
|
||||||
return static::entityCollection([ $this ]);
|
return static::entityCollection([ $this ]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @Ignore
|
|
||||||
*/
|
|
||||||
public function isLoaded() : bool
|
public function isLoaded() : bool
|
||||||
{
|
{
|
||||||
if (empty($this->entityLoadedDataset)) {
|
if (empty($this->entityLoadedDataset)) {
|
||||||
|
@ -221,9 +205,6 @@ trait EntityTrait {
|
||||||
return isset($this->$key);
|
return isset($this->$key);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @Ignore
|
|
||||||
*/
|
|
||||||
public function __get(string $name)
|
public function __get(string $name)
|
||||||
{
|
{
|
||||||
$relation = new Repository\RelationBuilder($this);
|
$relation = new Repository\RelationBuilder($this);
|
||||||
|
@ -235,32 +216,25 @@ trait EntityTrait {
|
||||||
throw new \Exception(sprintf("[%s] - Undefined variable: %s", static::class, $name));
|
throw new \Exception(sprintf("[%s] - Undefined variable: %s", static::class, $name));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @Ignore
|
|
||||||
*/
|
|
||||||
public function __isset(string $name) : bool
|
public function __isset(string $name) : bool
|
||||||
{
|
{
|
||||||
#if ( null !== $relation = static::resolveEntity()->searchFieldAnnotation($name, new Relation() ) ) {
|
#if ( null !== $relation = static::resolveEntity()->searchFieldAnnotation($name, new Relation() ) ) {
|
||||||
# return isset($this->{$relation->key});
|
# return isset($this->{$relation->key});
|
||||||
#}
|
#}
|
||||||
if ( $this->isLoaded() && static::resolveEntity()->searchFieldAnnotation($name, new Relation() ) ) {
|
$rel = static::resolveEntity()->searchFieldAnnotation($name, [ Attribute\Property\Relation::class, Relation::class ]);
|
||||||
|
|
||||||
|
if ( $this->isLoaded() && $rel ) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return isset($this->$name);
|
return isset($this->$name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @Ignore
|
|
||||||
*/
|
|
||||||
public function __sleep()
|
public function __sleep()
|
||||||
{
|
{
|
||||||
return array_keys($this->resolveEntity()->fieldList());
|
return array_keys($this->resolveEntity()->fieldList());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @Ignore
|
|
||||||
*/
|
|
||||||
public function __clone()
|
public function __clone()
|
||||||
{
|
{
|
||||||
foreach($this as $prop) {
|
foreach($this as $prop) {
|
||||||
|
@ -274,33 +248,21 @@ trait EntityTrait {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @Ignore
|
|
||||||
*/
|
|
||||||
public function jsonSerialize()
|
public function jsonSerialize()
|
||||||
{
|
{
|
||||||
return $this->entityGetDataset();
|
return $this->entityGetDataset();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @Ignore
|
|
||||||
*/
|
|
||||||
public static function resolveEntity() : EntityResolver
|
public static function resolveEntity() : EntityResolver
|
||||||
{
|
{
|
||||||
return Ulmus::resolveEntity(static::class);
|
return Ulmus::resolveEntity(static::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @Ignore
|
|
||||||
*/
|
|
||||||
public static function repository(string $alias = Repository::DEFAULT_ALIAS, ConnectionAdapter $adapter = null) : Repository
|
public static function repository(string $alias = Repository::DEFAULT_ALIAS, ConnectionAdapter $adapter = null) : Repository
|
||||||
{
|
{
|
||||||
return Ulmus::repository(static::class, $alias, $adapter);
|
return Ulmus::repository(static::class, $alias, $adapter);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @Ignore
|
|
||||||
*/
|
|
||||||
public static function entityCollection(...$arguments) : EntityCollection
|
public static function entityCollection(...$arguments) : EntityCollection
|
||||||
{
|
{
|
||||||
$collection = new EntityCollection(...$arguments);
|
$collection = new EntityCollection(...$arguments);
|
||||||
|
@ -309,26 +271,16 @@ trait EntityTrait {
|
||||||
return $collection;
|
return $collection;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @Ignore
|
|
||||||
*/
|
|
||||||
public static function queryBuilder() : QueryBuilder
|
public static function queryBuilder() : QueryBuilder
|
||||||
{
|
{
|
||||||
return Ulmus::queryBuilder(static::class);
|
return Ulmus::queryBuilder(static::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @Ignore
|
|
||||||
*/
|
|
||||||
public static function field($name, ? string $alias = Repository::DEFAULT_ALIAS) : EntityField
|
public static function field($name, ? string $alias = Repository::DEFAULT_ALIAS) : EntityField
|
||||||
{
|
{
|
||||||
|
|
||||||
return new EntityField(static::class, $name, $alias ? Ulmus::repository(static::class)->adapter->adapter()->escapeIdentifier($alias, Adapter\AdapterInterface::IDENTIFIER_FIELD) : Repository::DEFAULT_ALIAS, Ulmus::resolveEntity(static::class));
|
return new EntityField(static::class, $name, $alias ? Ulmus::repository(static::class)->adapter->adapter()->escapeIdentifier($alias, Adapter\AdapterInterface::IDENTIFIER_FIELD) : Repository::DEFAULT_ALIAS, Ulmus::resolveEntity(static::class));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @Ignore
|
|
||||||
*/
|
|
||||||
public static function fields(array $fields, ? string $alias = Repository::DEFAULT_ALIAS) : string
|
public static function fields(array $fields, ? string $alias = Repository::DEFAULT_ALIAS) : string
|
||||||
{
|
{
|
||||||
return implode(', ', array_map(function($item) use ($alias){
|
return implode(', ', array_map(function($item) use ($alias){
|
||||||
|
|
|
@ -4,6 +4,7 @@ namespace Ulmus\Migration;
|
||||||
|
|
||||||
use Ulmus\Adapter\AdapterInterface;
|
use Ulmus\Adapter\AdapterInterface;
|
||||||
use Ulmus\Annotation\Property\Field;
|
use Ulmus\Annotation\Property\Field;
|
||||||
|
use Ulmus\Attribute;
|
||||||
use Ulmus\Entity;
|
use Ulmus\Entity;
|
||||||
|
|
||||||
class FieldDefinition {
|
class FieldDefinition {
|
||||||
|
@ -40,7 +41,7 @@ class FieldDefinition {
|
||||||
$this->type = $field->type ?? $data['type'];
|
$this->type = $field->type ?? $data['type'];
|
||||||
$this->length = $field->length ?? null;
|
$this->length = $field->length ?? null;
|
||||||
$this->precision = $field->precision ?? null;
|
$this->precision = $field->precision ?? null;
|
||||||
$this->nullable = isset($field->nullable) ? $field->nullable : $data['nullable'];
|
$this->nullable = $data['nullable'] ?: $field->nullable;
|
||||||
$this->update = $field->attributes['update'] ?? null;
|
$this->update = $field->attributes['update'] ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,9 +70,9 @@ class FieldDefinition {
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFieldTag() : ? Field
|
public function getFieldTag() : Field|Attribute\Property\Field|null
|
||||||
{
|
{
|
||||||
$field = array_filter($this->tags, fn($item) => $item['object'] instanceof Field);
|
$field = array_filter($this->tags, fn($item) => $item['object'] instanceof Field || $item['object'] instanceof Attribute\Property\Field);
|
||||||
|
|
||||||
return array_pop($field)['object'];
|
return array_pop($field)['object'];
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,16 @@
|
||||||
|
|
||||||
namespace Ulmus;
|
namespace Ulmus;
|
||||||
|
|
||||||
use Ulmus\Annotation\Property\{Field, Where, Having, Relation, Filter, Join, FilterJoin, WithJoin, Relation\Ignore as RelationIgnore};
|
use Ulmus\Annotation\Property\{Field,
|
||||||
|
OrderBy,
|
||||||
|
Where,
|
||||||
|
Having,
|
||||||
|
Relation,
|
||||||
|
Filter,
|
||||||
|
Join,
|
||||||
|
FilterJoin,
|
||||||
|
WithJoin,
|
||||||
|
Relation\Ignore as RelationIgnore};
|
||||||
use Ulmus\Common\EntityResolver;
|
use Ulmus\Common\EntityResolver;
|
||||||
|
|
||||||
class Repository
|
class Repository
|
||||||
|
@ -332,7 +341,7 @@ class Repository
|
||||||
$dataset = $this->generateDatasetDiff($entity, $oldValues);
|
$dataset = $this->generateDatasetDiff($entity, $oldValues);
|
||||||
|
|
||||||
foreach($dataset as $field => $value) {
|
foreach($dataset as $field => $value) {
|
||||||
if ( false === ( $this->entityResolver->searchFieldAnnotation($field, new Field, false)->readonly ?? false ) ) {
|
if ( false === ( $this->entityResolver->searchFieldAnnotation($field, [ Attribute\Property\Field::class, Field::class ], false)->readonly ?? false ) ) {
|
||||||
$intersect[$field] = $field;
|
$intersect[$field] = $field;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -367,7 +376,7 @@ class Repository
|
||||||
$prependField and ($prependField .= "$");
|
$prependField and ($prependField .= "$");
|
||||||
|
|
||||||
foreach ($entity::resolveEntity()->fieldList(Common\EntityResolver::KEY_COLUMN_NAME, true) as $key => $field) {
|
foreach ($entity::resolveEntity()->fieldList(Common\EntityResolver::KEY_COLUMN_NAME, true) as $key => $field) {
|
||||||
if (null === $entity::resolveEntity()->searchFieldAnnotation($field['name'], new RelationIgnore)) {
|
if (null === $entity::resolveEntity()->searchFieldAnnotation($field['name'], [ Attribute\Property\Relation\Ignore::class, RelationIgnore::class ])) {
|
||||||
$this->select(sprintf("%s.$key as {$prependField}{$field['name']}", $this->escapeIdentifier($alias)));
|
$this->select(sprintf("%s.$key as {$prependField}{$field['name']}", $this->escapeIdentifier($alias)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -380,7 +389,7 @@ class Repository
|
||||||
$fieldlist = [];
|
$fieldlist = [];
|
||||||
|
|
||||||
foreach ($entity::resolveEntity()->fieldList(Common\EntityResolver::KEY_COLUMN_NAME, true) as $key => $field) {
|
foreach ($entity::resolveEntity()->fieldList(Common\EntityResolver::KEY_COLUMN_NAME, true) as $key => $field) {
|
||||||
if (null === $entity::resolveEntity()->searchFieldAnnotation($field['name'], new RelationIgnore)) {
|
if (null === $entity::resolveEntity()->searchFieldAnnotation($field['name'], [ Attribute\Property\Relation\Ignore::class, RelationIgnore::class ])) {
|
||||||
$fieldlist[] = $key;
|
$fieldlist[] = $key;
|
||||||
$fieldlist[] = $entity::field($field['name'], $this->escapeIdentifier($alias));
|
$fieldlist[] = $entity::field($field['name'], $this->escapeIdentifier($alias));
|
||||||
}
|
}
|
||||||
|
@ -574,7 +583,7 @@ class Repository
|
||||||
return $this->where($primaryKeyField[$pkField]->name ?? $pkField, $value);
|
return $this->where($primaryKeyField[$pkField]->name ?? $pkField, $value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function withJoin(/*string|array*/ $fields) : self
|
public function withJoin(string|array $fields) : self
|
||||||
{
|
{
|
||||||
if ( null === $this->queryBuilder->getFragment(Query\Select::class) ) {
|
if ( null === $this->queryBuilder->getFragment(Query\Select::class) ) {
|
||||||
$this->select("{$this->alias}.*");
|
$this->select("{$this->alias}.*");
|
||||||
|
@ -582,10 +591,12 @@ class Repository
|
||||||
|
|
||||||
# @TODO Apply FILTER annotation to this too !
|
# @TODO Apply FILTER annotation to this too !
|
||||||
foreach(array_filter((array) $fields) as $item) {
|
foreach(array_filter((array) $fields) as $item) {
|
||||||
$annotation = $this->entityResolver->searchFieldAnnotation($item, new Join) ?:
|
$annotation = $this->entityResolver->searchFieldAnnotation($item, [ Attribute\Property\Join::class, Join::class ]) ?:
|
||||||
$this->entityResolver->searchFieldAnnotation($item, new Relation);
|
$this->entityResolver->searchFieldAnnotation($item, [ Attribute\Property\Relation::class, Relation::class ]);
|
||||||
|
|
||||||
if (( $annotation instanceof Relation ) && ( $annotation->normalizeType() === 'manytomany' )) {
|
$isRelation = ( $annotation instanceof Relation ) || ($annotation instanceof Attribute\Property\Relation);
|
||||||
|
|
||||||
|
if ($isRelation && ( $annotation->normalizeType() === 'manytomany' )) {
|
||||||
throw new Exception("Many-to-many relation can not be preloaded within joins.");
|
throw new Exception("Many-to-many relation can not be preloaded within joins.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -595,10 +606,10 @@ class Repository
|
||||||
$entity = $annotation->entity ?? $this->entityResolver->properties[$item]['type'];
|
$entity = $annotation->entity ?? $this->entityResolver->properties[$item]['type'];
|
||||||
|
|
||||||
foreach($entity::resolveEntity()->fieldList(Common\EntityResolver::KEY_COLUMN_NAME, true) as $key => $field) {
|
foreach($entity::resolveEntity()->fieldList(Common\EntityResolver::KEY_COLUMN_NAME, true) as $key => $field) {
|
||||||
if ( null === $entity::resolveEntity()->searchFieldAnnotation($field['name'], new RelationIgnore) ) {
|
if ( null === $entity::resolveEntity()->searchFieldAnnotation($field['name'], [ Attribute\Property\Relation\Ignore::class, RelationIgnore::class ]) ) {
|
||||||
$escAlias = $this->escapeIdentifier($alias);
|
$escAlias = $this->escapeIdentifier($alias);
|
||||||
|
|
||||||
$name = $entity::resolveEntity()->searchFieldAnnotation($field['name'], new Field())->name ?? $field['name'];
|
$name = $entity::resolveEntity()->searchFieldAnnotation($field['name'], [ Attribute\Property\Field::class, Field::class ])->name ?? $field['name'];
|
||||||
|
|
||||||
$this->select("$escAlias.$key as $alias\${$name}");
|
$this->select("$escAlias.$key as $alias\${$name}");
|
||||||
}
|
}
|
||||||
|
@ -606,17 +617,17 @@ class Repository
|
||||||
|
|
||||||
$this->open();
|
$this->open();
|
||||||
|
|
||||||
foreach($this->entityResolver->searchFieldAnnotationList($item, new Where() ) as $condition) {
|
foreach($this->entityResolver->searchFieldAnnotationList($item, [ Attribute\Property\Where::class, Where::class ] ) as $condition) {
|
||||||
if ( is_object($condition->field) && ( $condition->field->entityClass !== $entity ) ) {
|
if ( is_object($condition->field) && ( $condition->field->entityClass !== $entity ) ) {
|
||||||
$this->where(is_object($condition->field) ? $condition->field : $entity::field($condition->field), $condition->value, $condition->operator);
|
$this->where(is_object($condition->field) ? $condition->field : $entity::field($condition->field), $condition->value, $condition->operator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach($this->entityResolver->searchFieldAnnotationList($item, new Having() ) as $condition) {
|
foreach($this->entityResolver->searchFieldAnnotationList($item, [ Attribute\Property\Having::class, Having::class ] ) as $condition) {
|
||||||
$this->having(is_object($condition->field) ? $condition->field : $entity::field($condition->field), $condition->value, $condition->operator);
|
$this->having(is_object($condition->field) ? $condition->field : $entity::field($condition->field), $condition->value, $condition->operator);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach($this->entityResolver->searchFieldAnnotationList($item, new Filter() ) as $filter) {
|
foreach($this->entityResolver->searchFieldAnnotationList($item, [ Attribute\Property\Filter::class, Filter::class ] ) as $filter) {
|
||||||
call_user_func_array([ $this->entityClass, $filter->method ], [ $this, $item, true ]);
|
call_user_func_array([ $this->entityClass, $filter->method ], [ $this, $item, true ]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -627,8 +638,7 @@ class Repository
|
||||||
$foreignKey = is_string($annotation->foreignKey) ? $entity::field($annotation->foreignKey, $alias) : $annotation->foreignKey;
|
$foreignKey = is_string($annotation->foreignKey) ? $entity::field($annotation->foreignKey, $alias) : $annotation->foreignKey;
|
||||||
|
|
||||||
$this->join("LEFT", $entity::resolveEntity()->tableName(), $key, $foreignKey, $alias, function($join) use ($item, $entity, $alias) {
|
$this->join("LEFT", $entity::resolveEntity()->tableName(), $key, $foreignKey, $alias, function($join) use ($item, $entity, $alias) {
|
||||||
|
foreach($this->entityResolver->searchFieldAnnotationList($item, [ Attribute\Property\Where::class, Where::class ]) as $condition) {
|
||||||
foreach($this->entityResolver->searchFieldAnnotationList($item, new Where() ) as $condition) {
|
|
||||||
if ( ! is_object($condition->field) ) {
|
if ( ! is_object($condition->field) ) {
|
||||||
$field = $this->entityClass::field($condition->field);
|
$field = $this->entityClass::field($condition->field);
|
||||||
}
|
}
|
||||||
|
@ -644,7 +654,7 @@ class Repository
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach($this->entityResolver->searchFieldAnnotationList($item, new FilterJoin() ) as $filter) {
|
foreach($this->entityResolver->searchFieldAnnotationList($item, [ Attribute\Property\FilterJoin::class, FilterJoin::class ]) as $filter) {
|
||||||
call_user_func_array([ $this->entityClass, $filter->method ], [ $join, $item, true ]);
|
call_user_func_array([ $this->entityClass, $filter->method ], [ $join, $item, true ]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -670,7 +680,7 @@ class Repository
|
||||||
|
|
||||||
# Apply FILTER annotation to this too !
|
# Apply FILTER annotation to this too !
|
||||||
foreach(array_filter((array) $fields) as $item) {
|
foreach(array_filter((array) $fields) as $item) {
|
||||||
if ( $relation = $this->entityResolver->searchFieldAnnotation($item, new Relation) ) {
|
if ( $relation = $this->entityResolver->searchFieldAnnotation($item, [ Attribute\Property\Relation::class, Relation::class ]) ) {
|
||||||
$alias = $relation->alias ?? $item;
|
$alias = $relation->alias ?? $item;
|
||||||
|
|
||||||
if ( $relation->isManyToMany() ) {
|
if ( $relation->isManyToMany() ) {
|
||||||
|
@ -691,11 +701,11 @@ class Repository
|
||||||
|
|
||||||
# $relation->isManyToMany() and $repository->selectJsonEntity($relation->bridge, $relation->bridgeField, true);
|
# $relation->isManyToMany() and $repository->selectJsonEntity($relation->bridge, $relation->bridgeField, true);
|
||||||
|
|
||||||
foreach($this->entityResolver->searchFieldAnnotationList($item, new Where() ) as $condition) {
|
foreach($this->entityResolver->searchFieldAnnotationList($item, [ Attribute\Property\Where::class, Where::class ]) as $condition) {
|
||||||
$repository->where(is_object($condition->field) ? $condition->field : $entity::field($condition->field), $condition->value, $condition->operator);
|
$repository->where(is_object($condition->field) ? $condition->field : $entity::field($condition->field), $condition->value, $condition->operator);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach($this->entityResolver->searchFieldAnnotationList($item, new Having() ) as $condition) {
|
foreach($this->entityResolver->searchFieldAnnotationList($item, [ Attribute\Property\Having::class, Having::class ] ) as $condition) {
|
||||||
$repository->having(is_object($condition->field) ? $condition->field : $entity::field($condition->field), $condition->value, $condition->operator);
|
$repository->having(is_object($condition->field) ? $condition->field : $entity::field($condition->field), $condition->value, $condition->operator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -722,9 +732,9 @@ class Repository
|
||||||
public function loadCollectionRelation(EntityCollection $collection, array|string $fields) : void
|
public function loadCollectionRelation(EntityCollection $collection, array|string $fields) : void
|
||||||
{
|
{
|
||||||
foreach ((array)$fields as $name) {
|
foreach ((array)$fields as $name) {
|
||||||
if (null !== ($relation = $this->entityResolver->searchFieldAnnotation($name, new Annotation\Property\Relation()))) {
|
if (null !== ($relation = $this->entityResolver->searchFieldAnnotation($name, [ Attribute\Property\Relation::class, Relation::class ] ))) {
|
||||||
$order = $this->entityResolver->searchFieldAnnotationList($name, new Annotation\Property\OrderBy());
|
$order = $this->entityResolver->searchFieldAnnotationList($name, [ Attribute\Property\OrderBy::class, OrderBy::class ]);
|
||||||
$where = $this->entityResolver->searchFieldAnnotationList($name, new Annotation\Property\Where());
|
$where = $this->entityResolver->searchFieldAnnotationList($name, [ Attribute\Property\Where::class, Where::class ]);
|
||||||
|
|
||||||
$baseEntity = $relation->entity ?? $relation->bridge ?? $this->entityResolver->properties[$name]['type'];
|
$baseEntity = $relation->entity ?? $relation->bridge ?? $this->entityResolver->properties[$name]['type'];
|
||||||
$baseEntityResolver = $baseEntity::resolveEntity();
|
$baseEntityResolver = $baseEntity::resolveEntity();
|
||||||
|
@ -735,7 +745,7 @@ class Repository
|
||||||
$repository = $baseEntity::repository();
|
$repository = $baseEntity::repository();
|
||||||
|
|
||||||
foreach ($baseEntityResolver->fieldList(Common\EntityResolver::KEY_COLUMN_NAME, true) as $key => $field) {
|
foreach ($baseEntityResolver->fieldList(Common\EntityResolver::KEY_COLUMN_NAME, true) as $key => $field) {
|
||||||
if (null === $baseEntityResolver->searchFieldAnnotation($field['name'], new RelationIgnore)) {
|
if (null === $baseEntityResolver->searchFieldAnnotation($field['name'], [ Attribute\Property\Relation\Ignore::class, RelationIgnore::class ])) {
|
||||||
$repository->select($baseEntityResolver->entityClass::field($key));
|
$repository->select($baseEntityResolver->entityClass::field($key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,17 @@
|
||||||
|
|
||||||
namespace Ulmus\Repository;
|
namespace Ulmus\Repository;
|
||||||
|
|
||||||
use Ulmus\{ Ulmus, Annotation, Query, Common, Common\EntityResolver, Repository, Event, EntityCollection, };
|
use Ulmus\{Attribute\Property\Field,
|
||||||
|
Ulmus,
|
||||||
|
Annotation,
|
||||||
|
Attribute,
|
||||||
|
Query,
|
||||||
|
Common,
|
||||||
|
Common\EntityResolver,
|
||||||
|
Repository,
|
||||||
|
Event,
|
||||||
|
EntityCollection};
|
||||||
|
|
||||||
use Ulmus\Annotation\Property\{Filter, OrderBy, Relation, Relation\Ignore as RelationIgnore, Where, WithJoin, };
|
use Ulmus\Annotation\Property\{Filter, OrderBy, Relation, Relation\Ignore as RelationIgnore, Where, WithJoin, };
|
||||||
|
|
||||||
use Closure;
|
use Closure;
|
||||||
|
@ -15,7 +25,7 @@ class RelationBuilder
|
||||||
|
|
||||||
protected Repository $repository;
|
protected Repository $repository;
|
||||||
|
|
||||||
protected /*object|string*/ $entity;
|
protected object|string $entity;
|
||||||
|
|
||||||
protected EntityResolver $resolver;
|
protected EntityResolver $resolver;
|
||||||
|
|
||||||
|
@ -27,7 +37,7 @@ class RelationBuilder
|
||||||
|
|
||||||
protected array $joins;
|
protected array $joins;
|
||||||
|
|
||||||
public function __construct(/*string|object*/ $entity, ? Repository $repository = null)
|
public function __construct(string|object $entity, ? Repository $repository = null)
|
||||||
{
|
{
|
||||||
$this->entity = $entity;
|
$this->entity = $entity;
|
||||||
$this->resolver = $entity::resolveEntity();
|
$this->resolver = $entity::resolveEntity();
|
||||||
|
@ -48,7 +58,7 @@ class RelationBuilder
|
||||||
return $this->resolveRelation($name) ?: $this->resolveVirtual($name) ?: false;
|
return $this->resolveRelation($name) ?: $this->resolveVirtual($name) ?: false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if ( $relation = $this->resolver->searchFieldAnnotation($name, new Relation() ) ) {
|
if ( $relation = $this->resolver->searchFieldAnnotation($name, [ Attribute\Property\Relation::class , Relation::class ] ) ) {
|
||||||
return $this->instanciateEmptyObject($name, $relation);
|
return $this->instanciateEmptyObject($name, $relation);
|
||||||
}
|
}
|
||||||
elseif ($virtual = $this->resolveVirtual($name)) {
|
elseif ($virtual = $this->resolveVirtual($name)) {
|
||||||
|
@ -59,9 +69,9 @@ class RelationBuilder
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function resolveVirtual(string $name) /* : bool|object|EntityCollection */
|
protected function resolveVirtual(string $name) : bool|object
|
||||||
{
|
{
|
||||||
if (null !== ($virtual = $this->resolver->searchFieldAnnotation($name, new Annotation\Property\Virtual()))) {
|
if (null !== ($virtual = $this->resolver->searchFieldAnnotation($name, [ Attribute\Property\Virtual::class, Annotation\Property\Virtual::class ]))) {
|
||||||
if ($virtual->closure ?? false) {
|
if ($virtual->closure ?? false) {
|
||||||
return call_user_func_array($virtual->closure, [ $this->entity ]);
|
return call_user_func_array($virtual->closure, [ $this->entity ]);
|
||||||
}
|
}
|
||||||
|
@ -72,17 +82,18 @@ class RelationBuilder
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function resolveRelation(string $name) /* : bool|object|EntityCollection */
|
protected function resolveRelation(string $name) : bool|object
|
||||||
{
|
{
|
||||||
if ( null !== ( $relation = $this->resolver->searchFieldAnnotation($name, new Relation() ) ) ) {
|
if ( null !== ( $relation = $this->resolver->searchFieldAnnotation($name, [ Attribute\Property\Relation::class, Relation::class ] ) ) ) {
|
||||||
$this->orders = $this->resolver->searchFieldAnnotationList($name, new OrderBy() );
|
$this->orders = $this->resolver->searchFieldAnnotationList($name, [ Attribute\Property\OrderBy::class, OrderBy::class ] );
|
||||||
$this->wheres = $this->resolver->searchFieldAnnotationList($name, new Where() );
|
$this->wheres = $this->resolver->searchFieldAnnotationList($name, [ Attribute\Property\Where::class, Where::class ] );
|
||||||
$this->filters = $this->resolver->searchFieldAnnotationList($name, new Filter() );
|
$this->filters = $this->resolver->searchFieldAnnotationList($name, [ Attribute\Property\Filter::class, Filter::class ] );
|
||||||
$this->joins = $this->resolver->searchFieldAnnotationList($name, new WithJoin() );
|
$this->joins = $this->resolver->searchFieldAnnotationList($name, [ Attribute\Property\WithJoin::class, WithJoin::class ] );
|
||||||
|
|
||||||
switch( true ) {
|
switch( true ) {
|
||||||
case $relation->isOneToOne():
|
case $relation->isOneToOne():
|
||||||
if ( $relation->hasBridge() ) {
|
if ( $relation->hasBridge() ) {
|
||||||
|
#dump($name, $relation);
|
||||||
# @TODO ! dump($this->relationAnnotations($name, $relation));
|
# @TODO ! dump($this->relationAnnotations($name, $relation));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -155,7 +166,7 @@ class RelationBuilder
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function instanciateEmptyEntity(string $name, Relation $relation) : object
|
protected function instanciateEmptyEntity(string $name, Relation|Attribute\Property\Relation $relation) : object
|
||||||
{
|
{
|
||||||
$class = $relation->entity ?? $this->resolver->properties[$name]['type'];
|
$class = $relation->entity ?? $this->resolver->properties[$name]['type'];
|
||||||
|
|
||||||
|
@ -163,7 +174,7 @@ class RelationBuilder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected function instanciateEmptyObject(string $name, Relation $relation) : object
|
protected function instanciateEmptyObject(string $name, Relation|Attribute\Property\Relation $relation) : object
|
||||||
{
|
{
|
||||||
switch( true ) {
|
switch( true ) {
|
||||||
case $relation->isOneToOne():
|
case $relation->isOneToOne():
|
||||||
|
@ -181,16 +192,18 @@ class RelationBuilder
|
||||||
return new $class();
|
return new $class();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function fetchFromDataset($name, ? array $data = null) /* object|bool */
|
protected function fetchFromDataset($name, ? array $data = null) : object|bool
|
||||||
{
|
{
|
||||||
$annotation = $this->resolver->searchFieldAnnotation($name, new Annotation\Property\Join) ?:
|
$annotation = $this->resolver->searchFieldAnnotation($name, [ Attribute\Property\Join::class, Annotation\Property\Join::class ]) ?:
|
||||||
$this->resolver->searchFieldAnnotation($name, new Annotation\Property\Relation);
|
$this->resolver->searchFieldAnnotation($name, [ Attribute\Property\Relation::class, Annotation\Property\Relation::class ]);
|
||||||
|
|
||||||
if ( $annotation ) {
|
if ( $annotation ) {
|
||||||
$vars = [];
|
$vars = [];
|
||||||
$len = strlen( $name ) + 1;
|
$len = strlen( $name ) + 1;
|
||||||
|
|
||||||
if ( ( $annotation instanceof Relation ) && $annotation->isManyToMany() ) {
|
$isRelation = ( $annotation instanceof Relation ) || ($annotation instanceof Attribute\Property\Relation);
|
||||||
|
|
||||||
|
if ( $isRelation && $annotation->isManyToMany() ) {
|
||||||
$entity = $this->relationAnnotations($name, $annotation)['relationRelation']->entity;
|
$entity = $this->relationAnnotations($name, $annotation)['relationRelation']->entity;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -231,12 +244,12 @@ class RelationBuilder
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function oneToOne(string $name, Relation $relation) : Repository
|
public function oneToOne(string $name, Relation|Attribute\Property\Relation $relation) : Repository
|
||||||
{
|
{
|
||||||
return $this->oneToMany($name, $relation)->limit(1);
|
return $this->oneToMany($name, $relation)->limit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function oneToMany(string $name, Relation $relation) : Repository
|
public function oneToMany(string $name, Relation|Attribute\Property\Relation $relation) : Repository
|
||||||
{
|
{
|
||||||
$baseEntity = $relation->entity ?? $this->resolver->properties[$name]['type'];
|
$baseEntity = $relation->entity ?? $this->resolver->properties[$name]['type'];
|
||||||
|
|
||||||
|
@ -249,13 +262,17 @@ class RelationBuilder
|
||||||
$field = $relation->key;
|
$field = $relation->key;
|
||||||
|
|
||||||
if ($relation->foreignKey) {
|
if ($relation->foreignKey) {
|
||||||
$this->repository->where( is_object($relation->foreignKey) ? $relation->foreignKey : $baseEntity::field($relation->foreignKey), ! is_string($field) && is_callable($field) ? $field($this->entity) : $this->entity->$field );
|
# dump($baseEntity::resolveEntity()->searchField($field));
|
||||||
|
|
||||||
|
# dump($this->resolver->properties[$name], $field);
|
||||||
|
$value = ! is_string($field) && is_callable($field) ? $field($this->entity) : $this->entity->$field;
|
||||||
|
$this->repository->where( is_object($relation->foreignKey) ? $relation->foreignKey : $baseEntity::field($relation->foreignKey), $value );
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->applyFilter($this->repository, $name);
|
return $this->applyFilter($this->repository, $name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function manyToMany(string $name, Relation $relation, ? Relation & $relationRelation = null, bool $selectBridgeField = true) : Repository
|
public function manyToMany(string $name, Relation|Attribute\Property\Relation $relation, Relation|Attribute\Property\Relation & $relationRelation = null, bool $selectBridgeField = true) : Repository
|
||||||
{
|
{
|
||||||
extract($this->relationAnnotations($name, $relation));
|
extract($this->relationAnnotations($name, $relation));
|
||||||
|
|
||||||
|
@ -272,7 +289,6 @@ class RelationBuilder
|
||||||
|
|
||||||
})->where( $this->entity::field($bridgeRelation->foreignKey, $relationAlias), is_string($this->entity) ? $this->entity::field($bridgeRelation->foreignKey) : $this->entity->{$bridgeRelation->foreignKey} );
|
})->where( $this->entity::field($bridgeRelation->foreignKey, $relationAlias), is_string($this->entity) ? $this->entity::field($bridgeRelation->foreignKey) : $this->entity->{$bridgeRelation->foreignKey} );
|
||||||
|
|
||||||
|
|
||||||
$this->applyWhere();
|
$this->applyWhere();
|
||||||
|
|
||||||
$this->applyOrderBy();
|
$this->applyOrderBy();
|
||||||
|
@ -284,7 +300,7 @@ class RelationBuilder
|
||||||
return $this->applyFilter($this->repository, $name);
|
return $this->applyFilter($this->repository, $name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function relationAnnotations(string $name, Relation $relation) : array
|
public static function relationAnnotations(string $name, Relation|Attribute\Property\Relation $relation) : array
|
||||||
{
|
{
|
||||||
if ( $relation->isOneToOne() || $relation->isManyToMany() ) {
|
if ( $relation->isOneToOne() || $relation->isManyToMany() ) {
|
||||||
if ( ! $relation->hasBridge() ) {
|
if ( ! $relation->hasBridge() ) {
|
||||||
|
@ -292,8 +308,8 @@ class RelationBuilder
|
||||||
}
|
}
|
||||||
|
|
||||||
$bridgeEntity = Ulmus::resolveEntity($relation->bridge);
|
$bridgeEntity = Ulmus::resolveEntity($relation->bridge);
|
||||||
$bridgeRelation = $bridgeEntity->searchFieldAnnotation($relation->field, new Relation() );
|
$bridgeRelation = $bridgeEntity->searchFieldAnnotation($relation->field, [ Attribute\Property\Relation::class, Relation::class ]);
|
||||||
$relationRelation = $bridgeEntity->searchFieldAnnotation($relation->foreignField, new Relation() );
|
$relationRelation = $bridgeEntity->searchFieldAnnotation($relation->foreignField, [ Attribute\Property\Relation::class, Relation::class ]);
|
||||||
|
|
||||||
if ($relationRelation === null) {
|
if ($relationRelation === null) {
|
||||||
throw new \Exception("@Relation annotation not found for field `{$relation->foreignField}` in entity {$relation->bridge}");
|
throw new \Exception("@Relation annotation not found for field `{$relation->foreignField}` in entity {$relation->bridge}");
|
||||||
|
|
|
@ -4,6 +4,8 @@ namespace Ulmus;
|
||||||
|
|
||||||
use Generator;
|
use Generator;
|
||||||
|
|
||||||
|
use Psr\SimpleCache\CacheInterface;
|
||||||
|
|
||||||
abstract class Ulmus
|
abstract class Ulmus
|
||||||
{
|
{
|
||||||
public static string $repositoryClass = "\\Ulmus\\Repository";
|
public static string $repositoryClass = "\\Ulmus\\Repository";
|
||||||
|
@ -16,6 +18,8 @@ abstract class Ulmus
|
||||||
|
|
||||||
public static Entity\ObjectInstanciator $objectInstanciator;
|
public static Entity\ObjectInstanciator $objectInstanciator;
|
||||||
|
|
||||||
|
public static CacheInterface $cache;
|
||||||
|
|
||||||
public static array $resolved = [];
|
public static array $resolved = [];
|
||||||
|
|
||||||
public static function iterateQueryBuilder(Query\QueryBuilderInterface $queryBuilder, ? ConnectionAdapter $adapter = null) : Generator
|
public static function iterateQueryBuilder(Query\QueryBuilderInterface $queryBuilder, ? ConnectionAdapter $adapter = null) : Generator
|
||||||
|
@ -104,7 +108,7 @@ abstract class Ulmus
|
||||||
|
|
||||||
public static function resolveEntity(string $entityClass) : Common\EntityResolver
|
public static function resolveEntity(string $entityClass) : Common\EntityResolver
|
||||||
{
|
{
|
||||||
return static::$resolved[$entityClass] ?? static::$resolved[$entityClass] = new Common\EntityResolver($entityClass);
|
return static::$resolved[$entityClass] ?? static::$resolved[$entityClass] = new Common\EntityResolver($entityClass, static::$cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function repository(string $entityClass, string $alias = Repository::DEFAULT_ALIAS, ConnectionAdapter $adapter = null) : Repository
|
public static function repository(string $entityClass, string $alias = Repository::DEFAULT_ALIAS, ConnectionAdapter $adapter = null) : Repository
|
||||||
|
|
Loading…
Reference in New Issue