2019-08-21 20:13:00 +00:00
< ? php
namespace Ulmus\Common ;
2020-10-16 15:27:54 +00:00
use Ulmus\Ulmus ,
Ulmus\Annotation\Annotation ,
2019-08-21 20:13:00 +00:00
Ulmus\Annotation\Classes\Table ,
2020-01-29 21:11:16 +00:00
Ulmus\Annotation\Property\Field ,
2020-11-27 17:09:15 +00:00
Ulmus\Annotation\Property\Virtual ,
2020-01-29 21:11:16 +00:00
Ulmus\Annotation\Property\Relation ;
2019-08-21 20:13:00 +00:00
class EntityResolver {
const KEY_ENTITY_NAME = 01 ;
2019-12-19 14:54:33 +00:00
2019-08-21 20:13:00 +00:00
const KEY_COLUMN_NAME = 02 ;
2021-03-01 16:26:06 +00:00
const KEY_LC_ENTITY_NAME = 03 ;
2019-08-21 20:13:00 +00:00
public string $entityClass ;
public array $uses ;
public array $class ;
public array $properties ;
public array $methods ;
2019-12-10 20:27:45 +00:00
protected array $fieldList = [];
2019-08-21 20:13:00 +00:00
public function __construct ( string $entityClass )
{
$this -> entityClass = $entityClass ;
list ( $this -> uses , $this -> class , $this -> methods , $this -> properties ) = array_values (
ObjectReflection :: fromClass ( $entityClass ) -> read ()
);
2019-12-10 20:27:45 +00:00
2019-08-21 20:13:00 +00:00
$this -> resolveAnnotations ();
}
2019-12-10 20:27:45 +00:00
public function field ( $name , $fieldKey = self :: KEY_ENTITY_NAME , $throwException = true ) : ? array
2019-08-21 20:13:00 +00:00
{
try {
2020-03-26 20:28:03 +00:00
return $this -> fieldList ( $fieldKey )[ $name ] ? ? null ;
2019-08-21 20:13:00 +00:00
}
catch ( \Throwable $e ) {
2019-12-10 20:27:45 +00:00
if ( $throwException ) {
throw new \InvalidArgumentException ( " Can't find entity field's column named ` $name ` from entity { $this -> entityClass } " );
}
2019-08-21 20:13:00 +00:00
}
2019-12-10 20:27:45 +00:00
return null ;
2019-08-21 20:13:00 +00:00
}
2020-11-27 17:09:15 +00:00
public function fieldList ( $fieldKey = self :: KEY_ENTITY_NAME , bool $skipVirtual = false ) : array
2019-08-21 20:13:00 +00:00
{
$fieldList = [];
2019-12-19 14:54:33 +00:00
2019-08-21 20:13:00 +00:00
foreach ( $this -> properties as $item ) {
foreach ( $item [ 'tags' ] ? ? [] as $tag ) {
if ( $tag [ 'object' ] instanceof Field ) {
2020-11-27 17:09:15 +00:00
if ( $skipVirtual && ( $tag [ 'object' ] instanceof Virtual )) {
break ;
}
2019-08-21 20:13:00 +00:00
switch ( $fieldKey ) {
2021-03-01 16:26:06 +00:00
case static :: KEY_LC_ENTITY_NAME :
$key = strtolower ( $item [ 'name' ]);
break ;
2019-08-21 20:13:00 +00:00
case static :: KEY_ENTITY_NAME :
$key = $item [ 'name' ];
break ;
case static :: KEY_COLUMN_NAME :
2020-02-17 13:23:41 +00:00
$key = strtolower ( $tag [ 'object' ] -> name ? ? $item [ 'name' ]);
2019-08-21 20:13:00 +00:00
break ;
default :
throw new \InvalidArgumentException ( " Given `fieldKey` is unknown to the EntityResolver " );
}
2021-03-15 13:42:52 +00:00
if ( $escape ) {
if ( isset ( $tag [ 'object' ] -> name ) ) {
$tag [ 'object' ] -> name = 2 ;
}
if ( isset ( $item [ 'name' ]) ) {
$item [ 'name' ] = 2 ;
}
}
2019-08-21 20:13:00 +00:00
$fieldList [ $key ] = $item ;
2020-02-03 16:13:26 +00:00
2019-08-21 20:13:00 +00:00
break ;
}
}
}
2021-03-15 13:42:52 +00:00
2019-08-21 20:13:00 +00:00
return $fieldList ;
}
2020-01-29 21:11:16 +00:00
public function relation ( string $name ) : ? array
{
try {
if ( null !== ( $this -> properties [ $name ] ? ? null ) ) {
foreach ( $this -> properties [ $name ][ 'tags' ] ? ? [] as $tag ) {
if ( $tag [ 'object' ] instanceof Relation ) {
return $this -> properties [ $name ];
}
}
}
return [];
}
catch ( \Throwable $e ) {
2020-04-09 13:50:09 +00:00
# if ( $throwException) {
2020-01-29 21:11:16 +00:00
throw new \InvalidArgumentException ( " Can't find entity relation's column named ` $name ` from entity { $this -> entityClass } " );
2020-04-09 13:50:09 +00:00
# }
2020-01-29 21:11:16 +00:00
}
return null ;
}
public function searchFieldAnnotation ( string $field , Annotation $annotationType ) : ? Annotation
{
$found = $this -> searchFieldAnnotationList ( $field , $annotationType );
return $found ? $found [ 0 ] : null ;
}
public function searchFieldAnnotationList ( string $field , Annotation $annotationType ) : array
{
$list = [];
if ( null !== ( $this -> properties [ $field ] ? ? null ) ) {
foreach ( $this -> properties [ $field ][ 'tags' ] ? ? [] as $tag ) {
if ( $tag [ 'object' ] instanceof $annotationType ) {
$list [] = $tag [ 'object' ];
}
}
}
return $list ;
}
2020-10-16 15:27:54 +00:00
public function tableName ( $required = false ) : string
2019-08-21 20:13:00 +00:00
{
2020-10-16 15:27:54 +00:00
$table = $this -> tableAnnotation ( $required );
if ( ( $table -> name ? ? " " ) === " " ) {
if ( $required ) {
throw new \ArgumentCountError ( " Your entity { $this -> entityClass } seems to be missing a `name` argument for your @Table() annotation " );
}
2019-08-21 20:13:00 +00:00
}
2020-10-16 15:27:54 +00:00
return $table -> name ? ? " " ;
}
public function tableAnnotation ( $required = false ) : Table
{
if ( null === $table = $this -> getAnnotationFromClassname ( Table :: class ) ) {
if ( $required ) {
throw new \LogicException ( " Your entity { $this -> entityClass } seems to be missing a @Table() annotation " );
}
2019-08-21 20:13:00 +00:00
}
2020-10-16 15:27:54 +00:00
return $table ;
}
2021-03-24 13:48:09 +00:00
public function databaseName () : ? string
2020-10-16 15:27:54 +00:00
{
2021-03-24 13:48:09 +00:00
return $this -> tableAnnotation ( false ) -> database ? ? $this -> databaseAdapter () -> adapter () -> database ? ? null ;
2019-08-21 20:13:00 +00:00
}
2021-03-01 16:26:06 +00:00
public function sqlAdapter () : \Ulmus\ConnectionAdapter
2020-02-13 03:56:53 +00:00
{
if ( null !== $table = $this -> getAnnotationFromClassname ( Table :: class ) ) {
if ( $table -> adapter ? ? null ) {
if ( null === ( $adapter = \Ulmus\Ulmus :: $registeredAdapters [ $table -> adapter ] ? ? null ) ) {
2021-03-01 16:26:06 +00:00
throw new \Exception ( " Requested database adapter ` { $table -> adapter } ` is not registered. " );
2020-02-13 03:56:53 +00:00
}
else {
return $adapter ;
}
}
}
2020-10-16 15:27:54 +00:00
return Ulmus :: $defaultAdapter ;
2020-02-13 03:56:53 +00:00
}
2021-03-01 16:26:06 +00:00
/**
* Alias of sqlAdapter
*/
public function databaseAdapter () : \Ulmus\ConnectionAdapter
{
return $this -> sqlAdapter ();
}
2020-10-16 15:27:54 +00:00
public function schemaName ( bool $required = false ) : ? string
2020-01-29 21:11:16 +00:00
{
if ( null === $table = $this -> getAnnotationFromClassname ( Table :: class ) ) {
throw new \LogicException ( " Your entity { $this -> entityClass } seems to be missing a @Table() annotation " );
}
2020-10-16 15:27:54 +00:00
if ( $required && ( ( $table -> schema ? ? " " ) === " " ) ) {
2020-01-29 21:11:16 +00:00
throw new \ArgumentCountError ( " Your entity { $this -> entityClass } seems to be missing a `schema` argument for your @Table() annotation " );
}
return $table -> schema ? ? null ;
}
2020-02-05 21:19:57 +00:00
public function getPrimaryKeyField () : ? array
2020-02-13 03:56:53 +00:00
{
2020-02-05 21:19:57 +00:00
foreach ( $this -> fieldList () as $key => $value ) {
if ( null !== ( $field = $this -> searchFieldAnnotation ( $key , new Field () ) ) ) {
2020-02-13 03:56:53 +00:00
if ( false !== ( $field -> attributes [ 'primary_key' ] ? ? false ) ) {
2020-02-05 21:19:57 +00:00
return [ $key => $field ];
}
}
}
return null ;
}
public function getCompoundKeyFields () : ? array
2019-08-21 20:13:00 +00:00
{
2020-02-05 21:19:57 +00:00
return null ;
}
public function getUniqueFields () : ? array
{
return null ;
2019-08-21 20:13:00 +00:00
}
/**
* Transform an annotation into it 's object' s counterpart
*/
public function getAnnotationFromClassname ( string $className ) : ? object
{
2019-12-19 14:54:33 +00:00
if ( $name = $this -> uses [ $className ] ? ? false ) {
2019-08-21 20:13:00 +00:00
2019-12-19 14:54:33 +00:00
foreach ( array_reverse ( $this -> class [ 'tags' ]) as $item ) {
2019-08-21 20:13:00 +00:00
if ( $item [ 'tag' ] === $name ) {
return $this -> instanciateAnnotationObject ( $item );
}
foreach ( $this -> properties as $item ) {
2019-12-19 14:54:33 +00:00
foreach ( array_reverse ( $item [ 'tags' ]) as $item ) {
2019-08-21 20:13:00 +00:00
if ( $item [ 'tag' ] === $name ) {
return $this -> instanciateAnnotationObject ( $item );
}
}
}
foreach ( $this -> methods as $item ) {
2019-12-19 14:54:33 +00:00
foreach ( array_reverse ( $item [ 'tags' ]) as $item ) {
2019-08-21 20:13:00 +00:00
if ( $item [ 'tag' ] === $name ) {
return $this -> instanciateAnnotationObject ( $item );
}
}
}
}
throw new \TypeError ( " Annotation ` $className ` could not be found within your object ` { $this -> entityClass } ` " );
}
else {
throw new \InvalidArgumentException ( " Class ` $className ` was not found within { $this -> entityClass } uses statement (or it's children / traits) " );
}
return null ;
}
public function instanciateAnnotationObject ( array $tagDefinition ) : Annotation
{
$arguments = $this -> extractArguments ( $tagDefinition [ 'arguments' ]);
if ( false === $class = array_search ( $tagDefinition [ 'tag' ], $this -> uses ) ) {
throw new \InvalidArgumentException ( " Annotation class ` { $tagDefinition [ 'tag' ] } ` was not found within { $this -> entityClass } uses statement (or it's children / traits) " );
}
$obj = new $class ( ... $arguments [ 'constructor' ]);
foreach ( $arguments [ 'setter' ] as $key => $value ) {
$obj -> $key = $value ;
}
return $obj ;
}
/**
* Extracts arguments from an Annotation definition , easing object ' s declaration .
*/
protected function extractArguments ( array $arguments ) : array
{
$list = [
'setter' => [],
'constructor' => [],
];
ksort ( $arguments );
foreach ( $arguments as $key => $value ) {
$list [ is_int ( $key ) ? 'constructor' : 'setter' ][ $key ] = $value ;
}
return $list ;
}
protected function resolveAnnotations ()
{
foreach ( $this -> class [ 'tags' ] as & $tag ) {
$tag [ 'object' ] = $this -> instanciateAnnotationObject ( $tag );
}
foreach ( $this -> properties as & $property ) {
foreach ( $property [ 'tags' ] as & $tag ){
$tag [ 'object' ] = $this -> instanciateAnnotationObject ( $tag );
}
}
foreach ( $this -> methods as & $method ) {
foreach ( $method [ 'tags' ] as & $tag ){
$tag [ 'object' ] = $this -> instanciateAnnotationObject ( $tag );
}
}
}
}