entityClass = $entityClass; list($this->uses, $this->class, $this->methods, $this->properties) = array_values( ObjectReflection::fromClass($entityClass)->read() ); $this->resolveAnnotations(); } public function field($name, $fieldKey = self::KEY_ENTITY_NAME, $throwException = true) : ? array { try{ return $this->fieldList($fieldKey)[$name] ?? null; } catch(\Throwable $e) { if ( $throwException) { throw new \InvalidArgumentException("Can't find entity field's column named `$name` from entity {$this->entityClass}"); } } return null; } public function fieldList($fieldKey = self::KEY_ENTITY_NAME, bool $skipVirtual = false) : array { $fieldList = []; foreach($this->properties as $item) { foreach($item['tags'] ?? [] as $tag) { if ( $tag['object'] instanceof Field ) { if ( $skipVirtual && ($tag['object'] instanceof Virtual )) { break; } switch($fieldKey) { case static::KEY_LC_ENTITY_NAME: $key = strtolower($item['name']); break; case static::KEY_ENTITY_NAME: $key = $item['name']; break; case static::KEY_COLUMN_NAME: $key = strtolower($tag['object']->name ?? $item['name']); break; default: throw new \InvalidArgumentException("Given `fieldKey` is unknown to the EntityResolver"); } if ($escape) { if ( isset($tag['object']->name) ) { $tag['object']->name = 2; } if ( isset($item['name']) ) { $item['name'] = 2; } } $fieldList[$key] = $item; break; } } } return $fieldList; } 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) { # if ( $throwException) { throw new \InvalidArgumentException("Can't find entity relation's column named `$name` from entity {$this->entityClass}"); # } } 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; } public function tableName($required = false) : string { $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"); } } 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"); } } return $table; } public function databaseName() : ? string { return $this->tableAnnotation(false)->database ?? $this->databaseAdapter()->adapter()->database ?? null; } public function sqlAdapter() : \Ulmus\ConnectionAdapter { if ( null !== $table = $this->getAnnotationFromClassname( Table::class ) ) { if ( $table->adapter ?? null ) { if ( null === ( $adapter = \Ulmus\Ulmus::$registeredAdapters[$table->adapter] ?? null ) ) { throw new \Exception("Requested database adapter `{$table->adapter}` is not registered."); } else { return $adapter; } } } return Ulmus::$defaultAdapter; } /** * Alias of sqlAdapter */ public function databaseAdapter() : \Ulmus\ConnectionAdapter { return $this->sqlAdapter(); } public function schemaName(bool $required = false) : ? string { if ( null === $table = $this->getAnnotationFromClassname( Table::class ) ) { throw new \LogicException("Your entity {$this->entityClass} seems to be missing a @Table() annotation"); } if ( $required && ( ( $table->schema ?? "" ) === "" ) ) { throw new \ArgumentCountError("Your entity {$this->entityClass} seems to be missing a `schema` argument for your @Table() annotation"); } return $table->schema ?? null; } public function getPrimaryKeyField() : ? array { foreach($this->fieldList() as $key => $value) { if ( null !== ( $field = $this->searchFieldAnnotation($key, new Field() ) ) ) { if ( false !== ( $field->attributes['primary_key'] ?? false ) ) { return [ $key => $field ]; } } } return null; } public function getCompoundKeyFields() : ? array { return null; } public function getUniqueFields() : ? array { return null; } /** * Transform an annotation into it's object's counterpart */ public function getAnnotationFromClassname(string $className) : ? object { 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); } } } } 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); } } } }