Merge branch 'notes-2.x' of https://git.mcnd.ca/mcndave/ulmus into notes-2.x
This commit is contained in:
commit
feee26cd26
|
@ -1,7 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Ulmus\Annotation\Classes;
|
|
||||||
|
|
||||||
class Collation implements \Notes\Annotation {
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Ulmus\Annotation\Classes;
|
|
||||||
|
|
||||||
class Method implements \Notes\Annotation {
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Ulmus\Annotation\Classes;
|
|
||||||
|
|
||||||
class Table implements \Notes\Annotation {
|
|
||||||
|
|
||||||
public string $name;
|
|
||||||
|
|
||||||
public string $database;
|
|
||||||
|
|
||||||
public string $schema;
|
|
||||||
|
|
||||||
public string $adapter;
|
|
||||||
|
|
||||||
public string $engine;
|
|
||||||
|
|
||||||
public function __construct($name = null, $engine = null)
|
|
||||||
{
|
|
||||||
if ( $name !== null ) {
|
|
||||||
$this->name = $name;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( $engine !== null ) {
|
|
||||||
$this->engine = $engine;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Ulmus\Annotation\Property;
|
|
||||||
|
|
||||||
class Field implements \Notes\Annotation {
|
|
||||||
|
|
||||||
public string $type;
|
|
||||||
|
|
||||||
public string $name;
|
|
||||||
|
|
||||||
public int|string $length;
|
|
||||||
|
|
||||||
public int $precision;
|
|
||||||
|
|
||||||
public array $attributes = [];
|
|
||||||
|
|
||||||
public bool $nullable;
|
|
||||||
|
|
||||||
public /* mixed */ $default;
|
|
||||||
|
|
||||||
public bool $readonly = false;
|
|
||||||
|
|
||||||
public function __construct(? string $type = null, ? int $length = null)
|
|
||||||
{
|
|
||||||
if ( $type !== null ) {
|
|
||||||
$this->type = $type;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( $length !== null ) {
|
|
||||||
$this->length = $length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Ulmus\Annotation\Property\Field;
|
|
||||||
|
|
||||||
class Bigint extends \Ulmus\Annotation\Property\Field
|
|
||||||
{
|
|
||||||
public function __construct(? string $type = "bigint", ? int $length = null)
|
|
||||||
{
|
|
||||||
parent::__construct($type, $length);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Ulmus\Annotation\Property\Field;
|
|
||||||
|
|
||||||
class Blob extends \Ulmus\Annotation\Property\Field
|
|
||||||
{
|
|
||||||
public function __construct(? string $type = "blob", ? int $length = null)
|
|
||||||
{
|
|
||||||
parent::__construct($type, $length);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Ulmus\Annotation\Property\Field;
|
|
||||||
|
|
||||||
class CreatedAt extends \Ulmus\Annotation\Property\Field {
|
|
||||||
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->nullable = false;
|
|
||||||
$this->type = "timestamp";
|
|
||||||
$this->attributes['default'] = "CURRENT_TIMESTAMP";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Ulmus\Annotation\Property\Field;
|
|
||||||
|
|
||||||
class Date extends \Ulmus\Annotation\Property\Field {
|
|
||||||
|
|
||||||
public function __construct(? string $type = "date", ? int $length = null)
|
|
||||||
{
|
|
||||||
parent::__construct($type, $length);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Ulmus\Annotation\Property\Field;
|
|
||||||
|
|
||||||
class Datetime extends \Ulmus\Annotation\Property\Field {
|
|
||||||
|
|
||||||
public function __construct(? string $type = "datetime", ? int $length = null)
|
|
||||||
{
|
|
||||||
parent::__construct($type, $length);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Ulmus\Annotation\Property\Field;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Since we need consistancy between the declaration of our ID and FK fields, it
|
|
||||||
* was decided to extend the Id class instead of Field for this case.
|
|
||||||
*/
|
|
||||||
class ForeignKey extends PrimaryKey {
|
|
||||||
|
|
||||||
public function __construct(? string $type = null, ? int $length = null)
|
|
||||||
{
|
|
||||||
parent::__construct($type, $length);
|
|
||||||
|
|
||||||
unset($this->nullable);
|
|
||||||
$this->attributes['primary_key'] = false;
|
|
||||||
$this->attributes['auto_increment'] = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Ulmus\Annotation\Property\Field;
|
|
||||||
|
|
||||||
class Id extends \Ulmus\Annotation\Property\Field\PrimaryKey {
|
|
||||||
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->attributes['unsigned'] = true;
|
|
||||||
$this->attributes['auto_increment'] = true;
|
|
||||||
|
|
||||||
parent::__construct('bigint');
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Ulmus\Annotation\Property\Field;
|
|
||||||
|
|
||||||
class Longblob extends \Ulmus\Annotation\Property\Field
|
|
||||||
{
|
|
||||||
public function __construct(? string $type = "longblob", ? int $length = null)
|
|
||||||
{
|
|
||||||
parent::__construct($type, $length);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Ulmus\Annotation\Property\Field;
|
|
||||||
|
|
||||||
class Longtext extends \Ulmus\Annotation\Property\Field
|
|
||||||
{
|
|
||||||
public function __construct(? string $type = "longtext", ? int $length = null)
|
|
||||||
{
|
|
||||||
parent::__construct($type, $length);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Ulmus\Annotation\Property\Field;
|
|
||||||
|
|
||||||
class Mediumblob extends \Ulmus\Annotation\Property\Field
|
|
||||||
{
|
|
||||||
public function __construct(? string $type = "mediumblob", ? int $length = null)
|
|
||||||
{
|
|
||||||
parent::__construct($type, $length);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Ulmus\Annotation\Property\Field;
|
|
||||||
|
|
||||||
class Mediumtext extends \Ulmus\Annotation\Property\Field
|
|
||||||
{
|
|
||||||
public function __construct(? string $type = "mediumtext", ? int $length = null)
|
|
||||||
{
|
|
||||||
parent::__construct($type, $length);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Ulmus\Annotation\Property\Field;
|
|
||||||
|
|
||||||
class PrimaryKey extends \Ulmus\Annotation\Property\Field {
|
|
||||||
|
|
||||||
public function __construct(? string $type = null, ? int $length = null)
|
|
||||||
{
|
|
||||||
$this->nullable = false;
|
|
||||||
$this->attributes['primary_key'] = true;
|
|
||||||
|
|
||||||
parent::__construct($type, $length);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Ulmus\Annotation\Property\Field;
|
|
||||||
|
|
||||||
class Text extends \Ulmus\Annotation\Property\Field
|
|
||||||
{
|
|
||||||
public function __construct(? string $type = "text", ? int $length = null)
|
|
||||||
{
|
|
||||||
parent::__construct($type, $length);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Ulmus\Annotation\Property\Field;
|
|
||||||
|
|
||||||
class Time extends \Ulmus\Annotation\Property\Field {
|
|
||||||
|
|
||||||
public function __construct(? string $type = "time", ? int $length = null)
|
|
||||||
{
|
|
||||||
parent::__construct($type, $length);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Ulmus\Annotation\Property\Field;
|
|
||||||
|
|
||||||
class Blob extends \Ulmus\Annotation\Property\Field
|
|
||||||
{
|
|
||||||
public function __construct(? string $type = "tinyblob", ? int $length = null)
|
|
||||||
{
|
|
||||||
parent::__construct($type, $length);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Ulmus\Annotation\Property\Field;
|
|
||||||
|
|
||||||
class Tinyint extends \Ulmus\Annotation\Property\Field
|
|
||||||
{
|
|
||||||
public function __construct(? string $type = "tinyint", ? int $length = null)
|
|
||||||
{
|
|
||||||
parent::__construct($type, $length);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Ulmus\Annotation\Property\Field;
|
|
||||||
|
|
||||||
class UpdatedAt extends \Ulmus\Annotation\Property\Field {
|
|
||||||
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->nullable = true;
|
|
||||||
$this->type = "timestamp";
|
|
||||||
$this->attributes['update'] = "CURRENT_TIMESTAMP";
|
|
||||||
$this->attributes['default'] = null;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Ulmus\Annotation\Property;
|
|
||||||
|
|
||||||
class Filter implements \Notes\Annotation {
|
|
||||||
|
|
||||||
public string $method;
|
|
||||||
|
|
||||||
public function __construct(string $method = null)
|
|
||||||
{
|
|
||||||
if ( $method !== null ) {
|
|
||||||
$this->method = $method;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Ulmus\Annotation\Property;
|
|
||||||
|
|
||||||
class FilterJoin implements \Notes\Annotation {
|
|
||||||
|
|
||||||
public string $method;
|
|
||||||
|
|
||||||
public function __construct(string $method = null)
|
|
||||||
{
|
|
||||||
if ( $method !== null ) {
|
|
||||||
$this->method = $method;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Ulmus\Annotation\Property;
|
|
||||||
|
|
||||||
class GroupBy implements \Notes\Annotation {
|
|
||||||
|
|
||||||
public array $fields = [];
|
|
||||||
|
|
||||||
public function __construct(...$field)
|
|
||||||
{
|
|
||||||
if ( $field ) {
|
|
||||||
$this->fields = $field;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Ulmus\Annotation\Property;
|
|
||||||
|
|
||||||
class Having extends Where {}
|
|
|
@ -1,31 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Ulmus\Annotation\Property;
|
|
||||||
|
|
||||||
class Join implements \Notes\Annotation {
|
|
||||||
|
|
||||||
public string $type;
|
|
||||||
|
|
||||||
public string|Stringable $key;
|
|
||||||
|
|
||||||
public string|Stringable $foreignKey;
|
|
||||||
|
|
||||||
public string $entity;
|
|
||||||
|
|
||||||
public string $alias;
|
|
||||||
|
|
||||||
public function __construct(? string $type = null, /*? string|Stringable*/ $key = null, /*? string|Stringable*/ $foreignKey = null)
|
|
||||||
{
|
|
||||||
if ($type !== null) {
|
|
||||||
$this->type = $type;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($key !== null) {
|
|
||||||
$this->key = $key;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($foreignKey !== null) {
|
|
||||||
$this->foreignKey = $foreignKey;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Ulmus\Annotation\Property;
|
|
||||||
|
|
||||||
class On extends Where {}
|
|
|
@ -1,14 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Ulmus\Annotation\Property;
|
|
||||||
|
|
||||||
use Ulmus\Query;
|
|
||||||
|
|
||||||
class OrWhere extends Where {
|
|
||||||
|
|
||||||
public function __construct(/* ? Stringable */ $field = null, $value = null, ? string $operator = null, ? string $condition = null)
|
|
||||||
{
|
|
||||||
parent::__construct($field, $value, $operator, Query\Where::CONDITION_OR);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Ulmus\Annotation\Property;
|
|
||||||
|
|
||||||
class OrderBy implements \Notes\Annotation {
|
|
||||||
|
|
||||||
public string $field;
|
|
||||||
|
|
||||||
public string $order = "ASC";
|
|
||||||
|
|
||||||
public function __construct(string $field = null, string $order = null)
|
|
||||||
{
|
|
||||||
if ( $field !== null ) {
|
|
||||||
$this->field = $field;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( $order !== null ) {
|
|
||||||
$this->order = $order;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,88 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Ulmus\Annotation\Property;
|
|
||||||
|
|
||||||
class Relation implements \Notes\Annotation {
|
|
||||||
|
|
||||||
public string $type;
|
|
||||||
|
|
||||||
public /*stringable*/ $key;
|
|
||||||
|
|
||||||
public /* callable */ $generateKey;
|
|
||||||
|
|
||||||
public /*stringable*/ $foreignKey;
|
|
||||||
|
|
||||||
public array $foreignKeys;
|
|
||||||
|
|
||||||
public string $bridge;
|
|
||||||
|
|
||||||
public /*stringable*/ $bridgeKey;
|
|
||||||
|
|
||||||
public /*stringable*/ $bridgeForeignKey;
|
|
||||||
|
|
||||||
public string $entity;
|
|
||||||
|
|
||||||
public string $join;
|
|
||||||
|
|
||||||
public string $function = "loadAll";
|
|
||||||
|
|
||||||
public function __construct(string $type = null)
|
|
||||||
{
|
|
||||||
if ( $type !== null ) {
|
|
||||||
$this->type = $type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 isset($this->bridge);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Ulmus\Annotation\Property\Relation;
|
|
||||||
|
|
||||||
class Ignore implements \Notes\Annotation {}
|
|
|
@ -1,19 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Ulmus\Annotation\Property;
|
|
||||||
|
|
||||||
class Virtual extends Field {
|
|
||||||
|
|
||||||
public bool $readonly = true;
|
|
||||||
|
|
||||||
public \Closure $closure;
|
|
||||||
|
|
||||||
public string $method;
|
|
||||||
|
|
||||||
public function __construct(? \Closure $closure = null)
|
|
||||||
{
|
|
||||||
if ( $closure !== null ) {
|
|
||||||
$this->closure = $closure;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Ulmus\Annotation\Property;
|
|
||||||
|
|
||||||
use Ulmus\Query;
|
|
||||||
|
|
||||||
class Where implements \Notes\Annotation {
|
|
||||||
|
|
||||||
public /* stringable */ $field;
|
|
||||||
|
|
||||||
public $value;
|
|
||||||
|
|
||||||
public string $operator;
|
|
||||||
|
|
||||||
public string $condition;
|
|
||||||
|
|
||||||
public function __construct(/* ? Stringable */ $field = null, $value = null, ? string $operator = null, ? string $condition = null)
|
|
||||||
{
|
|
||||||
if ( $field !== null ) {
|
|
||||||
$this->field = $field;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( $value !== null ) {
|
|
||||||
$this->value = $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->operator = $operator !== null ? $operator : Query\Where::OPERATOR_EQUAL;
|
|
||||||
$this->condition = $condition !== null ? $condition : Query\Where::CONDITION_AND;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Ulmus\Annotation\Property;
|
|
||||||
|
|
||||||
class WithJoin implements \Notes\Annotation {
|
|
||||||
|
|
||||||
protected array $joins;
|
|
||||||
|
|
||||||
public function __construct(/*Stringable|array|null*/ $joins = null)
|
|
||||||
{
|
|
||||||
if ( $joins ) {
|
|
||||||
$this->joins = (array)$joins;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -10,6 +10,7 @@ class Table implements AdapterAttributeInterface {
|
||||||
public ? string $schema = null,
|
public ? string $schema = null,
|
||||||
public ? string $adapter = null,
|
public ? string $adapter = null,
|
||||||
public ? string $engine = null,
|
public ? string $engine = null,
|
||||||
|
public string $description = "",
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function adapter() : false|string
|
public function adapter() : false|string
|
||||||
|
|
|
@ -14,5 +14,6 @@ class Field {
|
||||||
public mixed $default = null,
|
public mixed $default = null,
|
||||||
public bool $readonly = false,
|
public bool $readonly = false,
|
||||||
public null|int $decimal = null,
|
public null|int $decimal = null,
|
||||||
|
public string $description = "",
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,5 +14,6 @@ class Bigint extends \Ulmus\Attribute\Property\Field
|
||||||
public bool $nullable = false,
|
public bool $nullable = false,
|
||||||
public mixed $default = null,
|
public mixed $default = null,
|
||||||
public bool $readonly = false,
|
public bool $readonly = false,
|
||||||
|
public string $description = "",
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
|
@ -14,5 +14,6 @@ class Bit extends \Ulmus\Attribute\Property\Field
|
||||||
public bool $nullable = false,
|
public bool $nullable = false,
|
||||||
public mixed $default = null,
|
public mixed $default = null,
|
||||||
public bool $readonly = false,
|
public bool $readonly = false,
|
||||||
|
public string $description = "",
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
|
@ -14,5 +14,6 @@ class Blob extends \Ulmus\Attribute\Property\Field
|
||||||
public bool $nullable = false,
|
public bool $nullable = false,
|
||||||
public mixed $default = null,
|
public mixed $default = null,
|
||||||
public bool $readonly = false,
|
public bool $readonly = false,
|
||||||
|
public string $description = "",
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
|
@ -13,5 +13,6 @@ class CreatedAt extends \Ulmus\Attribute\Property\Field {
|
||||||
public bool $nullable = false,
|
public bool $nullable = false,
|
||||||
public mixed $default = null,
|
public mixed $default = null,
|
||||||
public bool $readonly = false,
|
public bool $readonly = false,
|
||||||
|
public string $description = "",
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,5 +11,6 @@ class Date extends \Ulmus\Attribute\Property\Field {
|
||||||
public bool $nullable = false,
|
public bool $nullable = false,
|
||||||
public mixed $default = null,
|
public mixed $default = null,
|
||||||
public bool $readonly = false,
|
public bool $readonly = false,
|
||||||
|
public string $description = "",
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,5 +11,6 @@ class Datetime extends \Ulmus\Attribute\Property\Field {
|
||||||
public bool $nullable = false,
|
public bool $nullable = false,
|
||||||
public mixed $default = null,
|
public mixed $default = null,
|
||||||
public bool $readonly = false,
|
public bool $readonly = false,
|
||||||
|
public string $description = "",
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,5 +14,6 @@ class Decimal extends \Ulmus\Attribute\Property\Field
|
||||||
public bool $nullable = false,
|
public bool $nullable = false,
|
||||||
public mixed $default = null,
|
public mixed $default = null,
|
||||||
public bool $readonly = false,
|
public bool $readonly = false,
|
||||||
|
public string $description = "",
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
|
@ -14,5 +14,6 @@ class Float extends \Ulmus\Attribute\Property\Field
|
||||||
public bool $nullable = false,
|
public bool $nullable = false,
|
||||||
public mixed $default = null,
|
public mixed $default = null,
|
||||||
public bool $readonly = false,
|
public bool $readonly = false,
|
||||||
|
public string $description = "",
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
|
@ -26,6 +26,7 @@ class ForeignKey extends PrimaryKey {
|
||||||
public null|string $relation = null,
|
public null|string $relation = null,
|
||||||
public ConstrainActionEnum $onDelete = ConstrainActionEnum::NoAction,
|
public ConstrainActionEnum $onDelete = ConstrainActionEnum::NoAction,
|
||||||
public ConstrainActionEnum $onUpdate = ConstrainActionEnum::NoAction,
|
public ConstrainActionEnum $onUpdate = ConstrainActionEnum::NoAction,
|
||||||
|
public string $description = "",
|
||||||
) {
|
) {
|
||||||
#$this->references = Attribute::handleArrayField($this->references, false);
|
#$this->references = Attribute::handleArrayField($this->references, false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,5 +17,6 @@ class Id extends \Ulmus\Attribute\Property\Field {
|
||||||
public bool $nullable = false,
|
public bool $nullable = false,
|
||||||
public mixed $default = null,
|
public mixed $default = null,
|
||||||
public bool $readonly = false,
|
public bool $readonly = false,
|
||||||
|
public string $description = "",
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,5 +14,6 @@ class Longblob extends \Ulmus\Attribute\Property\Field
|
||||||
public bool $nullable = false,
|
public bool $nullable = false,
|
||||||
public mixed $default = null,
|
public mixed $default = null,
|
||||||
public bool $readonly = false,
|
public bool $readonly = false,
|
||||||
|
public string $description = "",
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
|
@ -14,5 +14,6 @@ class Longtext extends \Ulmus\Attribute\Property\Field
|
||||||
public bool $nullable = false,
|
public bool $nullable = false,
|
||||||
public mixed $default = null,
|
public mixed $default = null,
|
||||||
public bool $readonly = false,
|
public bool $readonly = false,
|
||||||
|
public string $description = "",
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
|
@ -14,5 +14,6 @@ class Mediumblob extends \Ulmus\Attribute\Property\Field
|
||||||
public bool $nullable = false,
|
public bool $nullable = false,
|
||||||
public mixed $default = null,
|
public mixed $default = null,
|
||||||
public bool $readonly = false,
|
public bool $readonly = false,
|
||||||
|
public string $description = "",
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
|
@ -14,5 +14,6 @@ class Mediumint extends \Ulmus\Attribute\Property\Field
|
||||||
public bool $nullable = false,
|
public bool $nullable = false,
|
||||||
public mixed $default = null,
|
public mixed $default = null,
|
||||||
public bool $readonly = false,
|
public bool $readonly = false,
|
||||||
|
public string $description = "",
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
|
@ -14,5 +14,6 @@ class Mediumtext extends \Ulmus\Attribute\Property\Field
|
||||||
public bool $nullable = false,
|
public bool $nullable = false,
|
||||||
public mixed $default = null,
|
public mixed $default = null,
|
||||||
public bool $readonly = false,
|
public bool $readonly = false,
|
||||||
|
public string $description = "",
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
|
@ -14,5 +14,6 @@ class Numeric extends \Ulmus\Attribute\Property\Field
|
||||||
public bool $nullable = false,
|
public bool $nullable = false,
|
||||||
public mixed $default = null,
|
public mixed $default = null,
|
||||||
public bool $readonly = false,
|
public bool $readonly = false,
|
||||||
|
public string $description = "",
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
|
@ -16,5 +16,6 @@ class PrimaryKey extends \Ulmus\Attribute\Property\Field
|
||||||
public bool $nullable = false,
|
public bool $nullable = false,
|
||||||
public mixed $default = null,
|
public mixed $default = null,
|
||||||
public bool $readonly = false,
|
public bool $readonly = false,
|
||||||
|
public string $description = "",
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,5 +14,6 @@ class Smallint extends \Ulmus\Attribute\Property\Field
|
||||||
public bool $nullable = false,
|
public bool $nullable = false,
|
||||||
public mixed $default = null,
|
public mixed $default = null,
|
||||||
public bool $readonly = false,
|
public bool $readonly = false,
|
||||||
|
public string $description = "",
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
|
@ -14,5 +14,6 @@ class Text extends \Ulmus\Attribute\Property\Field
|
||||||
public bool $nullable = false,
|
public bool $nullable = false,
|
||||||
public mixed $default = null,
|
public mixed $default = null,
|
||||||
public bool $readonly = false,
|
public bool $readonly = false,
|
||||||
|
public string $description = "",
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
|
@ -5,8 +5,13 @@ namespace Ulmus\Attribute\Property\Field;
|
||||||
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
||||||
class Time extends \Ulmus\Attribute\Property\Field
|
class Time extends \Ulmus\Attribute\Property\Field
|
||||||
{
|
{
|
||||||
public function __construct(? string $type = "time", ? int $length = null)
|
public function __construct(
|
||||||
{
|
public ? string $name = null,
|
||||||
parent::__construct($type, $length);
|
public ? string $type = "time",
|
||||||
}
|
public array $attributes = [],
|
||||||
|
public bool $nullable = false,
|
||||||
|
public mixed $default = null,
|
||||||
|
public bool $readonly = false,
|
||||||
|
public string $description = "",
|
||||||
|
) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,13 @@ namespace Ulmus\Attribute\Property\Field;
|
||||||
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
||||||
class Timestamp extends \Ulmus\Attribute\Property\Field
|
class Timestamp extends \Ulmus\Attribute\Property\Field
|
||||||
{
|
{
|
||||||
public function __construct(? string $type = "timestamp", ? int $length = null)
|
public function __construct(
|
||||||
{
|
public ? string $name = null,
|
||||||
parent::__construct($type, $length);
|
public ? string $type = "timestamp",
|
||||||
}
|
public array $attributes = [],
|
||||||
|
public bool $nullable = false,
|
||||||
|
public mixed $default = null,
|
||||||
|
public bool $readonly = false,
|
||||||
|
public string $description = "",
|
||||||
|
) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,5 +14,6 @@ class Blob extends \Ulmus\Attribute\Property\Field
|
||||||
public bool $nullable = false,
|
public bool $nullable = false,
|
||||||
public mixed $default = null,
|
public mixed $default = null,
|
||||||
public bool $readonly = false,
|
public bool $readonly = false,
|
||||||
|
public string $description = "",
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
|
@ -14,5 +14,6 @@ class Tinyint extends \Ulmus\Attribute\Property\Field
|
||||||
public bool $nullable = false,
|
public bool $nullable = false,
|
||||||
public mixed $default = null,
|
public mixed $default = null,
|
||||||
public bool $readonly = false,
|
public bool $readonly = false,
|
||||||
|
public string $description = "",
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
|
@ -14,5 +14,6 @@ class Tinytext extends \Ulmus\Attribute\Property\Field
|
||||||
public bool $nullable = false,
|
public bool $nullable = false,
|
||||||
public mixed $default = null,
|
public mixed $default = null,
|
||||||
public bool $readonly = false,
|
public bool $readonly = false,
|
||||||
|
public string $description = "",
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
|
@ -15,5 +15,6 @@ class UpdatedAt extends \Ulmus\Attribute\Property\Field
|
||||||
public bool $nullable = true,
|
public bool $nullable = true,
|
||||||
public mixed $default = null,
|
public mixed $default = null,
|
||||||
public bool $readonly = false,
|
public bool $readonly = false,
|
||||||
|
public string $description = "",
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ namespace Ulmus\Attribute\Property;
|
||||||
use Ulmus\Attribute\Attribute;
|
use Ulmus\Attribute\Attribute;
|
||||||
|
|
||||||
#[\Attribute]
|
#[\Attribute]
|
||||||
class Join {
|
class Join implements ResettablePropertyInterface {
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public string $type,
|
public string $type,
|
||||||
public null|string|\Stringable|array $key = null,
|
public null|string|\Stringable|array $key = null,
|
||||||
|
|
|
@ -5,7 +5,7 @@ namespace Ulmus\Attribute\Property;
|
||||||
use Ulmus\Attribute\Attribute;
|
use Ulmus\Attribute\Attribute;
|
||||||
|
|
||||||
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
||||||
class Relation {
|
class Relation implements ResettablePropertyInterface {
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public Relation\RelationTypeEnum|string $type,
|
public Relation\RelationTypeEnum|string $type,
|
||||||
public \Stringable|string|array $key = "",
|
public \Stringable|string|array $key = "",
|
||||||
|
@ -35,7 +35,7 @@ class Relation {
|
||||||
try {
|
try {
|
||||||
$e = $this->entity;
|
$e = $this->entity;
|
||||||
} catch (\Throwable $ex) {
|
} catch (\Throwable $ex) {
|
||||||
throw new \Exception("Your @Relation annotation seems to be missing an `entity` entry.");
|
throw new \Exception("Your @Relation attribute seems to be missing an `entity` entry.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return new $e();
|
return new $e();
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Attribute\Property;
|
||||||
|
|
||||||
|
interface ResettablePropertyInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
|
@ -3,7 +3,7 @@
|
||||||
namespace Ulmus\Attribute\Property;
|
namespace Ulmus\Attribute\Property;
|
||||||
|
|
||||||
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
||||||
class Virtual extends Field {
|
class Virtual extends Field implements ResettablePropertyInterface {
|
||||||
|
|
||||||
public bool $readonly = true;
|
public bool $readonly = true;
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,10 @@
|
||||||
|
|
||||||
namespace Ulmus\Common;
|
namespace Ulmus\Common;
|
||||||
|
|
||||||
use Ulmus\Annotation\Annotation;
|
use Ulmus\Attribute\Property\{ Field };
|
||||||
use Ulmus\Attribute;
|
|
||||||
use Ulmus\Migration\FieldDefinition;
|
use Ulmus\Migration\FieldDefinition;
|
||||||
use Ulmus\Ulmus,
|
use Ulmus\Ulmus,
|
||||||
Ulmus\Adapter\AdapterInterface,
|
Ulmus\Adapter\AdapterInterface,
|
||||||
Ulmus\Annotation\Property\Field,
|
|
||||||
Ulmus\Query\WhereRawParameter;
|
Ulmus\Query\WhereRawParameter;
|
||||||
|
|
||||||
class EntityField implements WhereRawParameter
|
class EntityField implements WhereRawParameter
|
||||||
|
@ -30,7 +28,7 @@ class EntityField implements WhereRawParameter
|
||||||
|
|
||||||
public function name($useAlias = true) : string
|
public function name($useAlias = true) : string
|
||||||
{
|
{
|
||||||
$name = $this->entityResolver->searchFieldAnnotation($this->name, [ Attribute\Property\Field::class, Field::class ] )->name ?? $this->name;
|
$name = $this->entityResolver->searchFieldAnnotation($this->name, [ 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);
|
||||||
|
|
||||||
|
|
|
@ -6,13 +6,13 @@ use Notes\Common\ReflectedClass;
|
||||||
use Notes\Common\ReflectedProperty;
|
use Notes\Common\ReflectedProperty;
|
||||||
use Psr\SimpleCache\CacheInterface;
|
use Psr\SimpleCache\CacheInterface;
|
||||||
use Ulmus\Ulmus,
|
use Ulmus\Ulmus,
|
||||||
Ulmus\Annotation\Classes\Table,
|
Ulmus\Attribute\Obj\Table,
|
||||||
Ulmus\Annotation\Property\Field,
|
Ulmus\Attribute\Obj\AdapterAttributeInterface,
|
||||||
Ulmus\Annotation\Property\Virtual,
|
Ulmus\Attribute\Property\Field,
|
||||||
Ulmus\Annotation\Property\Relation,
|
Ulmus\Attribute\Property\Relation,
|
||||||
Ulmus\Attribute;
|
Ulmus\Attribute\Property\Virtual;
|
||||||
|
|
||||||
use Notes\Annotation;
|
use Notes\Common\ReflectedAttribute;
|
||||||
|
|
||||||
use Notes\ObjectReflection;
|
use Notes\ObjectReflection;
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ class EntityResolver {
|
||||||
$this->reflectedClass = ObjectReflection::fromClass($entityClass, $cache)->reflectClass();
|
$this->reflectedClass = ObjectReflection::fromClass($entityClass, $cache)->reflectClass();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function field($name, $fieldKey = self::KEY_ENTITY_NAME, $throwException = true) : ? ReflectedProperty
|
public function field($name, $fieldKey = self::KEY_ENTITY_NAME, $throwException = true) : null|ReflectedProperty
|
||||||
{
|
{
|
||||||
try{
|
try{
|
||||||
return $this->fieldList($fieldKey)[$name] ?? null;
|
return $this->fieldList($fieldKey)[$name] ?? null;
|
||||||
|
@ -51,7 +51,7 @@ class EntityResolver {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function searchField($name) : null|array
|
public function searchField($name) : null|ReflectedProperty
|
||||||
{
|
{
|
||||||
return $this->field($name, self::KEY_ENTITY_NAME, false) ?: $this->field($name, self::KEY_COLUMN_NAME, false);
|
return $this->field($name, self::KEY_ENTITY_NAME, false) ?: $this->field($name, self::KEY_COLUMN_NAME, false);
|
||||||
}
|
}
|
||||||
|
@ -62,8 +62,8 @@ class EntityResolver {
|
||||||
|
|
||||||
foreach($this->reflectedClass->getProperties(true) as $item) {
|
foreach($this->reflectedClass->getProperties(true) as $item) {
|
||||||
foreach($item->getAttributes() as $tag) {
|
foreach($item->getAttributes() as $tag) {
|
||||||
if ( $tag->object instanceof Attribute\Property\Field ) {
|
if ( $tag->object instanceof Field ) {
|
||||||
if ( $skipVirtual && $tag->object instanceof Attribute\Property\Virtual ) {
|
if ( $skipVirtual && $tag->object instanceof Virtual ) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,7 +102,7 @@ class EntityResolver {
|
||||||
try{
|
try{
|
||||||
if ( $property ) {
|
if ( $property ) {
|
||||||
foreach($property->getAttributes() as $tag) {
|
foreach($property->getAttributes() as $tag) {
|
||||||
if ( $tag->object instanceof Relation or $tag->object instanceof Attribute\Property\Relation ) {
|
if ( $tag->object instanceof Relation ) {
|
||||||
return $property;
|
return $property;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,7 +120,7 @@ class EntityResolver {
|
||||||
return $this->searchFieldAnnotationList($field, $annotationType, $caseSensitive)[0] ?? null;
|
return $this->searchFieldAnnotationList($field, $annotationType, $caseSensitive)[0] ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function searchFieldAnnotationList(string $field, array|object|string $annotationType, bool $caseSensitive = true) : array
|
public function searchFieldAnnotationList(string $field, array|object|string $attributeType, bool $caseSensitive = true) : false|array
|
||||||
{
|
{
|
||||||
$list = [];
|
$list = [];
|
||||||
|
|
||||||
|
@ -128,19 +128,11 @@ class EntityResolver {
|
||||||
|
|
||||||
$search = $caseSensitive ? $properties : array_change_key_case($properties, \CASE_LOWER);
|
$search = $caseSensitive ? $properties : array_change_key_case($properties, \CASE_LOWER);
|
||||||
|
|
||||||
$annotations = is_array($annotationType) ? $annotationType : [ $annotationType ];
|
|
||||||
|
|
||||||
if ( null !== ( $search[$field] ?? null ) ) {
|
if ( null !== ( $search[$field] ?? null ) ) {
|
||||||
foreach($search[$field]->getAttributes() as $tag) {
|
return array_map(fn(ReflectedAttribute $e) => $e->object, $search[$field]->getAttributes((array) $attributeType));
|
||||||
foreach($annotations as $annotation) {
|
|
||||||
if ( $tag->object instanceof $annotation ) {
|
|
||||||
$list[] = $tag->object;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $list;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function tableName($required = false) : string
|
public function tableName($required = false) : string
|
||||||
|
@ -156,7 +148,7 @@ class EntityResolver {
|
||||||
return $table->name ?? "";
|
return $table->name ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
public function tableAnnotation($required = false) : null|Table|Attribute\Obj\Table
|
public function tableAnnotation($required = false) : null|Table
|
||||||
{
|
{
|
||||||
if ( null === $table = $this->getTableAttribute() ) {
|
if ( null === $table = $this->getTableAttribute() ) {
|
||||||
if ($required) {
|
if ($required) {
|
||||||
|
@ -167,7 +159,7 @@ class EntityResolver {
|
||||||
return $table;
|
return $table;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function databaseName() : ? string
|
public function databaseName() : null|string
|
||||||
{
|
{
|
||||||
return $this->tableAnnotation(false)->database ?? $this->databaseAdapter()->adapter()->databaseName() ?? null;
|
return $this->tableAnnotation(false)->database ?? $this->databaseAdapter()->adapter()->databaseName() ?? null;
|
||||||
}
|
}
|
||||||
|
@ -196,7 +188,7 @@ class EntityResolver {
|
||||||
return $this->sqlAdapter();
|
return $this->sqlAdapter();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function schemaName(bool $required = false) : ? string
|
public function schemaName(bool $required = false) : null|string
|
||||||
{
|
{
|
||||||
if ( null === $table = $this->getTableAttribute() ) {
|
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");
|
||||||
|
@ -209,10 +201,10 @@ class EntityResolver {
|
||||||
return $table->schema ?? null;
|
return $table->schema ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPrimaryKeyField() : ? array
|
public function getPrimaryKeyField() : null|array
|
||||||
{
|
{
|
||||||
foreach($this->fieldList() as $key => $value) {
|
foreach($this->fieldList() as $key => $value) {
|
||||||
$field = $this->searchFieldAnnotation($key, [ Attribute\Property\Field::class, Field::class ]);
|
$field = $this->searchFieldAnnotation($key, [ Field::class ]);
|
||||||
if ( null !== $field ) {
|
if ( null !== $field ) {
|
||||||
if ( false !== ( $field->attributes['primary_key'] ?? false ) ) {
|
if ( false !== ( $field->attributes['primary_key'] ?? false ) ) {
|
||||||
return [ $key => $field ];
|
return [ $key => $field ];
|
||||||
|
@ -235,57 +227,17 @@ class EntityResolver {
|
||||||
|
|
||||||
protected function getAdapterInterfaceAttribute() : null|object
|
protected function getAdapterInterfaceAttribute() : null|object
|
||||||
{
|
{
|
||||||
return $this->getAttributeImplementing(Attribute\Obj\AdapterAttributeInterface::class);
|
return $this->getAttributeImplementing(AdapterAttributeInterface::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getTableAttribute()
|
protected function getTableAttribute()
|
||||||
{
|
{
|
||||||
return $this->getAttributeImplementing(Attribute\Obj\Table::class) ?: $this->getAnnotationFromClassname( Table::class, false );
|
return $this->getAttributeImplementing(Table::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function getAttributeImplementing(string $interface) : null|object
|
||||||
* Transform an annotation into it's object's counterpart
|
|
||||||
*/
|
|
||||||
public function getAnnotationFromClassname(string $className, bool $throwError = true) : ? object
|
|
||||||
{
|
{
|
||||||
exit(__FILE__);
|
foreach ($this->reflectedClass->getAttributes(true) as $item) {
|
||||||
if ( $name = $this->uses[$className] ?? false ) {
|
|
||||||
foreach(array_reverse($this->class['tags']) as $item) {
|
|
||||||
if ( $item['tag'] === $name ) {
|
|
||||||
return $this->instanciateAnnotationObject($item);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach($this->properties as $item) {
|
|
||||||
foreach(array_reverse($item['tags']) as $item) {
|
|
||||||
if ( $item['tag'] === $name ) {
|
|
||||||
return $this->instanciateAnnotationObject($item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach($this->methods as $item) {
|
|
||||||
foreach(array_reverse($item['tags']) as $item) {
|
|
||||||
if ( $item['tag'] === $name ) {
|
|
||||||
return $this->instanciateAnnotationObject($item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($throwError) {
|
|
||||||
throw new \TypeError("Annotation `$className` could not be found within your object `{$this->entityClass}`");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
elseif ($throwError) {
|
|
||||||
throw new \InvalidArgumentException("Class `$className` was not found within {$this->entityClass} uses statement (or it's children / traits)");
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getAttributeImplementing(string $interface) : ? object
|
|
||||||
{
|
|
||||||
foreach (array_reverse($this->reflectedClass->getAttributes(true)) as $item) {
|
|
||||||
if ($item->object instanceof $interface) {
|
if ($item->object instanceof $interface) {
|
||||||
return $item->object;
|
return $item->object;
|
||||||
}
|
}
|
||||||
|
@ -293,14 +245,4 @@ class EntityResolver {
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function instanciateAnnotationObject(array|\ReflectionAttribute $tagDefinition) : object
|
|
||||||
{
|
|
||||||
|
|
||||||
if ($tagDefinition instanceof \ReflectionAttribute) {
|
|
||||||
$obj = $tagDefinition->newInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $obj;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,153 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Entity;
|
||||||
|
|
||||||
|
use Generator;
|
||||||
|
use Ulmus\Attribute\Property\Field;
|
||||||
|
use Ulmus\Attribute\Property\Relation;
|
||||||
|
use Ulmus\Common\EntityField;
|
||||||
|
use Ulmus\Common\EntityResolver;
|
||||||
|
use Ulmus\Ulmus;
|
||||||
|
|
||||||
|
class DatasetHandler
|
||||||
|
{
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
protected EntityResolver $entityResolver,
|
||||||
|
protected bool $entityStrictFieldsDeclaration = false,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function pull(object $entity) : Generator
|
||||||
|
{
|
||||||
|
foreach($this->entityResolver->fieldList(EntityResolver::KEY_ENTITY_NAME, true) as $key => $field) {
|
||||||
|
$attribute = $this->entityResolver->searchFieldAnnotation($key,[ Field::class ]);
|
||||||
|
|
||||||
|
if ( $entity->__isset($key) ) {
|
||||||
|
yield $attribute->name ?? $key => $entity->$key;
|
||||||
|
}
|
||||||
|
elseif ( $field->allowsNull() ) {
|
||||||
|
yield $attribute->name ?? $key => null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function push(iterable $dataset) : Generator|array
|
||||||
|
{
|
||||||
|
$unmatched = [];
|
||||||
|
|
||||||
|
foreach($dataset as $key => $value) {
|
||||||
|
$field = $this->entityResolver->field(strtolower($key), EntityResolver::KEY_COLUMN_NAME, false) ?? $this->entityResolver->field(strtolower($key), EntityResolver::KEY_LC_ENTITY_NAME, false);
|
||||||
|
|
||||||
|
if ( $field === null ) {
|
||||||
|
if ($this->entityStrictFieldsDeclaration ) {
|
||||||
|
throw new \Exception("Field `$key` can not be found within your entity ".static::class);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$unmatched[$key] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$type = $field->getTypes()[0];
|
||||||
|
|
||||||
|
if ( is_null($value) ) {
|
||||||
|
yield $field->name => null;
|
||||||
|
}
|
||||||
|
elseif ( $field->expectType('array') ) {
|
||||||
|
if ( is_string($value)) {
|
||||||
|
if (substr($value, 0, 1) === "a") {
|
||||||
|
|
||||||
|
yield $field->name => unserialize($value);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$data = json_decode($value, true);
|
||||||
|
|
||||||
|
if (json_last_error() !== \JSON_ERROR_NONE) {
|
||||||
|
throw new \Exception(sprintf("JSON error while decoding in EntityTrait : '%s' given %s", json_last_error_msg(), $value));
|
||||||
|
}
|
||||||
|
|
||||||
|
yield $field->name => $data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif ( is_array($value) ) {
|
||||||
|
yield $field->name => $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif ( EntityField::isScalarType($type->type) ) {
|
||||||
|
|
||||||
|
if ( $type->type === 'string' ) {
|
||||||
|
$attribute = $this->entityResolver->searchFieldAnnotation($field->name, [ Field::class ] );
|
||||||
|
|
||||||
|
if ( $attribute->length ?? null ) {
|
||||||
|
$value = mb_substr($value, 0, $attribute->length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif ( $type->type === 'bool' ) {
|
||||||
|
$value = (bool) $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
yield $field->name => $value;
|
||||||
|
}
|
||||||
|
elseif ( $value instanceof \UnitEnum ) {
|
||||||
|
yield $field->name => $value;
|
||||||
|
}
|
||||||
|
elseif (enum_exists($type->type)) {
|
||||||
|
yield $field->name => $type->type::from($value);
|
||||||
|
}
|
||||||
|
elseif ( ! $type->builtIn ) {
|
||||||
|
try {
|
||||||
|
yield $field->name => Ulmus::instanciateObject($type->type, [ $value ]);
|
||||||
|
}
|
||||||
|
catch(\Error $e) {
|
||||||
|
throw new \Error(sprintf("%s for class '%s' on field '%s'", $e->getMessage(), get_class($this), $field->name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $unmatched;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function pullRelation(object $entity) : Generator
|
||||||
|
{
|
||||||
|
foreach($this->entityResolver->reflectedClass->getProperties(true) as $name => $field){
|
||||||
|
$relation = $this->entityResolver->searchFieldAnnotation($name, [ Relation::class ] );
|
||||||
|
|
||||||
|
if ($relation) {
|
||||||
|
$ignore = $this->entityResolver->searchFieldAnnotation($name, [ Relation\Ignore::class ] );
|
||||||
|
|
||||||
|
if ($ignore && $ignore->ignoreExport) {
|
||||||
|
if ( $relation->isOneToOne() ) {
|
||||||
|
# @TODO TO INCLUDED INTO getTypes() RETURNED CLASS WHEN DONE !
|
||||||
|
yield $name => ( new \ReflectionClass($field->getTypes()[0]) )->newInstanceWithoutConstructor();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
# empty collection
|
||||||
|
yield $name => [];
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
# @TODO Must fix recursive bug.. this last check is way too basic to work
|
||||||
|
if ( $entity->__isset($name) && ($relation->entity ?? $relation->bridge) !== static::class ) {
|
||||||
|
if ( null !== $value = $entity->__isset($name) ?? null ) {
|
||||||
|
if ( is_iterable($value) ) {
|
||||||
|
$list = [];
|
||||||
|
|
||||||
|
foreach($value as $entityObj) {
|
||||||
|
$list[] = $entityObj->entityGetDataset(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
yield $name => $list;
|
||||||
|
}
|
||||||
|
elseif ( is_object($value) ) {
|
||||||
|
yield $name => $value->entityGetDataset(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,7 +4,15 @@ namespace Ulmus;
|
||||||
|
|
||||||
use Notes\Attribute\Ignore;
|
use Notes\Attribute\Ignore;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
use Ulmus\{Common\EntityResolver, Common\EntityField, Entity\EntityInterface, QueryBuilder\QueryBuilderInterface};
|
use Ulmus\{Attribute\Property\Join,
|
||||||
|
Attribute\Property\Relation,
|
||||||
|
Attribute\Property\ResettablePropertyInterface,
|
||||||
|
Attribute\Property\Virtual,
|
||||||
|
Common\EntityResolver,
|
||||||
|
Common\EntityField,
|
||||||
|
Entity\DatasetHandler,
|
||||||
|
Entity\EntityInterface,
|
||||||
|
QueryBuilder\QueryBuilderInterface};
|
||||||
use Ulmus\SearchRequest\{Attribute\SearchParameter,
|
use Ulmus\SearchRequest\{Attribute\SearchParameter,
|
||||||
SearchMethodEnum,
|
SearchMethodEnum,
|
||||||
SearchRequestInterface,
|
SearchRequestInterface,
|
||||||
|
@ -14,6 +22,9 @@ use Ulmus\SearchRequest\{Attribute\SearchParameter,
|
||||||
trait EntityTrait {
|
trait EntityTrait {
|
||||||
use EventTrait;
|
use EventTrait;
|
||||||
|
|
||||||
|
#[Ignore]
|
||||||
|
public array $entityLoadedDataset = [];
|
||||||
|
|
||||||
#[Ignore]
|
#[Ignore]
|
||||||
protected bool $entityStrictFieldsDeclaration = false;
|
protected bool $entityStrictFieldsDeclaration = false;
|
||||||
|
|
||||||
|
@ -21,14 +32,23 @@ trait EntityTrait {
|
||||||
protected array $entityDatasetUnmatchedFields = [];
|
protected array $entityDatasetUnmatchedFields = [];
|
||||||
|
|
||||||
#[Ignore]
|
#[Ignore]
|
||||||
public array $entityLoadedDataset = [];
|
protected DatasetHandler $datasetHandler;
|
||||||
|
|
||||||
#[Ignore]
|
#[Ignore]
|
||||||
public function __construct(array $dataset = null) {
|
public function __construct(iterable|null $dataset = null)
|
||||||
|
{
|
||||||
|
$this->initializeEntity($dataset);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Ignore]
|
||||||
|
public function initializeEntity(iterable|null $dataset = null) : void
|
||||||
|
{
|
||||||
if ($dataset) {
|
if ($dataset) {
|
||||||
$this->entityFillFromDataset($dataset);
|
$this->fromArray($dataset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->datasetHandler = new DatasetHandler(static::resolveEntity(), $this->entityStrictFieldsDeclaration);
|
||||||
|
|
||||||
$this->resetVirtualProperties();
|
$this->resetVirtualProperties();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,98 +57,55 @@ trait EntityTrait {
|
||||||
{
|
{
|
||||||
$loaded = $this->isLoaded();
|
$loaded = $this->isLoaded();
|
||||||
|
|
||||||
$entityResolver = $this->resolveEntity();
|
$handler = $this->datasetHandler->push($dataset);
|
||||||
|
|
||||||
foreach($dataset as $key => $value) {
|
foreach($handler as $field => $value) {
|
||||||
$field = $entityResolver->field(strtolower($key), EntityResolver::KEY_COLUMN_NAME, false) ?? $entityResolver->field(strtolower($key), EntityResolver::KEY_LC_ENTITY_NAME, false);
|
$this->$field = $value;
|
||||||
|
}
|
||||||
|
|
||||||
# Temp. fix, incoming patch soon
|
$this->entityDatasetUnmatchedFields = $handler->getReturn();
|
||||||
$type = $field->getTypes()[0];
|
|
||||||
|
|
||||||
if ( $field === null ) {
|
# Keeping original data to diff on UPDATE query
|
||||||
if ($this->entityStrictFieldsDeclaration ) {
|
if ( ! $loaded ) {
|
||||||
throw new \Exception("Field `$key` can not be found within your entity ".static::class);
|
$this->entityLoadedDataset = array_change_key_case(is_array($dataset) ? $dataset : iterator_to_array($dataset), \CASE_LOWER);
|
||||||
}
|
}
|
||||||
else {
|
elseif ($overwriteDataset) {
|
||||||
$this->entityDatasetUnmatchedFields[$key] = $value;
|
$this->entityLoadedDataset = array_change_key_case(is_array($dataset) ? $dataset : iterator_to_array($dataset), \CASE_LOWER) + $this->entityLoadedDataset;
|
||||||
}
|
|
||||||
}
|
|
||||||
elseif ( is_null($value) ) {
|
|
||||||
$this->{$field->name} = null;
|
|
||||||
}
|
|
||||||
elseif ( $field->expectType('array') ) {
|
|
||||||
if ( is_string($value)) {
|
|
||||||
if (substr($value, 0, 1) === "a") {
|
|
||||||
$this->{$field->name} = unserialize($value);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$data = json_decode($value, true);
|
|
||||||
|
|
||||||
if (json_last_error() !== \JSON_ERROR_NONE) {
|
|
||||||
throw new \Exception(sprintf("JSON error while decoding in EntityTrait : '%s' given %s", json_last_error_msg(), $value));
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->{$field->name} = $data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
elseif ( is_array($value) ) {
|
|
||||||
$this->{$field->name} = $value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
elseif ( EntityField::isScalarType($type->type) ) {
|
|
||||||
|
|
||||||
if ( $type->type === 'string' ) {
|
|
||||||
$annotation = $entityResolver->searchFieldAnnotation($field->name, [ Attribute\Property\Field::class ] );
|
|
||||||
|
|
||||||
if ( $annotation->length ?? null ) {
|
|
||||||
$value = mb_substr($value, 0, $annotation->length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
elseif ( $type->type === 'bool' ) {
|
|
||||||
$value = (bool) $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->{$field->name} = $value;
|
|
||||||
}
|
|
||||||
elseif ( $value instanceof \UnitEnum ) {
|
|
||||||
$this->{$field->name} = $value;
|
|
||||||
}
|
|
||||||
elseif (enum_exists($type->type)) {
|
|
||||||
$this->{$field->name} = $type->type::from($value);
|
|
||||||
}
|
|
||||||
elseif ( ! $type->builtIn ) {
|
|
||||||
try {
|
|
||||||
$this->{$field->name} = Ulmus::instanciateObject($type->type, [ $value ]);
|
|
||||||
}
|
|
||||||
catch(\Error $e) {
|
|
||||||
$f = $type->type;
|
|
||||||
throw new \Error(sprintf("%s for class '%s' on field '%s'", $e->getMessage(), get_class($this), $field->name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Keeping original data to diff on UPDATE query
|
|
||||||
if ( ! $loaded /* || $isLoadedDataset */ ) {
|
|
||||||
#if ( $field !== null ) {
|
|
||||||
# $annotation = $entityResolver->searchFieldAnnotation($field['name'], new Field() );
|
|
||||||
# $this->entityLoadedDataset[$annotation ? $annotation->name : $field['name']] = $dataset; # <--------- THIS TO FIX !!!!!!
|
|
||||||
#}
|
|
||||||
$this->entityLoadedDataset = array_change_key_case($dataset, \CASE_LOWER);
|
|
||||||
}
|
|
||||||
elseif ($overwriteDataset) {
|
|
||||||
$this->entityLoadedDataset = iterator_to_array(array_change_key_case($dataset, \CASE_LOWER)) + $this->entityLoadedDataset;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[Ignore]
|
||||||
|
public function entityGetDataset(bool $includeRelations = false, bool $returnSource = false, bool $rewriteValue = true) : array
|
||||||
|
{
|
||||||
|
if ( $returnSource ) {
|
||||||
|
return $this->entityLoadedDataset;
|
||||||
|
}
|
||||||
|
|
||||||
|
$dataset = [];
|
||||||
|
|
||||||
|
foreach($this->datasetHandler->pull($this) as $field => $value) {
|
||||||
|
$dataset[$field] = $rewriteValue ? static::repository()->adapter->adapter()->writableValue($value) : $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($includeRelations) {
|
||||||
|
foreach($this->datasetHandler->pullRelation($this) as $field => $object) {
|
||||||
|
$dataset[$field] = $object;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $dataset;
|
||||||
|
}
|
||||||
|
|
||||||
#[Ignore]
|
#[Ignore]
|
||||||
public function resetVirtualProperties() : self
|
public function resetVirtualProperties() : self
|
||||||
{
|
{
|
||||||
foreach($this->resolveEntity()->reflectedClass->getProperties(true) as $prop => $property) {
|
foreach($this->resolveEntity()->reflectedClass->getProperties(true) as $field => $property) {
|
||||||
foreach($property->attributes as $tag) {
|
foreach($property->attributes as $tag) {
|
||||||
if ( in_array(strtolower($tag->tag), [ 'relation', 'join', 'virtual' ] ) ) {
|
|
||||||
unset($this->$prop);
|
if ( $tag->object instanceof ResettablePropertyInterface ) {
|
||||||
|
unset($this->$field);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -146,74 +123,6 @@ trait EntityTrait {
|
||||||
return $this->entityFillFromDataset($dataset);
|
return $this->entityFillFromDataset($dataset);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function entityGetDataset(bool $includeRelations = false, bool $returnSource = false, bool $rewriteValue = true) : array
|
|
||||||
{
|
|
||||||
if ( $returnSource ) {
|
|
||||||
return $this->entityLoadedDataset;
|
|
||||||
}
|
|
||||||
|
|
||||||
$dataset = [];
|
|
||||||
|
|
||||||
$entityResolver = $this->resolveEntity();
|
|
||||||
|
|
||||||
foreach($entityResolver->fieldList(Common\EntityResolver::KEY_ENTITY_NAME, true) as $key => $field) {
|
|
||||||
$annotation = $entityResolver->searchFieldAnnotation($key, [ Attribute\Property\Field::class ]);
|
|
||||||
|
|
||||||
if ( isset($this->$key) ) {
|
|
||||||
$dataset[$annotation->name ?? $key] = $rewriteValue ?
|
|
||||||
static::repository()->adapter->adapter()->writableValue($this->$key)
|
|
||||||
:
|
|
||||||
$this->$key;
|
|
||||||
}
|
|
||||||
elseif ( $field->allowsNull() ) {
|
|
||||||
$dataset[$annotation->name ?? $key] = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($includeRelations) {
|
|
||||||
foreach($entityResolver->reflectedClass->getProperties(true) as $name => $field){
|
|
||||||
$relation = $entityResolver->searchFieldAnnotation($name, [ Attribute\Property\Relation::class ] );
|
|
||||||
|
|
||||||
if ($relation) {
|
|
||||||
$ignore = $entityResolver->searchFieldAnnotation($name, [ Attribute\Property\Relation\Ignore::class ] );
|
|
||||||
|
|
||||||
if ($ignore && $ignore->ignoreExport) {
|
|
||||||
if ( $relation->isOneToOne() ) {
|
|
||||||
# empty object
|
|
||||||
$dataset[$name] = ( new \ReflectionClass($field->getTypes()[0]) )->newInstanceWithoutConstructor();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
# empty collection
|
|
||||||
$dataset[$name] = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
# @TODO Must fix recursive bug.. this last check is way too basic to work
|
|
||||||
if ( isset($this->$name) && ($relation->entity ?? $relation->bridge) !== static::class ) {
|
|
||||||
if ( null !== $value = $this->$name ?? null ) {
|
|
||||||
if ( is_iterable($value) ) {
|
|
||||||
$list = [];
|
|
||||||
|
|
||||||
foreach($value as $entity) {
|
|
||||||
$list[] = $entity->entityGetDataset(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
$dataset[$name] = $list;
|
|
||||||
}
|
|
||||||
elseif ( is_object($value) ) {
|
|
||||||
$dataset[$name] = $value->entityGetDataset(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $dataset;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[Ignore]
|
#[Ignore]
|
||||||
public function toArray($includeRelations = false, array $filterFields = null, bool $rewriteValue = true) : array
|
public function toArray($includeRelations = false, array $filterFields = null, bool $rewriteValue = true) : array
|
||||||
{
|
{
|
||||||
|
|
|
@ -3,8 +3,7 @@
|
||||||
namespace Ulmus\Query;
|
namespace Ulmus\Query;
|
||||||
|
|
||||||
use Ulmus\Adapter\AdapterInterface;
|
use Ulmus\Adapter\AdapterInterface;
|
||||||
use Ulmus\Annotation,
|
use Ulmus\Common\EntityField;
|
||||||
Ulmus\Common\EntityField;
|
|
||||||
|
|
||||||
class Alter extends Fragment {
|
class Alter extends Fragment {
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,7 @@
|
||||||
namespace Ulmus\Query;
|
namespace Ulmus\Query;
|
||||||
|
|
||||||
use Ulmus\Adapter\AdapterInterface;
|
use Ulmus\Adapter\AdapterInterface;
|
||||||
use Ulmus\Annotation,
|
use Ulmus\Common\EntityField;
|
||||||
Ulmus\Common\EntityField;
|
|
||||||
|
|
||||||
class Create extends Fragment {
|
class Create extends Fragment {
|
||||||
|
|
||||||
|
|
|
@ -2,16 +2,9 @@
|
||||||
|
|
||||||
namespace Ulmus;
|
namespace Ulmus;
|
||||||
|
|
||||||
use Ulmus\Annotation\Property\{Field,
|
use Ulmus\Attribute\Property\{
|
||||||
OrderBy,
|
Field, OrderBy, Where, Having, Relation, Filter, Join, FilterJoin, WithJoin
|
||||||
Where,
|
};
|
||||||
Having,
|
|
||||||
Relation,
|
|
||||||
Filter,
|
|
||||||
Join,
|
|
||||||
FilterJoin,
|
|
||||||
WithJoin,
|
|
||||||
Relation\Ignore as RelationIgnore};
|
|
||||||
use Ulmus\Common\EntityResolver;
|
use Ulmus\Common\EntityResolver;
|
||||||
use Ulmus\Repository\WithOptionEnum;
|
use Ulmus\Repository\WithOptionEnum;
|
||||||
|
|
||||||
|
@ -88,7 +81,16 @@ class Repository
|
||||||
$this->select( "DISTINCT COUNT(*) OVER ()" );
|
$this->select( "DISTINCT COUNT(*) OVER ()" );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$this->select(Common\Sql::function("COUNT", '*'));
|
$pk = Ulmus::resolveEntity($this->entityClass)->getPrimaryKeyField();
|
||||||
|
|
||||||
|
if (count($pk) === 1) {
|
||||||
|
$field = key($pk);
|
||||||
|
|
||||||
|
$this->select(Common\Sql::function('COUNT', Common\Sql::raw("DISTINCT " . $this->entityClass::field($field))));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$this->select(Common\Sql::function('COUNT', Common\Sql::raw('*')));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->selectSqlQuery();
|
$this->selectSqlQuery();
|
||||||
|
@ -362,7 +364,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, [ Attribute\Property\Field::class, Field::class ], false)->readonly ?? false ) ) {
|
if ( false === ( $this->entityResolver->searchFieldAnnotation($field, [ Field::class, Field::class ], false)->readonly ?? false ) ) {
|
||||||
$intersect[$field] = $field;
|
$intersect[$field] = $field;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -395,7 +397,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, [ Attribute\Property\Relation\Ignore::class, RelationIgnore::class ])) {
|
if (null === $entity::resolveEntity()->searchFieldAnnotation($field->name, [ Relation\Ignore::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)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -408,7 +410,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, [ Attribute\Property\Relation\Ignore::class, RelationIgnore::class ])) {
|
if (null === $entity::resolveEntity()->searchFieldAnnotation($field->name, [ Relation\Ignore::class ])) {
|
||||||
$fieldlist[] = $key;
|
$fieldlist[] = $key;
|
||||||
$fieldlist[] = $entity::field($field->name, $this->escapeIdentifier($alias));
|
$fieldlist[] = $entity::field($field->name, $this->escapeIdentifier($alias));
|
||||||
}
|
}
|
||||||
|
@ -623,26 +625,26 @@ class Repository
|
||||||
$this->joined[$item] = true;
|
$this->joined[$item] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$annotation = $this->entityResolver->searchFieldAnnotation($item, [ Attribute\Property\Join::class ]) ?:
|
$attribute = $this->entityResolver->searchFieldAnnotation($item, [ Join::class ]) ?:
|
||||||
$this->entityResolver->searchFieldAnnotation($item, [ Attribute\Property\Relation::class ]);
|
$this->entityResolver->searchFieldAnnotation($item, [ Relation::class ]);
|
||||||
|
|
||||||
$isRelation = ( $annotation instanceof Relation ) || ($annotation instanceof Attribute\Property\Relation);
|
$isRelation = ( $attribute instanceof Relation ) || ($attribute instanceof Relation);
|
||||||
|
|
||||||
if ($isRelation && ( $annotation->isManyToMany() )) {
|
if ($isRelation && ( $attribute->isManyToMany() )) {
|
||||||
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.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $annotation ) {
|
if ( $attribute ) {
|
||||||
$alias = $annotation->alias ?? $item;
|
$alias = $attribute->alias ?? $item;
|
||||||
|
|
||||||
$entity = $annotation->entity ?? $this->entityResolver->reflectedClass->getProperties(true)[$item]->getTypes()[0]->type;
|
$entity = $attribute->entity ?? $this->entityResolver->reflectedClass->getProperties(true)[$item]->getTypes()[0]->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, [ Attribute\Property\Relation\Ignore::class ]) ) {
|
if ( null === $entity::resolveEntity()->searchFieldAnnotation($field->name, [ Relation\Ignore::class ]) ) {
|
||||||
$escAlias = $this->escapeIdentifier($alias);
|
$escAlias = $this->escapeIdentifier($alias);
|
||||||
$fieldName = $this->escapeIdentifier($key);
|
$fieldName = $this->escapeIdentifier($key);
|
||||||
|
|
||||||
$name = $entity::resolveEntity()->searchFieldAnnotation($field->name, [ Attribute\Property\Field::class ])->name ?? $field->name;
|
$name = $entity::resolveEntity()->searchFieldAnnotation($field->name, [ Field::class ])->name ?? $field->name;
|
||||||
|
|
||||||
|
|
||||||
$this->select("$escAlias.$fieldName as $alias\${$name}");
|
$this->select("$escAlias.$fieldName as $alias\${$name}");
|
||||||
|
@ -652,7 +654,7 @@ class Repository
|
||||||
$this->open();
|
$this->open();
|
||||||
|
|
||||||
if ( ! in_array(WithOptionEnum::SkipWhere, $options)) {
|
if ( ! in_array(WithOptionEnum::SkipWhere, $options)) {
|
||||||
foreach($this->entityResolver->searchFieldAnnotationList($item, [ Attribute\Property\Where::class ] ) as $condition) {
|
foreach($this->entityResolver->searchFieldAnnotationList($item, [ 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->getValue(), $condition->operator);
|
$this->where(is_object($condition->field) ? $condition->field : $entity::field($condition->field), $condition->getValue(), $condition->operator);
|
||||||
}
|
}
|
||||||
|
@ -660,26 +662,26 @@ class Repository
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! in_array(WithOptionEnum::SkipHaving, $options)) {
|
if ( ! in_array(WithOptionEnum::SkipHaving, $options)) {
|
||||||
foreach ($this->entityResolver->searchFieldAnnotationList($item, [ Attribute\Property\Having::class ]) as $condition) {
|
foreach ($this->entityResolver->searchFieldAnnotationList($item, [ Having::class ]) as $condition) {
|
||||||
$this->having(is_object($condition->field) ? $condition->field : $entity::field($condition->field), $condition->getValue(), $condition->operator);
|
$this->having(is_object($condition->field) ? $condition->field : $entity::field($condition->field), $condition->getValue(), $condition->operator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! in_array(WithOptionEnum::SkipFilter, $options)) {
|
if ( ! in_array(WithOptionEnum::SkipFilter, $options)) {
|
||||||
foreach ($this->entityResolver->searchFieldAnnotationList($item, [ Attribute\Property\Filter::class ]) as $filter) {
|
foreach ($this->entityResolver->searchFieldAnnotationList($item, [ 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]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->close();
|
$this->close();
|
||||||
|
|
||||||
$key = is_string($annotation->key) ? $this->entityClass::field($annotation->key) : $annotation->key;
|
$key = is_string($attribute->key) ? $this->entityClass::field($attribute->key) : $attribute->key;
|
||||||
|
|
||||||
$foreignKey = is_string($annotation->foreignKey) ? $entity::field($annotation->foreignKey, $alias) : $annotation->foreignKey;
|
$foreignKey = is_string($attribute->foreignKey) ? $entity::field($attribute->foreignKey, $alias) : $attribute->foreignKey;
|
||||||
|
|
||||||
$this->join("LEFT", $entity::resolveEntity()->tableName(), $key, $foreignKey, $alias, function($join) use ($item, $entity, $alias, $options) {
|
$this->join("LEFT", $entity::resolveEntity()->tableName(), $key, $foreignKey, $alias, function($join) use ($item, $entity, $alias, $options) {
|
||||||
if ( ! in_array(WithOptionEnum::SkipJoinWhere, $options)) {
|
if ( ! in_array(WithOptionEnum::SkipJoinWhere, $options)) {
|
||||||
foreach($this->entityResolver->searchFieldAnnotationList($item, [ Attribute\Property\Where::class ]) as $condition) {
|
foreach($this->entityResolver->searchFieldAnnotationList($item, [ Where::class ]) as $condition) {
|
||||||
if ( ! is_object($condition->field) ) {
|
if ( ! is_object($condition->field) ) {
|
||||||
$field = $this->entityClass::field($condition->field);
|
$field = $this->entityClass::field($condition->field);
|
||||||
}
|
}
|
||||||
|
@ -697,14 +699,14 @@ class Repository
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! in_array(WithOptionEnum::SkipJoinFilter, $options) ) {
|
if ( ! in_array(WithOptionEnum::SkipJoinFilter, $options) ) {
|
||||||
foreach ($this->entityResolver->searchFieldAnnotationList($item, [ Attribute\Property\FilterJoin::class ]) as $filter) {
|
foreach ($this->entityResolver->searchFieldAnnotationList($item, [ 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]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new \Exception("Referenced field `$item` which do not exist or do not contain a valid @Join or @Relation annotation.");
|
throw new \Exception("Referenced field `$item` which do not exist or do not contain a valid @Join or @Relation attribute.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -724,7 +726,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, [ Attribute\Property\Relation::class ]) ) {
|
if ( $relation = $this->entityResolver->searchFieldAnnotation($item, [ Relation::class ]) ) {
|
||||||
$alias = $relation->alias ?? $item;
|
$alias = $relation->alias ?? $item;
|
||||||
|
|
||||||
if ( $relation->isManyToMany() ) {
|
if ( $relation->isManyToMany() ) {
|
||||||
|
@ -745,11 +747,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, [ Attribute\Property\Where::class ]) as $condition) {
|
foreach($this->entityResolver->searchFieldAnnotationList($item, [ Where::class ]) as $condition) {
|
||||||
$repository->where(is_object($condition->field) ? $condition->field : $entity::field($condition->field), $condition->getValue(), $condition->operator);
|
$repository->where(is_object($condition->field) ? $condition->field : $entity::field($condition->field), $condition->getValue(), $condition->operator);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach($this->entityResolver->searchFieldAnnotationList($item, [ Attribute\Property\Having::class ] ) as $condition) {
|
foreach($this->entityResolver->searchFieldAnnotationList($item, [ Having::class ] ) as $condition) {
|
||||||
$repository->having(is_object($condition->field) ? $condition->field : $entity::field($condition->field), $condition->getValue(), $condition->operator);
|
$repository->having(is_object($condition->field) ? $condition->field : $entity::field($condition->field), $condition->getValue(), $condition->operator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -766,7 +768,7 @@ class Repository
|
||||||
$this->select("(" . $r = Common\Sql::raw($repository->queryBuilder->render() . ") as $item\$collection"));
|
$this->select("(" . $r = Common\Sql::raw($repository->queryBuilder->render() . ") as $item\$collection"));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new \Exception("You referenced field `$item` which do not exist or do not contain a valid @Join annotation.");
|
throw new \Exception("You referenced field `$item` which do not exist or do not contain a valid @Join attribute.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -776,9 +778,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, [ Attribute\Property\Relation::class ] ))) {
|
if (null !== ($relation = $this->entityResolver->searchFieldAnnotation($name, [ Relation::class ] ))) {
|
||||||
$order = $this->entityResolver->searchFieldAnnotationList($name, [ Attribute\Property\OrderBy::class ]);
|
$order = $this->entityResolver->searchFieldAnnotationList($name, [ OrderBy::class ]);
|
||||||
$where = $this->entityResolver->searchFieldAnnotationList($name, [ Attribute\Property\Where::class ]);
|
$where = $this->entityResolver->searchFieldAnnotationList($name, [ 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();
|
||||||
|
@ -789,7 +791,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, [ Attribute\Property\Relation\Ignore::class ])) {
|
if (null === $baseEntityResolver->searchFieldAnnotation($field->name, [ Relation\Ignore::class ])) {
|
||||||
$repository->select($baseEntityResolver->entityClass::field($key));
|
$repository->select($baseEntityResolver->entityClass::field($key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,20 +2,13 @@
|
||||||
|
|
||||||
namespace Ulmus\Repository;
|
namespace Ulmus\Repository;
|
||||||
|
|
||||||
use Ulmus\{Attribute\Property\Field,
|
use Ulmus\{
|
||||||
Ulmus,
|
Ulmus, Query, Common\EntityResolver, Repository, Event,
|
||||||
Annotation,
|
};
|
||||||
Attribute,
|
|
||||||
Query,
|
|
||||||
Common,
|
|
||||||
Common\EntityResolver,
|
|
||||||
Repository,
|
|
||||||
Event,
|
|
||||||
EntityCollection};
|
|
||||||
|
|
||||||
use Ulmus\Annotation\Property\{Filter, OrderBy, Relation, Relation\Ignore as RelationIgnore, Where, WithJoin, };
|
use Ulmus\Attribute\Property\{
|
||||||
|
Filter, Join, OrderBy, Relation, Virtual, Where, WithJoin,
|
||||||
use Closure;
|
};
|
||||||
|
|
||||||
class RelationBuilder
|
class RelationBuilder
|
||||||
{
|
{
|
||||||
|
@ -64,7 +57,7 @@ class RelationBuilder
|
||||||
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if ( $relation = $this->resolver->searchFieldAnnotation($name, [ Attribute\Property\Relation::class , Relation::class ] ) ) {
|
if ( $relation = $this->resolver->searchFieldAnnotation($name, [ Relation::class ] ) ) {
|
||||||
return $this->instanciateEmptyObject($name, $relation);
|
return $this->instanciateEmptyObject($name, $relation);
|
||||||
}
|
}
|
||||||
elseif ($virtual = $this->resolveVirtual($name)) {
|
elseif ($virtual = $this->resolveVirtual($name)) {
|
||||||
|
@ -77,7 +70,7 @@ class RelationBuilder
|
||||||
|
|
||||||
protected function resolveVirtual(string $name) : mixed
|
protected function resolveVirtual(string $name) : mixed
|
||||||
{
|
{
|
||||||
if (null !== ($virtual = $this->resolver->searchFieldAnnotation($name, [ Attribute\Property\Virtual::class, Annotation\Property\Virtual::class ]))) {
|
if (null !== ($virtual = $this->resolver->searchFieldAnnotation($name, Virtual::class))) {
|
||||||
try {
|
try {
|
||||||
$arguments = [ $this->entity ];
|
$arguments = [ $this->entity ];
|
||||||
|
|
||||||
|
@ -98,11 +91,11 @@ class RelationBuilder
|
||||||
|
|
||||||
protected function resolveRelation(string $name) : mixed
|
protected function resolveRelation(string $name) : mixed
|
||||||
{
|
{
|
||||||
if ( null !== ( $relation = $this->resolver->searchFieldAnnotation($name, [ Attribute\Property\Relation::class, Relation::class ] ) ) ) {
|
if ( null !== ( $relation = $this->resolver->searchFieldAnnotation($name, [ Relation::class ] ) ) ) {
|
||||||
$this->orders = $this->resolver->searchFieldAnnotationList($name, [ Attribute\Property\OrderBy::class, OrderBy::class ] );
|
$this->orders = $this->resolver->searchFieldAnnotationList($name, [ OrderBy::class ] );
|
||||||
$this->wheres = $this->resolver->searchFieldAnnotationList($name, [ Attribute\Property\Where::class, Where::class ] );
|
$this->wheres = $this->resolver->searchFieldAnnotationList($name, [ Where::class ] );
|
||||||
$this->filters = $this->resolver->searchFieldAnnotationList($name, [ Attribute\Property\Filter::class, Filter::class ] );
|
$this->filters = $this->resolver->searchFieldAnnotationList($name, [ Filter::class ] );
|
||||||
$this->joins = $this->resolver->searchFieldAnnotationList($name, [ Attribute\Property\WithJoin::class, WithJoin::class ] );
|
$this->joins = $this->resolver->searchFieldAnnotationList($name, [ WithJoin::class ] );
|
||||||
|
|
||||||
switch( true ) {
|
switch( true ) {
|
||||||
case $relation->isOneToOne():
|
case $relation->isOneToOne():
|
||||||
|
@ -178,22 +171,22 @@ class RelationBuilder
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function instanciateEmptyEntity(string $name, Relation|Attribute\Property\Relation $relation) : object
|
protected function instanciateEmptyEntity(string $name, Relation $relation) : object
|
||||||
{
|
{
|
||||||
$class = $relation->entity ?? $this->resolver->properties[$name]['type'];
|
$class = $relation->entity ?? $this->resolver->reflectedClass->getProperties()[$name]->getTypes()[0]->type;
|
||||||
|
|
||||||
return new $class();
|
return new $class();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected function instanciateEmptyObject(string $name, Relation|Attribute\Property\Relation $relation) : object
|
protected function instanciateEmptyObject(string $name, Relation $relation) : object
|
||||||
{
|
{
|
||||||
switch( true ) {
|
switch( true ) {
|
||||||
case $relation->isOneToOne():
|
case $relation->isOneToOne():
|
||||||
return $this->instanciateEmptyEntity($name, $relation);
|
return $this->instanciateEmptyEntity($name, $relation);
|
||||||
|
|
||||||
case $relation->isOneToMany():
|
case $relation->isOneToMany():
|
||||||
return ($relation->entity ?? $this->resolver->properties[$name]['type'])::entityCollection();
|
return ($relation->entity ?? $this->resolver->reflectedClass->getProperties()[$name]->getTypes()[0]->type)::entityCollection();
|
||||||
|
|
||||||
case $relation->isManyToMany():
|
case $relation->isManyToMany():
|
||||||
extract($this->relationAnnotations($name, $relation));
|
extract($this->relationAnnotations($name, $relation));
|
||||||
|
@ -206,20 +199,20 @@ class RelationBuilder
|
||||||
|
|
||||||
protected function fetchFromDataset($name, ? array $data = null) : object|bool
|
protected function fetchFromDataset($name, ? array $data = null) : object|bool
|
||||||
{
|
{
|
||||||
$annotation = $this->resolver->searchFieldAnnotation($name, [ Attribute\Property\Join::class, Annotation\Property\Join::class ]) ?:
|
$attribute = $this->resolver->searchFieldAnnotation($name, [ Join::class ]) ?:
|
||||||
$this->resolver->searchFieldAnnotation($name, [ Attribute\Property\Relation::class, Annotation\Property\Relation::class ]);
|
$this->resolver->searchFieldAnnotation($name, [ Relation::class ]);
|
||||||
|
|
||||||
if ( $annotation ) {
|
if ( $attribute ) {
|
||||||
$vars = [];
|
$vars = [];
|
||||||
$len = strlen( $name ) + 1;
|
$len = strlen( $name ) + 1;
|
||||||
|
|
||||||
$isRelation = ( $annotation instanceof Relation ) || ( $annotation instanceof Attribute\Property\Relation );
|
$isRelation = $attribute instanceof Relation;
|
||||||
|
|
||||||
if ( $isRelation && $annotation->isManyToMany() ) {
|
if ( $isRelation && $attribute->isManyToMany() ) {
|
||||||
$entity = $this->relationAnnotations($name, $annotation)['relationRelation']->entity;
|
$entity = $this->relationAnnotations($name, $attribute)['relationRelation']->entity;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$entity = $annotation->entity ?? $this->resolver->reflectedClass->getProperties()[$name]->getTypes()[0]->type;
|
$entity = $attribute->entity ?? $this->resolver->reflectedClass->getProperties()[$name]->getTypes()[0]->type;
|
||||||
}
|
}
|
||||||
|
|
||||||
$name = strtolower($name);
|
$name = strtolower($name);
|
||||||
|
@ -259,12 +252,12 @@ class RelationBuilder
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function oneToOne(string $name, Relation|Attribute\Property\Relation $relation) : Repository
|
public function oneToOne(string $name, Relation $relation) : Repository
|
||||||
{
|
{
|
||||||
return $this->oneToMany($name, $relation)->limit(1);
|
return $this->oneToMany($name, $relation)->limit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function oneToMany(string $name, Relation|Attribute\Property\Relation $relation) : Repository
|
public function oneToMany(string $name, Relation $relation) : Repository
|
||||||
{
|
{
|
||||||
$baseEntity = $relation->entity ?? $this->resolver->reflectedClass->getProperties()[$name]->getTypes()[0]->type;
|
$baseEntity = $relation->entity ?? $this->resolver->reflectedClass->getProperties()[$name]->getTypes()[0]->type;
|
||||||
|
|
||||||
|
@ -290,7 +283,7 @@ class RelationBuilder
|
||||||
return $this->applyFilter($this->repository, $name);
|
return $this->applyFilter($this->repository, $name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function manyToMany(string $name, Relation|Attribute\Property\Relation $relation, Relation|Attribute\Property\Relation & $relationRelation = null, bool $selectBridgeField = true) : Repository
|
public function manyToMany(string $name, Relation $relation, Relation & $relationRelation = null, bool $selectBridgeField = true) : Repository
|
||||||
{
|
{
|
||||||
extract($this->relationAnnotations($name, $relation));
|
extract($this->relationAnnotations($name, $relation));
|
||||||
|
|
||||||
|
@ -318,7 +311,7 @@ class RelationBuilder
|
||||||
return $this->applyFilter($this->repository, $name);
|
return $this->applyFilter($this->repository, $name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function relationAnnotations(string $name, Relation|Attribute\Property\Relation $relation) : array
|
public static function relationAnnotations(string $name, Relation $relation) : array
|
||||||
{
|
{
|
||||||
if ( $relation->isOneToOne() || $relation->isManyToMany() ) {
|
if ( $relation->isOneToOne() || $relation->isManyToMany() ) {
|
||||||
if ( ! $relation->hasBridge() ) {
|
if ( ! $relation->hasBridge() ) {
|
||||||
|
@ -326,14 +319,14 @@ class RelationBuilder
|
||||||
}
|
}
|
||||||
|
|
||||||
$bridgeEntity = Ulmus::resolveEntity($relation->bridge);
|
$bridgeEntity = Ulmus::resolveEntity($relation->bridge);
|
||||||
$bridgeRelation = $bridgeEntity->searchFieldAnnotation($relation->field, [ Attribute\Property\Relation::class, Relation::class ]);
|
$bridgeRelation = $bridgeEntity->searchFieldAnnotation($relation->field, [ Relation::class ]);
|
||||||
$relationRelation = $bridgeEntity->searchFieldAnnotation($relation->foreignField, [ Attribute\Property\Relation::class, Relation::class ]);
|
$relationRelation = $bridgeEntity->searchFieldAnnotation($relation->foreignField, [ 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 attribute not found for field `{$relation->foreignField}` in entity {$relation->bridge}");
|
||||||
}
|
}
|
||||||
|
|
||||||
$relationRelation->entity ??= $relation->bridge::resolveEntity()->properties[$relation->foreignField]['type'];
|
$relationRelation->entity ??= $relation->bridge::resolveEntity()->reflectedClass->getProperties()[$relation->foreignField]->getTypes()[0]->type;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'bridgeEntity' => $bridgeEntity,
|
'bridgeEntity' => $bridgeEntity,
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\SearchRequest\Attribute;
|
||||||
|
|
||||||
|
enum PropertyValueSource
|
||||||
|
{
|
||||||
|
case QueryParams;
|
||||||
|
|
||||||
|
case RequestAttribute;
|
||||||
|
|
||||||
|
public const ATTRIBUTE_FIRST = [ self::RequestAttribute, self::QueryParams ];
|
||||||
|
|
||||||
|
public const QUERY_PARAMS_FIRST = [ self::QueryParams, self::RequestAttribute ];
|
||||||
|
}
|
|
@ -9,5 +9,6 @@ class SearchGroupBy extends SearchParameter
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public null|string|\Stringable|array $field = null,
|
public null|string|\Stringable|array $field = null,
|
||||||
|
public string $description = "",
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
|
@ -8,9 +8,11 @@ use Ulmus\SearchRequest\SearchMethodEnum;
|
||||||
class SearchLike extends SearchParameter
|
class SearchLike extends SearchParameter
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public ? string $parameter = null,
|
public null|string|array $parameter = null,
|
||||||
public null|string|\Stringable|array $field = null,
|
public null|string|\Stringable|array $field = null,
|
||||||
public bool $toggle = false,
|
public bool $toggle = false,
|
||||||
public SearchMethodEnum $method = SearchMethodEnum::Like,
|
public SearchMethodEnum $method = SearchMethodEnum::Like,
|
||||||
|
public string $description = "",
|
||||||
|
public PropertyValueSource|array $source = PropertyValueSource::QueryParams,
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
|
@ -12,8 +12,9 @@ class SearchOrderBy extends SearchParameter
|
||||||
public const DESCENDING = "DESC";
|
public const DESCENDING = "DESC";
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public ? string $parameter = null,
|
public null|string|array $parameter = null,
|
||||||
public null|string|\Stringable|array $field = null,
|
public null|string|\Stringable|array $field = null,
|
||||||
public null|SearchMethodEnum $order = null,
|
public null|SearchMethodEnum $order = null,
|
||||||
|
public string $description = "",
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
|
@ -5,8 +5,21 @@ namespace Ulmus\SearchRequest\Attribute;
|
||||||
use Ulmus\SearchRequest\SearchMethodEnum;
|
use Ulmus\SearchRequest\SearchMethodEnum;
|
||||||
|
|
||||||
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
||||||
class SearchParameter
|
abstract class SearchParameter {
|
||||||
{
|
|
||||||
|
|
||||||
|
public PropertyValueSource|array $source = PropertyValueSource::QueryParams;
|
||||||
|
|
||||||
|
public null|string|\Stringable|array $field = null;
|
||||||
|
|
||||||
|
public string $description = "";
|
||||||
|
|
||||||
|
public function getSources() : array
|
||||||
|
{
|
||||||
|
return is_array($this->source) ? $this->source : [ $this->source ];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getParameters() : array
|
||||||
|
{
|
||||||
|
return array_filter((array) $this->parameter);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -10,5 +10,6 @@ class SearchRequestParameter
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public string $class,
|
public string $class,
|
||||||
public ? string $alias = null,
|
public ? string $alias = null,
|
||||||
|
public string $description = "",
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
|
@ -8,9 +8,11 @@ use Ulmus\SearchRequest\SearchMethodEnum;
|
||||||
class SearchWhere extends SearchParameter
|
class SearchWhere extends SearchParameter
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public ? string $parameter = null,
|
public null|string|array $parameter = null,
|
||||||
public null|string|\Stringable|array $field = null,
|
public null|string|\Stringable|array $field = null,
|
||||||
public bool $toggle = false,
|
public bool $toggle = false,
|
||||||
public SearchMethodEnum $method = SearchMethodEnum::Where,
|
public SearchMethodEnum $method = SearchMethodEnum::Where,
|
||||||
|
public string $description = "",
|
||||||
|
public PropertyValueSource|array $source = PropertyValueSource::QueryParams,
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
|
@ -2,9 +2,16 @@
|
||||||
|
|
||||||
namespace Ulmus\SearchRequest;
|
namespace Ulmus\SearchRequest;
|
||||||
|
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
use Ulmus\SearchRequest\Attribute\{ SearchParameter, SearchWhere, SearchLike, SearchOrderBy, SearchGroupBy, SearchRequestParameter, };
|
use Ulmus\SearchRequest\Attribute\{PropertyValueSource,
|
||||||
|
SearchParameter,
|
||||||
|
SearchWhere,
|
||||||
|
SearchLike,
|
||||||
|
SearchOrderBy,
|
||||||
|
SearchGroupBy,
|
||||||
|
SearchRequestParameter};
|
||||||
|
|
||||||
trait SearchRequestFromRequestTrait
|
trait SearchRequestFromRequestTrait
|
||||||
{
|
{
|
||||||
|
@ -18,6 +25,10 @@ trait SearchRequestFromRequestTrait
|
||||||
|
|
||||||
public function fromRequest(ServerRequestInterface $request)
|
public function fromRequest(ServerRequestInterface $request)
|
||||||
{
|
{
|
||||||
|
if (method_exists($this, 'prepare')) {
|
||||||
|
$this->prepare($request);
|
||||||
|
}
|
||||||
|
|
||||||
$queryParams = new \ArrayObject(array_filter($request->getQueryParams(), function($i) { return ! is_null($i) && $i !== ""; }));
|
$queryParams = new \ArrayObject(array_filter($request->getQueryParams(), function($i) { return ! is_null($i) && $i !== ""; }));
|
||||||
|
|
||||||
$this->page = $queryParams->offsetExists('page') ? $queryParams['page'] : 1;
|
$this->page = $queryParams->offsetExists('page') ? $queryParams['page'] : 1;
|
||||||
|
@ -26,15 +37,12 @@ trait SearchRequestFromRequestTrait
|
||||||
|
|
||||||
foreach($classReflection->getProperties() as $property) {
|
foreach($classReflection->getProperties() as $property) {
|
||||||
$attributeList = $property->getAttributes();
|
$attributeList = $property->getAttributes();
|
||||||
|
|
||||||
$attributeReflection = array_filter($attributeList, fn($e) => $e->newInstance() instanceof Attribute\SearchParameter);
|
$attributeReflection = array_filter($attributeList, fn($e) => $e->newInstance() instanceof Attribute\SearchParameter);
|
||||||
|
|
||||||
if ($attributeReflection) {
|
if ($attributeReflection) {
|
||||||
$attribute = $attributeReflection[0]->newInstance();
|
$attribute = $attributeReflection[0]->newInstance();
|
||||||
|
|
||||||
$propertyName = $property->getName();
|
$propertyName = $property->getName();
|
||||||
$fieldName = $attribute->field ?? $propertyName;
|
$fieldName = $attribute->field ?? $propertyName;
|
||||||
$queryParamName = $attribute->parameter ?? $propertyName;
|
|
||||||
|
|
||||||
# Field could be defined for another entity class
|
# Field could be defined for another entity class
|
||||||
if (is_array($fieldName)) {
|
if (is_array($fieldName)) {
|
||||||
|
@ -51,11 +59,15 @@ trait SearchRequestFromRequestTrait
|
||||||
$field = $fieldName;
|
$field = $fieldName;
|
||||||
}
|
}
|
||||||
|
|
||||||
$value = $queryParams->offsetExists($queryParamName) ? $this->transformValue($attributeList, $queryParams[$queryParamName]) : null;
|
$value = $this->getValueFromSource($request, $propertyName, $attribute);
|
||||||
|
|
||||||
|
if ($value !== null) {
|
||||||
|
$value = $this->transformValue($attributeList, $value);
|
||||||
|
}
|
||||||
|
|
||||||
if ($attribute instanceof SearchWhere || $attribute instanceof SearchLike) {
|
if ($attribute instanceof SearchWhere || $attribute instanceof SearchLike) {
|
||||||
if ($attribute->toggle) {
|
if ($attribute->toggle) {
|
||||||
$this->$propertyName = $queryParams->offsetExists($queryParamName);
|
$this->$propertyName = ! empty($value);
|
||||||
}
|
}
|
||||||
elseif ($value !== null) {
|
elseif ($value !== null) {
|
||||||
$this->$propertyName = $value;
|
$this->$propertyName = $value;
|
||||||
|
@ -79,6 +91,39 @@ trait SearchRequestFromRequestTrait
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function getValueFromSource(RequestInterface $request, string $propertyName, SearchParameter $attribute) : mixed
|
||||||
|
{
|
||||||
|
$queryParamName = $attribute->getParameters() ?? [ $propertyName ];
|
||||||
|
|
||||||
|
foreach($attribute->getSources() as $source) {
|
||||||
|
switch($source) {
|
||||||
|
case PropertyValueSource::QueryParams:
|
||||||
|
$queryParams = new \ArrayObject(array_filter($request->getQueryParams(), function($i) { return ! is_null($i) && $i !== ""; }));
|
||||||
|
|
||||||
|
foreach($queryParamName as $param) {
|
||||||
|
if ($queryParams->offsetExists($param)) {
|
||||||
|
return $queryParams[$param];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PropertyValueSource::RequestAttribute:
|
||||||
|
foreach($queryParamName as $param) {
|
||||||
|
$fromAttribute = $request->getAttribute($param, null);
|
||||||
|
|
||||||
|
if ($fromAttribute !== null) {
|
||||||
|
return $fromAttribute;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
protected function transformValue(array $attributeList, mixed $value, ) : mixed
|
protected function transformValue(array $attributeList, mixed $value, ) : mixed
|
||||||
{
|
{
|
||||||
$transforms = array_map(function($e) {
|
$transforms = array_map(function($e) {
|
||||||
|
|
Loading…
Reference in New Issue