Compare commits
2 Commits
430cd03bc4
...
e97df353ae
Author | SHA1 | Date | |
---|---|---|---|
e97df353ae | |||
00ebf3381f |
@ -7,7 +7,7 @@ use Ulmus\Common\PdoObject;
|
||||
use Ulmus\Entity\Sqlite\Table;
|
||||
use Ulmus\Exception\AdapterConfigurationException;
|
||||
use Ulmus\Migration\FieldDefinition;
|
||||
use Ulmus\{ Repository, QueryBuilder };
|
||||
use Ulmus\{Repository, QueryBuilder, Ulmus};
|
||||
|
||||
class SQLite implements AdapterInterface {
|
||||
use DefaultAdapterTrait;
|
||||
@ -38,6 +38,8 @@ class SQLite implements AdapterInterface {
|
||||
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
||||
$pdo->setAttribute(\PDO::ATTR_EMULATE_PREPARES, false);
|
||||
$pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC);
|
||||
|
||||
$this->exportFunctions($pdo);
|
||||
}
|
||||
catch(PDOException $ex){
|
||||
throw $ex;
|
||||
@ -126,7 +128,23 @@ class SQLite implements AdapterInterface {
|
||||
}
|
||||
}
|
||||
|
||||
return $typeOnly ? $type : $type . ( $length ? "($length" . ( $precision ? ",$precision" : "" ) . ")" : "" );
|
||||
return $typeOnly ? $type : $type . ( $length ? "($length" . ( isset($precision) ? ",$precision" : "" ) . ")" : "" );
|
||||
}
|
||||
|
||||
public function writableValue(/* mixed */ $value) /*: mixed*/
|
||||
{
|
||||
switch (true) {
|
||||
case is_object($value):
|
||||
return Ulmus::convertObject($value);
|
||||
|
||||
case is_array($value):
|
||||
return json_encode($value);
|
||||
|
||||
case is_bool($value):
|
||||
return (int) $value;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function tableSyntax() : array
|
||||
@ -147,4 +165,28 @@ class SQLite implements AdapterInterface {
|
||||
{
|
||||
return QueryBuilder\SqliteQueryBuilder::class;
|
||||
}
|
||||
|
||||
public function exportFunctions(PdoObject $pdo) : void
|
||||
{
|
||||
$pdo->sqliteCreateFunction('lcase', fn($string) => strtolower($string), 1);
|
||||
$pdo->sqliteCreateFunction('ucase', fn($string) => strtoupper($string), 1);
|
||||
$pdo->sqliteCreateFunction('left', fn($string, $length) => substr($string, 0, $length), 2);
|
||||
$pdo->sqliteCreateFunction('right', fn($string, $length) => substr($string, -$length), 2);
|
||||
$pdo->sqliteCreateFunction('strcmp', fn($s1, $s2) => strcmp($s1, $s2), 2);
|
||||
$pdo->sqliteCreateFunction('lpad', fn($string, $length, $pad) => str_pad($string, $length, $pad, STR_PAD_LEFT), 3);
|
||||
$pdo->sqliteCreateFunction('rpad', fn($string, $length, $pad) => str_pad($string, $length, $pad, STR_PAD_RIGHT), 3);
|
||||
$pdo->sqliteCreateFunction('concat', fn(...$args) => implode('', $args), -1);
|
||||
$pdo->sqliteCreateFunction('concat_ws', fn($separator, ...$args) => implode($separator, $args), -1);
|
||||
$pdo->sqliteCreateFunction('find_in_set', function($string, $string_list) {
|
||||
if ( $string === null || $string_list === null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($string_list === "") {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (int) in_array($string, explode(',', $string_list));
|
||||
}, 2);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Ulmus\Annotation;
|
||||
|
||||
interface Annotation {}
|
@ -1,103 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Ulmus\Annotation;
|
||||
|
||||
use Reflector, ReflectionClass, ReflectionProperty, ReflectionMethod;
|
||||
|
||||
/**
|
||||
* This class exists while waiting for the official RFC [ https://wiki.php.net/rfc/annotations_v2 ]
|
||||
*/
|
||||
class AnnotationReader
|
||||
{
|
||||
const PHP_TYPES = [ "string", "int", "float", "object", "double", "closure", ];
|
||||
|
||||
public string $class;
|
||||
|
||||
public function __construct($class) {
|
||||
$this->class = $class;
|
||||
}
|
||||
|
||||
public static function fromClass($class) : self
|
||||
{
|
||||
return new static($class);
|
||||
}
|
||||
|
||||
public function getProperty(ReflectionProperty $property)
|
||||
{
|
||||
return $this->parseDocComment($property);
|
||||
}
|
||||
|
||||
public function getClass(ReflectionClass $class)
|
||||
{
|
||||
return $this->parseDocComment($class);
|
||||
}
|
||||
|
||||
public function getMethod(ReflectionMethod $method)
|
||||
{
|
||||
return $this->parseDocComment($method);
|
||||
}
|
||||
|
||||
protected function parseDocComment(Reflector $reflect)
|
||||
{
|
||||
$namespace = $this->getObjectNamespace($reflect);
|
||||
$tags = [];
|
||||
|
||||
foreach(preg_split("/\r\n|\n|\r/", $reflect->getDocComment()) as $line) {
|
||||
$line = ltrim($line, "* \t\/");
|
||||
$line = rtrim($line, "\t ");
|
||||
|
||||
if ( substr($line, 0, 1) === '@' ) {
|
||||
$line = ltrim($line, '@');
|
||||
|
||||
$open = strpos($line, "(");
|
||||
$close = strrpos($line, ")");
|
||||
|
||||
if ( ! in_array(false, [ $open, $close ], true) && ( ++$open !== $close ) ) {
|
||||
$arguments = substr($line, $open, $close - $open);
|
||||
|
||||
try {
|
||||
$tags[] = [
|
||||
'tag' => substr($line, 0, $open - 1),
|
||||
'arguments' => eval("namespace $namespace; return [ $arguments ];" ),
|
||||
];
|
||||
}
|
||||
catch(\Throwable $error) {
|
||||
throw new \InvalidArgumentException("An error occured while parsing annotation from '" . $this->getObjectName($reflect) . "' : @$line -- " . $error->getMessage());
|
||||
}
|
||||
}
|
||||
else {
|
||||
$tags[] = [
|
||||
'tag' => $line,
|
||||
'arguments' => [],
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $tags;
|
||||
}
|
||||
|
||||
protected function getObjectName(Reflector $reflect) : string
|
||||
{
|
||||
switch(true) {
|
||||
case $reflect instanceof ReflectionMethod :
|
||||
case $reflect instanceof ReflectionProperty :
|
||||
return $reflect->class . "::" . $reflect->name;
|
||||
|
||||
case $reflect instanceof ReflectionClass :
|
||||
return $reflect->name;
|
||||
}
|
||||
}
|
||||
|
||||
protected function getObjectNamespace(Reflector $reflect) : string
|
||||
{
|
||||
switch(true) {
|
||||
case $reflect instanceof ReflectionMethod :
|
||||
case $reflect instanceof ReflectionProperty :
|
||||
return $reflect->getDeclaringClass()->getNamespaceName();
|
||||
|
||||
case $reflect instanceof ReflectionClass :
|
||||
return $reflect->getNamespaceName();
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,6 @@
|
||||
|
||||
namespace Ulmus\Annotation\Classes;
|
||||
|
||||
class Collation implements \Ulmus\Annotation\Annotation {
|
||||
class Collation implements \Notes\Annotation {
|
||||
|
||||
}
|
||||
|
@ -2,6 +2,6 @@
|
||||
|
||||
namespace Ulmus\Annotation\Classes;
|
||||
|
||||
class Method implements \Ulmus\Annotation\Annotation {
|
||||
class Method implements \Notes\Annotation {
|
||||
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace Ulmus\Annotation\Classes;
|
||||
|
||||
class Table implements \Ulmus\Annotation\Annotation {
|
||||
class Table implements \Notes\Annotation {
|
||||
|
||||
public string $name;
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace Ulmus\Annotation\Property;
|
||||
|
||||
class Field implements \Ulmus\Annotation\Annotation {
|
||||
class Field implements \Notes\Annotation {
|
||||
|
||||
public string $type;
|
||||
|
||||
@ -16,6 +16,8 @@ class Field implements \Ulmus\Annotation\Annotation {
|
||||
|
||||
public bool $nullable;
|
||||
|
||||
public /* mixed */ $default;
|
||||
|
||||
public bool $readonly = false;
|
||||
|
||||
public function __construct(? string $type = null, ? int $length = null)
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace Ulmus\Annotation\Property;
|
||||
|
||||
class Filter implements \Ulmus\Annotation\Annotation {
|
||||
class Filter implements \Notes\Annotation {
|
||||
|
||||
public string $method;
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace Ulmus\Annotation\Property;
|
||||
|
||||
class FilterJoin implements \Ulmus\Annotation\Annotation {
|
||||
class FilterJoin implements \Notes\Annotation {
|
||||
|
||||
public string $method;
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace Ulmus\Annotation\Property;
|
||||
|
||||
class GroupBy implements \Ulmus\Annotation\Annotation {
|
||||
class GroupBy implements \Notes\Annotation {
|
||||
|
||||
public array $fields = [];
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace Ulmus\Annotation\Property;
|
||||
|
||||
class Join implements \Ulmus\Annotation\Annotation {
|
||||
class Join implements \Notes\Annotation {
|
||||
|
||||
public string $type;
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace Ulmus\Annotation\Property;
|
||||
|
||||
class OrderBy implements \Ulmus\Annotation\Annotation {
|
||||
class OrderBy implements \Notes\Annotation {
|
||||
|
||||
public string $field;
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace Ulmus\Annotation\Property;
|
||||
|
||||
class Relation implements \Ulmus\Annotation\Annotation {
|
||||
class Relation implements \Notes\Annotation {
|
||||
|
||||
public string $type;
|
||||
|
||||
@ -69,6 +69,18 @@ class Relation implements \Ulmus\Annotation\Annotation {
|
||||
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);
|
||||
|
@ -2,4 +2,4 @@
|
||||
|
||||
namespace Ulmus\Annotation\Property\Relation;
|
||||
|
||||
class Ignore implements \Ulmus\Annotation\Annotation {}
|
||||
class Ignore implements \Notes\Annotation {}
|
||||
|
@ -4,7 +4,7 @@ namespace Ulmus\Annotation\Property;
|
||||
|
||||
use Ulmus\Query;
|
||||
|
||||
class Where implements \Ulmus\Annotation\Annotation {
|
||||
class Where implements \Notes\Annotation {
|
||||
|
||||
public /* stringable */ $field;
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace Ulmus\Annotation\Property;
|
||||
|
||||
class WithJoin implements \Ulmus\Annotation\Annotation {
|
||||
class WithJoin implements \Notes\Annotation {
|
||||
|
||||
protected array $joins;
|
||||
|
||||
|
@ -15,11 +15,11 @@ class EntityField implements WhereRawParameter
|
||||
|
||||
public string $entityClass;
|
||||
|
||||
public string $alias;
|
||||
public ? string $alias;
|
||||
|
||||
protected EntityResolver $entityResolver;
|
||||
|
||||
public function __construct(string $entityClass, string $name, string $alias, EntityResolver $resolver)
|
||||
public function __construct(string $entityClass, string $name, ? string $alias, EntityResolver $resolver)
|
||||
{
|
||||
$this->entityClass = $entityClass;
|
||||
$this->name = $name;
|
||||
|
@ -3,12 +3,15 @@
|
||||
namespace Ulmus\Common;
|
||||
|
||||
use Ulmus\Ulmus,
|
||||
Ulmus\Annotation\Annotation,
|
||||
Ulmus\Annotation\Classes\Table,
|
||||
Ulmus\Annotation\Property\Field,
|
||||
Ulmus\Annotation\Property\Virtual,
|
||||
Ulmus\Annotation\Property\Relation;
|
||||
|
||||
use Notes\Annotation;
|
||||
|
||||
use Notes\ObjectReflection;
|
||||
|
||||
class EntityResolver {
|
||||
|
||||
const KEY_ENTITY_NAME = 01;
|
||||
|
@ -1,268 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Ulmus\Common;
|
||||
|
||||
use Ulmus\Ulmus;
|
||||
|
||||
use Ulmus\Annotation\AnnotationReader;
|
||||
|
||||
use Reflector, ReflectionClass, ReflectionProperty, ReflectionMethod;
|
||||
|
||||
class ObjectReflection {
|
||||
|
||||
public AnnotationReader $annotationReader;
|
||||
|
||||
public function __construct($class, AnnotationReader $annotationReader = null) {
|
||||
$this->classReflection = $class instanceof ReflectionClass ? $class : new ReflectionClass($class);
|
||||
$this->annotationReader = $annotationReader ?: AnnotationReader::fromClass($class);
|
||||
}
|
||||
|
||||
public static function fromClass($class) : self
|
||||
{
|
||||
return new static($class);
|
||||
}
|
||||
|
||||
public function read() : array
|
||||
{
|
||||
return [
|
||||
'uses' => $this->gatherUses(true),
|
||||
'class' => $this->gatherClass(true),
|
||||
'method' => $this->gatherMethods(true),
|
||||
'property' => $this->gatherProperties(true),
|
||||
];
|
||||
}
|
||||
|
||||
public function gatherUses(bool $full = true) : array
|
||||
{
|
||||
$list = [];
|
||||
|
||||
if ( $full ) {
|
||||
if ( $parentClass = $this->classReflection->getParentClass() ) {
|
||||
$list = static::fromClass($parentClass)->gatherUses(true);
|
||||
}
|
||||
|
||||
foreach($this->classReflection->getTraits() as $trait) {
|
||||
$list = array_merge($list, static::fromClass($trait)->gatherUses(true));
|
||||
}
|
||||
}
|
||||
|
||||
return array_merge($this->getUsesStatements(), $list);
|
||||
}
|
||||
|
||||
public function gatherClass(bool $full = true) : array
|
||||
{
|
||||
$class = [];
|
||||
|
||||
if ( $full ) {
|
||||
if ( $parentClass = $this->classReflection->getParentClass() ) {
|
||||
$class = static::fromClass($parentClass)->gatherClass(true);
|
||||
}
|
||||
|
||||
$itemName = function($item) {
|
||||
return $item->getName();
|
||||
};
|
||||
}
|
||||
|
||||
return array_merge_recursive($class, [
|
||||
'tags' => $this->annotationReader->getClass($this->classReflection)
|
||||
] + ( ! $full ? [] : [
|
||||
'traits' => array_map($itemName, $this->classReflection->getTraits()),
|
||||
'interfaces' => array_map($itemName, $this->classReflection->getInterfaces()),
|
||||
]));
|
||||
}
|
||||
|
||||
public function gatherProperties(bool $full = true, int $filter =
|
||||
ReflectionProperty::IS_PUBLIC |
|
||||
ReflectionProperty::IS_PROTECTED |
|
||||
ReflectionProperty::IS_PRIVATE
|
||||
) : array
|
||||
{
|
||||
$properties = [];
|
||||
$defaultValues = $this->classReflection->getDefaultProperties();
|
||||
|
||||
if ( $full ) {
|
||||
if ( $parentClass = $this->classReflection->getParentClass() ) {
|
||||
$properties = static::fromClass($parentClass)->gatherProperties($full, $filter);
|
||||
}
|
||||
}
|
||||
|
||||
$list = [];
|
||||
|
||||
foreach($this->classReflection->getProperties($filter) as $property) {
|
||||
$current = [
|
||||
'name' => $property->getName()
|
||||
];
|
||||
|
||||
# Default value can be 'null', so isset() it not suitable here
|
||||
if ( array_key_exists($current['name'], $defaultValues) ) {
|
||||
$current['value'] = $defaultValues[ $current['name'] ];
|
||||
}
|
||||
|
||||
if ( $property->hasType() ) {
|
||||
$current['type'] = $property->getType()->getName();
|
||||
$current['builtin'] = $property->getType()->isBuiltIn();
|
||||
$current['nullable'] = $property->getType()->allowsNull();
|
||||
}
|
||||
|
||||
$current['tags'] = $this->annotationReader->getProperty($property);
|
||||
|
||||
if ( $this->ignoreElementAnnotation($current['tags']) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$list[ $current['name'] ] = $current;
|
||||
}
|
||||
|
||||
return array_merge($properties, $list);
|
||||
}
|
||||
|
||||
public function gatherMethods(bool $full = true, int $filter =
|
||||
ReflectionMethod::IS_PUBLIC |
|
||||
ReflectionMethod::IS_PROTECTED |
|
||||
ReflectionMethod::IS_PRIVATE |
|
||||
ReflectionMethod::IS_STATIC
|
||||
) : array
|
||||
{
|
||||
$list = $methods = [];
|
||||
|
||||
if ( $full ) {
|
||||
if ( $parentClass = $this->classReflection->getParentClass() ) {
|
||||
$methods = static::fromClass($parentClass)->gatherMethods($full, $filter);
|
||||
}
|
||||
}
|
||||
|
||||
foreach($this->classReflection->getMethods($filter) as $method) {
|
||||
$parameters = [];
|
||||
|
||||
foreach($method->getParameters() as $parameter) {
|
||||
$parameters[$parameter->getName()] = [
|
||||
'null' => $parameter->allowsNull(),
|
||||
'position' => $parameter->getPosition(),
|
||||
'type' => $parameter->hasType() ? $parameter->getType()->getName() : false,
|
||||
'array' => $parameter->isArray(),
|
||||
'callable' => $parameter->isCallable(),
|
||||
'optional' => $parameter->isOptional(),
|
||||
'byReference' => $parameter->isPassedByReference(),
|
||||
];
|
||||
}
|
||||
|
||||
$current = [
|
||||
'name' => $method->getName(),
|
||||
'type' => $method->hasReturnType() ? $method->getReturnType()->getName() : false,
|
||||
'constructor' => $method->isConstructor(),
|
||||
'destructor' => $method->isDestructor(),
|
||||
'parameters' => $parameters,
|
||||
];
|
||||
|
||||
$current['tags'] = $this->annotationReader->getMethod($method);
|
||||
|
||||
if ( $this->ignoreElementAnnotation($current['tags']) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$list[ $current['name'] ] = $current;
|
||||
}
|
||||
|
||||
return array_merge($methods, $list);
|
||||
}
|
||||
|
||||
protected function ignoreElementAnnotation($tags) : bool
|
||||
{
|
||||
return in_array('IGNORE', array_map('strtoupper', array_column($tags, 'tag') ));
|
||||
}
|
||||
|
||||
|
||||
protected function readCode() : string
|
||||
{
|
||||
static $code = [];
|
||||
$fileName = $this->classReflection->getFilename();
|
||||
return $code[$fileName] ?? $code[$fileName] = file_get_contents($fileName);
|
||||
}
|
||||
|
||||
protected function getUsesStatements() : array
|
||||
{
|
||||
$uses = [];
|
||||
$tokens = token_get_all( $c = $this->readCode() );
|
||||
|
||||
while ( $token = array_shift($tokens) ) {
|
||||
|
||||
if ( is_array($token) ) {
|
||||
list($token, $value) = $token;
|
||||
}
|
||||
|
||||
switch ($token) {
|
||||
case T_CLASS:
|
||||
case T_TRAIT:
|
||||
case T_INTERFACE:
|
||||
break 2;
|
||||
|
||||
case T_USE:
|
||||
$isUse = true;
|
||||
break;
|
||||
|
||||
case T_NS_SEPARATOR:
|
||||
$isNamespace = $isUse;
|
||||
break;
|
||||
|
||||
case T_STRING:
|
||||
if ( $isNamespace && $latestString ) {
|
||||
$statement[] = $latestString;
|
||||
}
|
||||
|
||||
$latestString = $value;
|
||||
break;
|
||||
|
||||
case T_AS:
|
||||
# My\Name\Space\aClassHere `as` ClassAlias;
|
||||
$replacedClass = implode("\\", array_merge($statement, [ $latestString ]));
|
||||
$latestString = null;
|
||||
break;
|
||||
|
||||
case T_WHITESPACE:
|
||||
case T_COMMENT:
|
||||
case T_DOC_COMMENT:
|
||||
break;
|
||||
|
||||
case '{':
|
||||
# opening a sub-namespace -> \My\Name\Space\`{`OneItem, AnotherItem}
|
||||
if ( $isNamespace ) {
|
||||
$inNamespace = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case ';';
|
||||
case ',':
|
||||
case '}':
|
||||
if ( $isUse ) {
|
||||
if ( $replacedClass ) {
|
||||
$uses[$replacedClass] = $latestString;
|
||||
$replacedClass = "";
|
||||
}
|
||||
elseif ( $latestString ) {
|
||||
$uses[implode("\\", array_merge($statement, [ $latestString ]))] = $latestString;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $inNamespace ) {
|
||||
$latestString = "";
|
||||
|
||||
# \My\Name\Space\{OneItem, AnotherItem`}` <- closing a sub-namespace
|
||||
if ( $token !== "}" ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
case T_OPEN_TAG:
|
||||
default:
|
||||
$statement = [];
|
||||
$latestString = "";
|
||||
$replacedClass = null;
|
||||
$isNamespace = $inNamespace = false;
|
||||
$isUse = ( $isUse ?? false ) && ( $token === ',' );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $uses;
|
||||
}
|
||||
}
|
@ -70,7 +70,6 @@ class EntityCollection extends \ArrayObject {
|
||||
public function removeOne($value, string $field, bool $strict = true) : ? object
|
||||
{
|
||||
foreach($this->search($value, $field, $strict) as $key => $item) {
|
||||
|
||||
$this->offsetUnset($key);
|
||||
|
||||
return $item;
|
||||
@ -79,7 +78,7 @@ class EntityCollection extends \ArrayObject {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function remove(/* mixed */ $values, string $field, bool $strict = true) : array
|
||||
public function remove(/* mixed */ $value, string $field, bool $strict = true) : array
|
||||
{
|
||||
$removed = [];
|
||||
|
||||
@ -161,7 +160,7 @@ class EntityCollection extends \ArrayObject {
|
||||
$list = [];
|
||||
|
||||
foreach($this as $item) {
|
||||
if ( is_callable($field) ) {
|
||||
if ( ! is_string($field) && is_callable($field) ) {
|
||||
$value = call_user_func_array($field, [ $item ]);
|
||||
}
|
||||
else {
|
||||
@ -173,7 +172,7 @@ class EntityCollection extends \ArrayObject {
|
||||
}
|
||||
|
||||
if ( $keyField !== null ) {
|
||||
if ( is_callable($keyField) ) {
|
||||
if ( ! is_string($field) && is_callable($keyField) ) {
|
||||
$key = call_user_func_array($keyField, [ $item ]);
|
||||
}
|
||||
else {
|
||||
|
@ -9,7 +9,7 @@ use Ulmus\Repository,
|
||||
|
||||
use Ulmus\Annotation\Classes\{ Method, Table, Collation, };
|
||||
use Ulmus\Annotation\Property\{ Field, Filter, FilterJoin, Relation, OrderBy, Where, OrWhere, Join, Virtual, On, WithJoin, };
|
||||
use Ulmus\Annotation\Property\Field\{ Id, ForeignKey, CreatedAt, UpdatedAt, Datetime as DateTime, Date, Time, Bigint, Tinyint, Text, Mediumtext, Longtext, };
|
||||
use Ulmus\Annotation\Property\Field\{ Id, ForeignKey, CreatedAt, UpdatedAt, Datetime as DateTime, Date, Time, Bigint, Tinyint, Text, Mediumtext, Longtext, Blob, Mediumblob, Longblob };
|
||||
use Ulmus\Annotation\Property\Relation\{ Ignore as RelationIgnore };
|
||||
|
||||
trait EntityTrait {
|
||||
@ -273,7 +273,7 @@ trait EntityTrait {
|
||||
/**
|
||||
* @Ignore
|
||||
*/
|
||||
public function jsonSerialize()
|
||||
public function jsonSerialize() : mixed
|
||||
{
|
||||
return $this->entityGetDataset();
|
||||
}
|
||||
@ -317,7 +317,7 @@ trait EntityTrait {
|
||||
/**
|
||||
* @Ignore
|
||||
*/
|
||||
public static function field($name, ? string $alias = null) : EntityField
|
||||
public static function field($name, ? string $alias = Repository::DEFAULT_ALIAS) : EntityField
|
||||
{
|
||||
return new EntityField(static::class, $name, $alias ? Ulmus::repository(static::class)->adapter->adapter()->escapeIdentifier($alias, Adapter\AdapterInterface::IDENTIFIER_FIELD) : Repository::DEFAULT_ALIAS, Ulmus::resolveEntity(static::class));
|
||||
}
|
||||
@ -325,7 +325,7 @@ trait EntityTrait {
|
||||
/**
|
||||
* @Ignore
|
||||
*/
|
||||
public static function fields(array $fields, ? string $alias = null) : string
|
||||
public static function fields(array $fields, ? string $alias = Repository::DEFAULT_ALIAS) : string
|
||||
{
|
||||
return implode(', ', array_map(function($item) use ($alias){
|
||||
return static::field($item, $alias);
|
||||
|
@ -463,7 +463,7 @@ class QueryBuilder implements Query\QueryBuilderInterface
|
||||
|
||||
public function getFragments(/*? Query\Fragment|array*/ $fragment = null) : array
|
||||
{
|
||||
return $fragment === null ? array_filter($this->queryStack, fn($e) => get_class($e) === $fragment) : $this->queryStack;
|
||||
return $fragment !== null ? array_filter($this->queryStack, fn($e) => is_a($e, $fragment, true) ) : $this->queryStack;
|
||||
}
|
||||
|
||||
public function __toString() : string
|
||||
|
@ -692,8 +692,6 @@ class Repository
|
||||
{
|
||||
foreach ((array)$fields as $name) {
|
||||
if (null !== ($relation = $this->entityResolver->searchFieldAnnotation($name, new Annotation\Property\Relation()))) {
|
||||
$relationType = strtolower(str_replace(['-', '_', ' '], '', $relation->type));
|
||||
|
||||
$order = $this->entityResolver->searchFieldAnnotationList($name, new Annotation\Property\OrderBy());
|
||||
$where = $this->entityResolver->searchFieldAnnotationList($name, new Annotation\Property\Where());
|
||||
|
||||
@ -705,6 +703,12 @@ class Repository
|
||||
|
||||
$repository = $baseEntity::repository();
|
||||
|
||||
foreach ($baseEntityResolver->fieldList(Common\EntityResolver::KEY_COLUMN_NAME, true) as $key => $field) {
|
||||
if (null === $baseEntityResolver->searchFieldAnnotation($field['name'], new RelationIgnore)) {
|
||||
$repository->select($baseEntityResolver->entityClass::field($key));
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($where as $condition) {
|
||||
$repository->where($condition->field, is_callable($condition->value) ? call_user_func_array($condition->value, [$this]) : $condition->value, $condition->operator, $condition->condition);
|
||||
}
|
||||
@ -725,22 +729,16 @@ class Repository
|
||||
|
||||
$repository->where($key, $values);
|
||||
|
||||
switch ($relationType) {
|
||||
case 'onetoone':
|
||||
$results = call_user_func([$repository, "loadOne"]);
|
||||
$item->$name = $results ?: new $baseEntity();
|
||||
$results = call_user_func([ $repository, $relation->function() ]);
|
||||
|
||||
break;
|
||||
|
||||
case 'onetomany':
|
||||
$results = call_user_func([$repository, $relation->function]);
|
||||
|
||||
foreach ($collection as $item) {
|
||||
$item->$name = $baseEntity::entityCollection();
|
||||
$item->$name->mergeWith($results->filtersCollection(fn($e) => $e->$property === $item->$entityProperty));
|
||||
}
|
||||
|
||||
break;
|
||||
if ($relation->isOneToOne()) {
|
||||
$item->$name = $results ?: new $baseEntity();
|
||||
}
|
||||
elseif ($relation->isOneToMany()) {
|
||||
foreach ($collection as $item) {
|
||||
$item->$name = $baseEntity::entityCollection();
|
||||
$item->$name->mergeWith($results->filtersCollection(fn($e) => $e->$property === $item->$entityProperty));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,8 +47,13 @@ class RelationBuilder
|
||||
|
||||
return $this->resolveRelation($name) ?: $this->resolveVirtual($name);
|
||||
}
|
||||
elseif ( $relation = $this->resolver->searchFieldAnnotation($name, new Relation() ) ) {
|
||||
return $this->instanciateEmptyObject($name, $relation);
|
||||
else {
|
||||
if ( $relation = $this->resolver->searchFieldAnnotation($name, new Relation() ) ) {
|
||||
return $this->instanciateEmptyObject($name, $relation);
|
||||
}
|
||||
elseif ($virtual = $this->resolveVirtual($name)) {
|
||||
return $virtual;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
Loading…
x
Reference in New Issue
Block a user