From 4f90d69b6e36e86f0abfe9af49b287f2c01dbbd2 Mon Sep 17 00:00:00 2001 From: Bastian Lederer Date: Tue, 9 Dec 2025 08:45:16 +0100 Subject: [PATCH 1/6] Add non-breaking strict type declarations Add strict type declarations to properties, function/method signatures, where types are unambiguous and no inheritance or interface contract is affected. --- src/AliasedExpression.php | 2 +- src/Behavior/Binary.php | 2 +- src/Behavior/BoolCast.php | 14 ++-- src/Behaviors.php | 48 ++++++----- src/ColumnDefinition.php | 4 +- src/Common/PropertiesWithDefaults.php | 2 +- src/Common/SortUtil.php | 14 ++-- src/Compat/FilterProcessor.php | 21 +++-- src/Contract/PersistBehavior.php | 2 +- src/Contract/PropertyBehavior.php | 10 +-- src/Contract/RetrieveBehavior.php | 2 +- src/Defaults.php | 4 +- src/Exception/InvalidColumnException.php | 12 +-- src/Exception/InvalidRelationException.php | 16 ++-- src/Hydrator.php | 18 ++-- src/Query.php | 98 +++++++++++----------- src/Relation.php | 66 ++++++--------- src/Relation/BelongsTo.php | 2 +- src/Relation/BelongsToMany.php | 36 ++++---- src/Relation/BelongsToOne.php | 2 +- src/Relation/HasMany.php | 2 +- src/Relations.php | 24 +++--- src/ResolvedExpression.php | 6 +- src/Resolver.php | 82 ++++++++---------- src/UnionQuery.php | 8 +- tests/RelationTest.php | 8 -- 26 files changed, 243 insertions(+), 262 deletions(-) diff --git a/src/AliasedExpression.php b/src/AliasedExpression.php index dca03bc7..4a14e2e9 100644 --- a/src/AliasedExpression.php +++ b/src/AliasedExpression.php @@ -7,7 +7,7 @@ class AliasedExpression extends Expression { /** @var string */ - protected $alias; + protected string $alias; /** * Create a new database expression diff --git a/src/Behavior/Binary.php b/src/Behavior/Binary.php index 43e3e111..78ffbaf8 100644 --- a/src/Behavior/Binary.php +++ b/src/Behavior/Binary.php @@ -19,7 +19,7 @@ class Binary extends PropertyBehavior implements QueryAwareBehavior, RewriteFilterBehavior { /** @var bool Whether the query is using a pgsql adapter */ - protected $isPostgres = true; + protected bool $isPostgres = true; public function fromDb($value, $key, $_) { diff --git a/src/Behavior/BoolCast.php b/src/Behavior/BoolCast.php index 3163ff9f..46a6c1f2 100644 --- a/src/Behavior/BoolCast.php +++ b/src/Behavior/BoolCast.php @@ -19,20 +19,20 @@ class BoolCast extends PropertyBehavior { /** @var mixed Database value for boolean `false` */ - protected $falseValue = 'n'; + protected mixed $falseValue = 'n'; /** @var mixed Database value for boolean `true` */ - protected $trueValue = 'y'; + protected mixed $trueValue = 'y'; /** @var bool Whether to throw an exception if the value is not equal to the value for false or true */ - protected $strict = true; + protected bool $strict = true; /** * Get the database value representing boolean `false` * * @return mixed */ - public function getFalseValue() + public function getFalseValue(): mixed { return $this->falseValue; } @@ -44,7 +44,7 @@ public function getFalseValue() * * @return $this */ - public function setFalseValue($falseValue): self + public function setFalseValue(mixed $falseValue): self { $this->falseValue = $falseValue; @@ -56,7 +56,7 @@ public function setFalseValue($falseValue): self * * @return mixed */ - public function getTrueValue() + public function getTrueValue(): mixed { return $this->trueValue; } @@ -68,7 +68,7 @@ public function getTrueValue() * * @return $this */ - public function setTrueValue($trueValue): self + public function setTrueValue(mixed $trueValue): self { $this->trueValue = $trueValue; diff --git a/src/Behaviors.php b/src/Behaviors.php index 5c543502..d46a83e6 100644 --- a/src/Behaviors.php +++ b/src/Behaviors.php @@ -16,32 +16,34 @@ class Behaviors implements IteratorAggregate { /** @var array Registered behaviors */ - protected $behaviors = []; + protected array $behaviors = []; /** @var RetrieveBehavior[] Registered retrieve behaviors */ - protected $retrieveBehaviors = []; + protected array $retrieveBehaviors = []; /** @var PersistBehavior[] Registered persist behaviors */ - protected $persistBehaviors = []; + protected array $persistBehaviors = []; /** @var PropertyBehavior[] Registered property behaviors */ - protected $propertyBehaviors = []; + protected array $propertyBehaviors = []; /** @var RewriteFilterBehavior[] Registered rewrite filter behaviors */ - protected $rewriteFilterBehaviors = []; + protected array $rewriteFilterBehaviors = []; /** @var RewriteColumnBehavior[] Registered rewrite column behaviors */ - protected $rewriteColumnBehaviors = []; + protected array $rewriteColumnBehaviors = []; /** @var RewritePathBehavior[] Registered rewrite path behaviors */ - protected $rewritePathBehaviors = []; + protected array $rewritePathBehaviors = []; /** * Add a behavior * - * @param PersistBehavior|PropertyBehavior|RetrieveBehavior|RewriteFilterBehavior $behavior + * @param Behavior $behavior + * + * @return void */ - public function add(Behavior $behavior) + public function add(Behavior $behavior): void { $this->behaviors[] = $behavior; @@ -86,8 +88,10 @@ public function getIterator(): Traversable * Apply all retrieve behaviors on the given model * * @param Model $model + * + * @return void */ - public function retrieve(Model $model) + public function retrieve(Model $model): void { foreach ($this->retrieveBehaviors as $behavior) { $behavior->retrieve($model); @@ -98,8 +102,10 @@ public function retrieve(Model $model) * Apply all persist behaviors on the given model * * @param Model $model + * + * @return void */ - public function persist(Model $model) + public function persist(Model $model): void { foreach ($this->persistBehaviors as $behavior) { $behavior->persist($model); @@ -114,7 +120,7 @@ public function persist(Model $model) * * @return mixed */ - public function retrieveProperty($value, $key) + public function retrieveProperty(mixed $value, string $key): mixed { foreach ($this->propertyBehaviors as $behavior) { $value = $behavior->retrieveProperty($value, $key); @@ -131,7 +137,7 @@ public function retrieveProperty($value, $key) * * @return mixed */ - public function persistProperty($value, $key) + public function persistProperty(mixed $value, string $key): mixed { foreach ($this->propertyBehaviors as $behavior) { $value = $behavior->persistProperty($value, $key); @@ -144,11 +150,11 @@ public function persistProperty($value, $key) * Rewrite the given filter condition * * @param Filter\Condition $condition - * @param string $relation Absolute path (with a trailing dot) of the model + * @param ?string $relation Absolute path (with a trailing dot) of the model * * @return Filter\Rule|null */ - public function rewriteCondition(Filter\Condition $condition, $relation = null) + public function rewriteCondition(Filter\Condition $condition, ?string $relation = null): ?Filter\Rule { $filter = null; foreach ($this->rewriteFilterBehaviors as $behavior) { @@ -164,12 +170,12 @@ public function rewriteCondition(Filter\Condition $condition, $relation = null) /** * Rewrite the given relation path * - * @param string $path - * @param string $relation Absolute path of the model + * @param string $path + * @param ?string $relation Absolute path of the model * * @return string|null */ - public function rewritePath($path, $relation = null) + public function rewritePath(string $path, ?string $relation = null): ?string { $newPath = null; foreach ($this->rewritePathBehaviors as $behavior) { @@ -185,12 +191,12 @@ public function rewritePath($path, $relation = null) /** * Rewrite the given column * - * @param string $column - * @param string $relation Absolute path of the model + * @param string $column + * @param ?string $relation Absolute path of the model * * @return mixed */ - public function rewriteColumn($column, $relation = null) + public function rewriteColumn(string $column, ?string $relation = null): mixed { $newColumn = null; foreach ($this->rewriteColumnBehaviors as $behavior) { diff --git a/src/ColumnDefinition.php b/src/ColumnDefinition.php index ddb80621..8e4e5ce6 100644 --- a/src/ColumnDefinition.php +++ b/src/ColumnDefinition.php @@ -8,10 +8,10 @@ class ColumnDefinition { /** @var string The name of the column */ - protected $name; + protected string $name; /** @var ?string The label of the column */ - protected $label; + protected ?string $label = null; /** * Create a new column definition diff --git a/src/Common/PropertiesWithDefaults.php b/src/Common/PropertiesWithDefaults.php index e8d3a84b..6214162e 100644 --- a/src/Common/PropertiesWithDefaults.php +++ b/src/Common/PropertiesWithDefaults.php @@ -11,7 +11,7 @@ trait PropertiesWithDefaults \ipl\Stdlib\Properties::getProperty as private parentGetProperty; } - protected function getProperty($key) + protected function getProperty(string $key): mixed { if (isset($this->properties[$key]) && $this->properties[$key] instanceof Closure) { $this->setProperty($key, $this->properties[$key]($this, $key)); diff --git a/src/Common/SortUtil.php b/src/Common/SortUtil.php index a14ea2bd..e615cae7 100644 --- a/src/Common/SortUtil.php +++ b/src/Common/SortUtil.php @@ -9,11 +9,11 @@ class SortUtil /** * Create the sort column(s) and direction(s) from the given sort spec * - * @param array|string $sort + * @param string|array $sort * * @return array Sort column(s) and direction(s) suitable for {@link OrderByInterface::orderBy()} */ - public static function createOrderBy($sort): array + public static function createOrderBy(string|array $sort): array { $columnsAndDirections = static::explodeSortSpec($sort); $orderBy = []; @@ -30,11 +30,11 @@ public static function createOrderBy($sort): array /** * Explode the given sort spec into its sort parts * - * @param array|string $sort + * @param string|array $sort * * @return array */ - public static function explodeSortSpec($sort) + public static function explodeSortSpec(string|array $sort): array { return Str::trimSplit(implode(',', (array) $sort)); } @@ -42,11 +42,11 @@ public static function explodeSortSpec($sort) /** * Normalize the given sort spec to a sort string * - * @param array|string $sort + * @param string|array $sort * * @return string */ - public static function normalizeSortSpec($sort) + public static function normalizeSortSpec(string|array $sort): string { return implode(',', static::explodeSortSpec($sort)); } @@ -58,7 +58,7 @@ public static function normalizeSortSpec($sort) * * @return array */ - public static function splitColumnAndDirection($sort) + public static function splitColumnAndDirection(string $sort): array { return Str::symmetricSplit($sort, ' ', 2); } diff --git a/src/Compat/FilterProcessor.php b/src/Compat/FilterProcessor.php index 1b3aab93..222c533c 100644 --- a/src/Compat/FilterProcessor.php +++ b/src/Compat/FilterProcessor.php @@ -20,9 +20,9 @@ class FilterProcessor extends \ipl\Sql\Compat\FilterProcessor { - protected $baseJoins = []; + protected array $baseJoins = []; - protected $madeJoins = []; + protected array $madeJoins = []; /** * Require and resolve the filter rule and apply it on the query @@ -33,8 +33,10 @@ class FilterProcessor extends \ipl\Sql\Compat\FilterProcessor * * @param Filter\Rule $filter * @param Query $query + * + * @return void */ - public static function apply(Filter\Rule $filter, Query $query) + public static function apply(Filter\Rule $filter, Query $query): void { if ($query instanceof UnionQuery) { foreach ($query->getUnions() as $union) { @@ -71,7 +73,7 @@ public static function apply(Filter\Rule $filter, Query $query) * * @return void */ - public static function resolveFilter(Filter\Chain $filter, Query $query) + public static function resolveFilter(Filter\Chain $filter, Query $query): void { $processor = new static(); foreach ($query->getUtilize() as $path => $_) { @@ -81,8 +83,11 @@ public static function resolveFilter(Filter\Chain $filter, Query $query) $processor->requireAndResolveFilterColumns($filter, $query); } - protected function requireAndResolveFilterColumns(Filter\Rule $filter, Query $query, $forceOptimization = null) - { + protected function requireAndResolveFilterColumns( + Filter\Rule $filter, + Query $query, + ?bool $forceOptimization = null + ): MetaDataProvider|Filter\Rule|null { if ($filter instanceof Filter\Condition) { if ( $filter instanceof Exists @@ -90,7 +95,7 @@ protected function requireAndResolveFilterColumns(Filter\Rule $filter, Query $qu || $filter instanceof In || $filter instanceof NotIn ) { - return; + return null; } $resolver = $query->getResolver(); @@ -387,5 +392,7 @@ function ($relationFilter) use ($rule) { $filter->remove($rule); } } + + return null; } } diff --git a/src/Contract/PersistBehavior.php b/src/Contract/PersistBehavior.php index a6db05dd..50a69528 100644 --- a/src/Contract/PersistBehavior.php +++ b/src/Contract/PersistBehavior.php @@ -14,5 +14,5 @@ interface PersistBehavior extends Behavior * * @param Model $model */ - public function persist(Model $model); + public function persist(Model $model): void; } diff --git a/src/Contract/PropertyBehavior.php b/src/Contract/PropertyBehavior.php index c828458a..6242c205 100644 --- a/src/Contract/PropertyBehavior.php +++ b/src/Contract/PropertyBehavior.php @@ -8,7 +8,7 @@ abstract class PropertyBehavior implements RetrieveBehavior, PersistBehavior { /** @var array Property names of which the value should be processed */ - protected $properties; + protected array $properties; /** * PropertyBehavior constructor @@ -24,7 +24,7 @@ public function __construct(array $properties) } } - public function retrieve(Model $model) + public function retrieve(Model $model): void { foreach ($this->properties as $key => $ctx) { if ($model->hasProperty($key)) { @@ -33,7 +33,7 @@ public function retrieve(Model $model) } } - public function persist(Model $model) + public function persist(Model $model): void { foreach ($this->properties as $key => $ctx) { try { @@ -52,7 +52,7 @@ public function persist(Model $model) * * @return mixed */ - public function retrieveProperty($value, $key) + public function retrieveProperty(mixed $value, string $key): mixed { if (! isset($this->properties[$key])) { return $value; @@ -69,7 +69,7 @@ public function retrieveProperty($value, $key) * * @return mixed */ - public function persistProperty($value, $key) + public function persistProperty(mixed $value, string $key): mixed { if (! isset($this->properties[$key])) { return $value; diff --git a/src/Contract/RetrieveBehavior.php b/src/Contract/RetrieveBehavior.php index 884d0749..be3c037a 100644 --- a/src/Contract/RetrieveBehavior.php +++ b/src/Contract/RetrieveBehavior.php @@ -14,5 +14,5 @@ interface RetrieveBehavior extends Behavior * * @param Model $model */ - public function retrieve(Model $model); + public function retrieve(Model $model): void; } diff --git a/src/Defaults.php b/src/Defaults.php index aa2d517e..08172547 100644 --- a/src/Defaults.php +++ b/src/Defaults.php @@ -8,7 +8,7 @@ class Defaults implements IteratorAggregate { /** @var array Registered defaults */ - protected $defaults = []; + protected array $defaults = []; /** * Iterate over the defaults @@ -31,7 +31,7 @@ public function getIterator(): Traversable * * @return $this */ - public function add(string $property, $default): self + public function add(string $property, mixed $default): self { $this->defaults[$property] = $default; diff --git a/src/Exception/InvalidColumnException.php b/src/Exception/InvalidColumnException.php index cd320c6d..8fb93443 100644 --- a/src/Exception/InvalidColumnException.php +++ b/src/Exception/InvalidColumnException.php @@ -8,10 +8,10 @@ class InvalidColumnException extends Exception { /** @var string The column name */ - protected $column; + protected string $column; /** @var Model The target model */ - protected $model; + protected Model $model; /** * Create a new InvalidColumnException @@ -19,9 +19,9 @@ class InvalidColumnException extends Exception * @param string $column The column name * @param Model $model The target model */ - public function __construct($column, Model $model) + public function __construct(string $column, Model $model) { - $this->column = (string) $column; + $this->column = $column; $this->model = $model; parent::__construct(sprintf( @@ -36,7 +36,7 @@ public function __construct($column, Model $model) * * @return string */ - public function getColumn() + public function getColumn(): string { return $this->column; } @@ -46,7 +46,7 @@ public function getColumn() * * @return Model */ - public function getModel() + public function getModel(): Model { return $this->model; } diff --git a/src/Exception/InvalidRelationException.php b/src/Exception/InvalidRelationException.php index 215b7041..6cfe1219 100644 --- a/src/Exception/InvalidRelationException.php +++ b/src/Exception/InvalidRelationException.php @@ -8,10 +8,10 @@ class InvalidRelationException extends Exception { /** @var string The relation name */ - protected $relation; + protected string $relation; - /** @var Model The target model */ - protected $model; + /** @var ?Model The target model */ + protected ?Model $model = null; /** * Create a new InvalidRelationException @@ -19,9 +19,9 @@ class InvalidRelationException extends Exception * @param string $relation The relation name * @param ?Model $model The target model */ - public function __construct($relation, ?Model $model = null) + public function __construct(string $relation, ?Model $model = null) { - $this->relation = (string) $relation; + $this->relation = $relation; $this->model = $model; parent::__construct(sprintf( @@ -36,7 +36,7 @@ public function __construct($relation, ?Model $model = null) * * @return string */ - public function getRelation() + public function getRelation(): string { return $this->relation; } @@ -44,9 +44,9 @@ public function getRelation() /** * Get the target model * - * @return Model + * @return ?Model */ - public function getModel() + public function getModel(): ?Model { return $this->model; } diff --git a/src/Hydrator.php b/src/Hydrator.php index 63b3ff2e..f02026c2 100644 --- a/src/Hydrator.php +++ b/src/Hydrator.php @@ -11,13 +11,13 @@ class Hydrator { /** @var array Additional hydration rules for the model's relations */ - protected $hydrators = []; + protected array $hydrators = []; /** @var array> Map of columns to referencing paths */ - protected $columnToTargetMap = []; + protected array $columnToTargetMap = []; /** @var Query The query the hydration rules are for */ - protected $query; + protected Query $query; /** * Create a new Hydrator @@ -38,7 +38,7 @@ public function __construct(Query $query) * * @throws \InvalidArgumentException If a hydrator for the given path already exists */ - public function add($path) + public function add(string $path): static { if (isset($this->hydrators[$path])) { throw new \InvalidArgumentException("Hydrator for path '$path' already exists"); @@ -106,7 +106,7 @@ protected function updateColumnToTargetMap(string $path, array $columnToProperty * * @return Model */ - public function hydrate(array $data, Model $model) + public function hydrate(array $data, Model $model): Model { $defaultsToApply = []; $columnToTargetMap = $this->columnToTargetMap; @@ -221,8 +221,12 @@ public function hydrate(array $data, Model $model) * * @return array */ - protected function extractAndMap(array &$data, array $columnToPropertyMap, string $path, array &$columnToTargetMap) - { + protected function extractAndMap( + array &$data, + array $columnToPropertyMap, + string $path, + array &$columnToTargetMap + ): array { $extracted = []; foreach (array_intersect_key($columnToPropertyMap, $data) as $column => $property) { $extracted[$property] = $data[$column]; diff --git a/src/Query.php b/src/Query.php index 76cd8d3c..fa37ad74 100644 --- a/src/Query.php +++ b/src/Query.php @@ -47,44 +47,44 @@ class Query implements Filterable, LimitOffsetInterface, OrderByInterface, Pagin */ public const ON_SELECT_ASSEMBLED = 'selectAssembled'; - /** @var int Count cache */ - protected $count; + /** @var ?int Count cache */ + protected ?int $count = null; /** @var Connection Database connection */ protected $db; /** @var string Class to return results as */ - protected $resultSetClass = ResultSet::class; + protected string $resultSetClass = ResultSet::class; /** @var Model Model to query */ protected $model; /** @var array Columns to select from the model (or its relations). If empty, all columns are selected */ - protected $columns = []; + protected array $columns = []; /** @var array Additional columns to select from the model (or its relations) */ - protected $withColumns = []; + protected array $withColumns = []; /** @var array Columns not to select from the model (or its relations) */ - protected $withoutColumns = []; + protected array $withoutColumns = []; /** @var bool Whether to peek ahead for more results */ - protected $peekAhead = false; + protected bool $peekAhead = false; - /** @var Resolver Column and relation resolver */ - protected $resolver; + /** @var ?Resolver Column and relation resolver */ + protected ?Resolver $resolver = null; - /** @var Select Base SELECT query */ - protected $selectBase; + /** @var ?Select Base SELECT query */ + protected ?Select $selectBase = null; /** @var Relation[] Relations to eager load */ - protected $with = []; + protected array $with = []; /** @var Relation[] Relations to utilize (join) */ - protected $utilize = []; + protected array $utilize = []; /** @var bool Whether to disable the default sorts of the model */ - protected $disableDefaultSort = false; + protected bool $disableDefaultSort = false; /** * Get the database connection @@ -103,7 +103,7 @@ public function getDb() * * @return $this */ - public function setDb(Connection $db) + public function setDb(Connection $db): static { $this->db = $db; @@ -115,7 +115,7 @@ public function setDb(Connection $db) * * @return string */ - public function getResultSetClass() + public function getResultSetClass(): string { return $this->resultSetClass; } @@ -129,12 +129,8 @@ public function getResultSetClass() * * @throws InvalidArgumentException If class is not an instance of {@link ResultSet} */ - public function setResultSetClass($class) + public function setResultSetClass(string $class): static { - if (! is_string($class)) { - throw new InvalidArgumentException('Argument $class must be a string'); - } - if (! (new ReflectionClass($class))->newInstanceWithoutConstructor() instanceof ResultSet) { throw new InvalidArgumentException( $class . ' must be an instance of ' . ResultSet::class @@ -159,11 +155,11 @@ public function getModel() /** * Set the model to query * - * @param $model + * @param Model $model * * @return $this */ - public function setModel(Model $model) + public function setModel(Model $model): static { $this->model = $model; $this->getResolver()->setAlias($model, $model->getTableAlias()); @@ -176,7 +172,7 @@ public function setModel(Model $model) * * @return array */ - public function getColumns() + public function getColumns(): array { return $this->columns; } @@ -188,7 +184,7 @@ public function getColumns() * * @return $this */ - public function setFilter(Filter\Chain $filter) + public function setFilter(Filter\Chain $filter): static { $this->filter = $filter; @@ -204,9 +200,9 @@ public function setFilter(Filter\Chain $filter) * * @return $this */ - public function disableDefaultSort($disable = true) + public function disableDefaultSort(bool $disable = true): static { - $this->disableDefaultSort = (bool) $disable; + $this->disableDefaultSort = $disable; return $this; } @@ -216,7 +212,7 @@ public function disableDefaultSort($disable = true) * * @return bool */ - public function defaultSortDisabled() + public function defaultSortDisabled(): bool { return $this->disableDefaultSort; } @@ -236,7 +232,7 @@ public function defaultSortDisabled() * * @return $this */ - public function columns($columns) + public function columns(string|array $columns): static { $this->columns = (array) $columns; $this->withColumns = []; @@ -255,7 +251,7 @@ public function columns($columns) * * @return $this */ - public function withColumns($columns) + public function withColumns(string|array $columns): static { $tableName = $this->getModel()->getTableAlias(); @@ -283,7 +279,7 @@ public function withColumns($columns) * * @return $this */ - public function withoutColumns($columns): self + public function withoutColumns(string|array $columns): self { $tableName = $this->getModel()->getTableAlias(); @@ -304,7 +300,7 @@ public function withoutColumns($columns): self * * @return Resolver */ - public function getResolver() + public function getResolver(): Resolver { if ($this->resolver === null) { $this->resolver = new Resolver($this); @@ -318,7 +314,7 @@ public function getResolver() * * @return Select */ - public function getSelectBase() + public function getSelectBase(): Select { if ($this->selectBase === null) { $this->selectBase = new Select(); @@ -336,7 +332,7 @@ public function getSelectBase() * * @return Relation[] */ - public function getWith() + public function getWith(): array { return $this->with; } @@ -348,7 +344,7 @@ public function getWith() * * @return $this */ - public function with($relations) + public function with(string|array $relations): static { $tableName = $this->getModel()->getTableAlias(); foreach ((array) $relations as $relation) { @@ -366,7 +362,7 @@ public function with($relations) * * @return $this */ - public function without($relations) + public function without(string|array $relations): static { $tableName = $this->getModel()->getTableAlias(); foreach ((array) $relations as $relation) { @@ -382,7 +378,7 @@ public function without($relations) * * @return Relation[] */ - public function getUtilize() + public function getUtilize(): array { return $this->utilize; } @@ -394,7 +390,7 @@ public function getUtilize() * * @return $this */ - public function utilize($path) + public function utilize(string $path): static { $path = $this->getResolver()->qualifyPath($path, $this->getModel()->getTableAlias()); $this->utilize[$path] = $this->getResolver()->resolveRelation($path); @@ -409,7 +405,7 @@ public function utilize($path) * * @return $this */ - public function omit($path) + public function omit(string $path): static { $path = $this->getResolver()->qualifyPath($path, $this->getModel()->getTableAlias()); unset($this->utilize[$path]); @@ -422,7 +418,7 @@ public function omit($path) * * @return Select */ - public function assembleSelect() + public function assembleSelect(): Select { $columns = $this->getColumns(); $model = $this->getModel(); @@ -560,7 +556,7 @@ public function assembleSelect() * * @return Hydrator */ - public function createHydrator() + public function createHydrator(): Hydrator { $hydrator = new Hydrator($this); @@ -582,7 +578,7 @@ public function createHydrator() * * @throws InvalidArgumentException If the relation with the given name does not exist */ - public function derive($relation, Model $source) + public function derive($relation, Model $source): static { // TODO: Think of a way to merge derive() and createSubQuery() return $this->createSubQuery( @@ -602,7 +598,7 @@ public function derive($relation, Model $source) * * @return static */ - public function createSubQuery(Model $target, $targetPath, ?Model $from = null, bool $link = true) + public function createSubQuery(Model $target, string $targetPath, ?Model $from = null, bool $link = true): static { $subQuery = (new static()) ->setDb($this->getDb()) @@ -654,7 +650,7 @@ public function createSubQuery(Model $target, $targetPath, ?Model $from = null, * * @return array */ - public function dump() + public function dump(): array { return $this->getDb()->getQueryBuilder()->assembleSelect($this->assembleSelect()); } @@ -664,7 +660,7 @@ public function dump() * * @return ResultSet */ - public function execute() + public function execute(): ResultSet { $class = $this->getResultSetClass(); /** @var ResultSet $class Just for type hinting. $class is of course a string */ @@ -677,7 +673,7 @@ public function execute() * * @return Model|null Null in case there's no result */ - public function first() + public function first(): ?Model { return $this->execute()->current(); } @@ -692,9 +688,9 @@ public function first() * * @return $this */ - public function peekAhead($peekAhead = true) + public function peekAhead(bool $peekAhead = true): static { - $this->peekAhead = (bool) $peekAhead; + $this->peekAhead = $peekAhead; return $this; } @@ -704,7 +700,7 @@ public function peekAhead($peekAhead = true) * * @return \Generator */ - public function yieldResults() + public function yieldResults(): Generator { $select = $this->assembleSelect(); $stmt = $this->getDb()->select($select); @@ -739,7 +735,7 @@ public function getIterator(): Traversable * * @return SplObjectStorage */ - protected function groupColumnsByTarget(Generator $columns) + protected function groupColumnsByTarget(Generator $columns): SplObjectStorage { $columnStorage = new SplObjectStorage(); @@ -768,7 +764,7 @@ protected function groupColumnsByTarget(Generator $columns) * * @return $this */ - protected function order(Select $select) + protected function order(Select $select): static { $orderBy = $this->getOrderBy(); $defaultSort = []; diff --git a/src/Relation.php b/src/Relation.php index 19593636..60ddfb88 100644 --- a/src/Relation.php +++ b/src/Relation.php @@ -2,8 +2,6 @@ namespace ipl\Orm; -use function ipl\Stdlib\get_php_type; - /** * Relations represent the connection between models, i.e. the association between rows in one or more tables * on the basis of matching key columns. The relationships are defined using candidate key-foreign key constructs. @@ -16,11 +14,11 @@ class Relation /** @var Model Source model */ protected $source; - /** @var string|array Column name(s) of the foreign key found in the target table */ - protected $foreignKey; + /** @var string|array|null Column name(s) of the foreign key found in the target table */ + protected string|array|null $foreignKey = null; - /** @var string|array Column name(s) of the candidate key in the source table which references the foreign key */ - protected $candidateKey; + /** @var string|array|null Column name(s) of the candidate key in the source table which references the foreign key */ + protected string|array|null $candidateKey = null; /** @var string Target model class */ protected $targetClass; @@ -29,13 +27,13 @@ class Relation protected $target; /** @var string Type of the JOIN used in the query */ - protected $joinType = 'INNER'; + protected string $joinType = 'INNER'; /** @var bool Whether this is the inverse of a relationship */ - protected $inverse; + protected bool $inverse = false; /** @var bool Whether this is a to-one relationship */ - protected $isOne = true; + protected bool $isOne = true; /** * Get the default column name(s) in the source table used to match the foreign key @@ -46,7 +44,7 @@ class Relation * * @return array */ - public static function getDefaultCandidateKey(Model $source) + public static function getDefaultCandidateKey(Model $source): array { return (array) $source->getKeyName(); } @@ -60,7 +58,7 @@ public static function getDefaultCandidateKey(Model $source) * * @return array */ - public static function getDefaultForeignKey(Model $source) + public static function getDefaultForeignKey(Model $source): array { $tableName = $source->getTableName(); @@ -77,7 +75,7 @@ function ($key) use ($tableName) { * * @return bool */ - public function isOne() + public function isOne(): bool { return $this->isOne; } @@ -99,7 +97,7 @@ public function getName() * * @return $this */ - public function setName($name) + public function setName(string $name): static { $this->name = $name; @@ -123,7 +121,7 @@ public function getSource() * * @return $this */ - public function setSource(Model $source) + public function setSource(Model $source): static { $this->source = $source; @@ -133,9 +131,9 @@ public function setSource(Model $source) /** * Get the column name(s) of the foreign key found in the target table * - * @return string|array Array if the foreign key is compound, string otherwise + * @return string|array|null Array if the foreign key is compound, string otherwise */ - public function getForeignKey() + public function getForeignKey(): string|array|null { return $this->foreignKey; } @@ -143,11 +141,11 @@ public function getForeignKey() /** * Set the column name(s) of the foreign key found in the target table * - * @param string|array $foreignKey Array if the foreign key is compound, string otherwise + * @param string|array|null $foreignKey Array if the foreign key is compound, string otherwise * * @return $this */ - public function setForeignKey($foreignKey) + public function setForeignKey(string|array|null $foreignKey): static { $this->foreignKey = $foreignKey; @@ -157,9 +155,9 @@ public function setForeignKey($foreignKey) /** * Get the column name(s) of the candidate key in the source table which references the foreign key * - * @return string|array Array if the candidate key is compound, string otherwise + * @return string|array|null Array if the candidate key is compound, string otherwise */ - public function getCandidateKey() + public function getCandidateKey(): string|array|null { return $this->candidateKey; } @@ -167,11 +165,11 @@ public function getCandidateKey() /** * Set the column name(s) of the candidate key in the source table which references the foreign key * - * @param string|array $candidateKey Array if the candidate key is compound, string otherwise + * @param string|array|null $candidateKey Array if the candidate key is compound, string otherwise * * @return $this */ - public function setCandidateKey($candidateKey) + public function setCandidateKey(string|array|null $candidateKey): static { $this->candidateKey = $candidateKey; @@ -194,21 +192,9 @@ public function getTargetClass() * @param string $targetClass * * @return $this - * - * @throws \InvalidArgumentException If the target model class is not of type string */ - public function setTargetClass($targetClass) + public function setTargetClass(string $targetClass): static { - if (! is_string($targetClass)) { - // Require a class name here instead of a concrete model in oder to prevent circular references when - // constructing relations - throw new \InvalidArgumentException(sprintf( - '%s() expects parameter 1 to be string, %s given', - __METHOD__, - get_php_type($targetClass) - )); - } - $this->targetClass = $targetClass; return $this; @@ -239,7 +225,7 @@ public function getTarget() * * @return $this */ - public function setTarget(Model $target) + public function setTarget(Model $target): static { $this->target = $target; @@ -251,7 +237,7 @@ public function setTarget(Model $target) * * @return string */ - public function getJoinType() + public function getJoinType(): string { return $this->joinType; } @@ -263,7 +249,7 @@ public function getJoinType() * * @return Relation */ - public function setJoinType($joinType) + public function setJoinType(string $joinType): static { $this->joinType = $joinType; @@ -280,7 +266,7 @@ public function setJoinType($joinType) * @throws \UnexpectedValueException If there's no candidate key to be found * or the foreign key count does not match the candidate key count */ - public function determineKeys(Model $source) + public function determineKeys(Model $source): array { $candidateKey = (array) $this->getCandidateKey(); @@ -327,7 +313,7 @@ public function determineKeys(Model $source) * * @return \Generator */ - public function resolve() + public function resolve(): \Generator { $source = $this->getSource(); diff --git a/src/Relation/BelongsTo.php b/src/Relation/BelongsTo.php index 1edcff3e..1982f10b 100644 --- a/src/Relation/BelongsTo.php +++ b/src/Relation/BelongsTo.php @@ -9,5 +9,5 @@ */ class BelongsTo extends Relation { - protected $inverse = true; + protected bool $inverse = true; } diff --git a/src/Relation/BelongsToMany.php b/src/Relation/BelongsToMany.php index fa692450..4106949a 100644 --- a/src/Relation/BelongsToMany.php +++ b/src/Relation/BelongsToMany.php @@ -17,22 +17,22 @@ class BelongsToMany extends Relation /** @var string Relation class */ protected const RELATION_CLASS = HasMany::class; - protected $isOne = false; + protected bool $isOne = false; - /** @var string Name of the join table or junction model class */ - protected $throughClass; + /** @var ?string Name of the join table or junction model class */ + protected ?string $throughClass = null; /** @var ?string Alias for the join table or junction model class */ protected ?string $throughAlias = null; - /** @var Model The junction model */ - protected $through; + /** @var ?Model The junction model */ + protected ?Model $through = null; - /** @var string|array Column name(s) of the target model's foreign key found in the join table */ - protected $targetForeignKey; + /** @var string|array|null Column name(s) of the target model's foreign key found in the join table */ + protected string|array|null $targetForeignKey = null; - /** @var string|array Candidate key column name(s) in the target table which references the target foreign key */ - protected $targetCandidateKey; + /** @var string|array|null Candidate key column name(s) in the target table which references the target foreign key */ + protected string|array|null $targetCandidateKey = null; /** * Get the name of the join table or junction model class @@ -85,7 +85,7 @@ public function setThroughAlias(string $throughAlias): self /** * Get the junction model * - * @return Model|Junction + * @return Model */ public function getThrough(): Model { @@ -126,9 +126,9 @@ public function setThrough(Model $through): self /** * Get the column name(s) of the target model's foreign key found in the join table * - * @return string|array Array if the foreign key is compound, string otherwise + * @return string|array|null Array if the foreign key is compound, string otherwise */ - public function getTargetForeignKey() + public function getTargetForeignKey(): string|array|null { return $this->targetForeignKey; } @@ -140,7 +140,7 @@ public function getTargetForeignKey() * * @return $this */ - public function setTargetForeignKey($targetForeignKey): self + public function setTargetForeignKey(string|array $targetForeignKey): self { $this->targetForeignKey = $targetForeignKey; @@ -150,9 +150,9 @@ public function setTargetForeignKey($targetForeignKey): self /** * Get the candidate key column name(s) in the target table which references the target foreign key * - * @return string|array Array if the foreign key is compound, string otherwise + * @return string|array|null Array if the foreign key is compound, string otherwise */ - public function getTargetCandidateKey() + public function getTargetCandidateKey(): string|array|null { return $this->targetCandidateKey; } @@ -164,14 +164,14 @@ public function getTargetCandidateKey() * * @return $this */ - public function setTargetCandidateKey($targetCandidateKey): self + public function setTargetCandidateKey(string|array $targetCandidateKey): self { $this->targetCandidateKey = $targetCandidateKey; return $this; } - public function resolve() + public function resolve(): \Generator { $source = $this->getSource(); @@ -229,7 +229,7 @@ public function resolve() } } - protected function extractKey(array $possibleKey) + protected function extractKey(array $possibleKey): string|array|null { $filtered = array_filter($possibleKey); diff --git a/src/Relation/BelongsToOne.php b/src/Relation/BelongsToOne.php index afdfbec9..5272bf3a 100644 --- a/src/Relation/BelongsToOne.php +++ b/src/Relation/BelongsToOne.php @@ -9,5 +9,5 @@ class BelongsToOne extends BelongsToMany { protected const RELATION_CLASS = HasOne::class; - protected $isOne = true; + protected bool $isOne = true; } diff --git a/src/Relation/HasMany.php b/src/Relation/HasMany.php index 3e71e25b..13d1abc4 100644 --- a/src/Relation/HasMany.php +++ b/src/Relation/HasMany.php @@ -9,5 +9,5 @@ */ class HasMany extends Relation { - protected $isOne = false; + protected bool $isOne = false; } diff --git a/src/Relations.php b/src/Relations.php index e19306e3..d5809bf8 100644 --- a/src/Relations.php +++ b/src/Relations.php @@ -20,7 +20,7 @@ class Relations implements IteratorAggregate { /** @var Relation[] */ - protected $relations = []; + protected array $relations = []; /** * Get whether a relation with the given name exists @@ -29,7 +29,7 @@ class Relations implements IteratorAggregate * * @return bool */ - public function has($name) + public function has(string $name): bool { return isset($this->relations[$name]); } @@ -43,7 +43,7 @@ public function has($name) * * @throws \InvalidArgumentException If the relation with the given name does not exist */ - public function get($name) + public function get(string $name): Relation { $this->assertRelationExists($name); @@ -59,7 +59,7 @@ public function get($name) * * @throws \InvalidArgumentException If a relation with the given name already exists */ - public function add(Relation $relation) + public function add(Relation $relation): static { $name = $relation->getName(); @@ -81,7 +81,7 @@ public function add(Relation $relation) * * @throws \InvalidArgumentException If the target model class is not of type string */ - public function create($class, $name, $targetClass) + public function create(string $class, string $name, string $targetClass): Relation { $relation = new $class(); @@ -122,7 +122,7 @@ public function create($class, $name, $targetClass) * * @return HasOne */ - public function hasOne($name, $targetClass) + public function hasOne(string $name, string $targetClass): HasOne { /** @var HasOne $relation */ $relation = $this->create(HasOne::class, $name, $targetClass); @@ -140,7 +140,7 @@ public function hasOne($name, $targetClass) * * @return HasMany */ - public function hasMany($name, $targetClass) + public function hasMany(string $name, string $targetClass): HasMany { /** @var HasMany $relation */ $relation = $this->create(HasMany::class, $name, $targetClass); @@ -158,7 +158,7 @@ public function hasMany($name, $targetClass) * * @return BelongsTo */ - public function belongsTo($name, $targetClass) + public function belongsTo(string $name, string $targetClass): BelongsTo { /** @var BelongsTo $relation */ $relation = $this->create(BelongsTo::class, $name, $targetClass); @@ -194,7 +194,7 @@ public function belongsToOne(string $name, string $targetClass): BelongsToOne * * @return BelongsToMany */ - public function belongsToMany($name, $targetClass) + public function belongsToMany(string $name, string $targetClass): BelongsToMany { /** @var BelongsToMany $relation */ $relation = $this->create(BelongsToMany::class, $name, $targetClass); @@ -213,8 +213,10 @@ public function getIterator(): Traversable * Throw exception if a relation with the given name already exists * * @param string $name + * + * @return void */ - protected function assertRelationDoesNotYetExist($name) + protected function assertRelationDoesNotYetExist(string $name): void { if ($this->has($name)) { throw new \InvalidArgumentException("Relation '$name' already exists"); @@ -226,7 +228,7 @@ protected function assertRelationDoesNotYetExist($name) * * @param string $name */ - protected function assertRelationExists($name) + protected function assertRelationExists(string $name): void { if (! $this->has($name)) { throw new InvalidRelationException($name); diff --git a/src/ResolvedExpression.php b/src/ResolvedExpression.php index 86883daf..c1a23475 100644 --- a/src/ResolvedExpression.php +++ b/src/ResolvedExpression.php @@ -10,7 +10,7 @@ class ResolvedExpression extends Expression { /** @var Generator */ - protected $resolvedColumns; + protected Generator $resolvedColumns; /** * Create a resolved database expression @@ -28,7 +28,7 @@ public function __construct(ExpressionInterface $expr, Generator $resolvedColumn /** * @throws RuntimeException In case the columns are not qualified yet */ - public function getColumns() + public function getColumns(): array { if ($this->resolvedColumns->valid()) { throw new RuntimeException('Columns are not yet qualified'); @@ -42,7 +42,7 @@ public function getColumns() * * @return Generator */ - public function getResolvedColumns() + public function getResolvedColumns(): Generator { return $this->resolvedColumns; } diff --git a/src/Resolver.php b/src/Resolver.php index a055bfe0..dd8f0e30 100644 --- a/src/Resolver.php +++ b/src/Resolver.php @@ -24,34 +24,34 @@ class Resolver { /** @var Query The query to resolve */ - protected $query; + protected Query $query; /** @var SplObjectStorage Model relations */ - protected $relations; + protected SplObjectStorage $relations; /** @var SplObjectStorage Model behaviors */ - protected $behaviors; + protected SplObjectStorage $behaviors; /** @var SplObjectStorage Model defaults */ - protected $defaults; + protected SplObjectStorage $defaults; /** @var SplObjectStorage Model aliases */ - protected $aliases; + protected SplObjectStorage $aliases; - /** @var string The alias prefix to use */ - protected $aliasPrefix; + /** @var ?string The alias prefix to use */ + protected ?string $aliasPrefix = null; /** @var SplObjectStorage Selectable columns from resolved models */ - protected $selectableColumns; + protected SplObjectStorage $selectableColumns; /** @var SplObjectStorage Select columns from resolved models */ - protected $selectColumns; + protected SplObjectStorage $selectColumns; /** @var SplObjectStorage Meta data from models and their direct relations */ - protected $metaData; + protected SplObjectStorage $metaData; /** @var SplObjectStorage Resolved relations */ - protected $resolvedRelations; + protected SplObjectStorage $resolvedRelations; /** * Create a new resolver @@ -79,7 +79,7 @@ public function __construct(Query $query) * * @return Relations */ - public function getRelations(Model $model) + public function getRelations(Model $model): Relations { if (! $this->relations->offsetExists($model)) { $relations = new Relations(); @@ -97,7 +97,7 @@ public function getRelations(Model $model) * * @return Behaviors */ - public function getBehaviors(Model $model) + public function getBehaviors(Model $model): Behaviors { if (! $this->behaviors->offsetExists($model)) { $behaviors = new Behaviors(); @@ -141,7 +141,7 @@ public function getDefaults(Model $model): Defaults * * @throws OutOfBoundsException If no alias exists for the given model */ - public function getAlias(Model $model) + public function getAlias(Model $model): string { if (! $this->aliases->offsetExists($model)) { throw new OutOfBoundsException(sprintf( @@ -161,7 +161,7 @@ public function getAlias(Model $model) * * @return $this */ - public function setAlias(Model $model, $alias) + public function setAlias(Model $model, string $alias): static { $this->aliases[$model] = $alias; @@ -171,9 +171,9 @@ public function setAlias(Model $model, $alias) /** * Get the alias prefix * - * @return string + * @return ?string */ - public function getAliasPrefix() + public function getAliasPrefix(): ?string { return $this->aliasPrefix; } @@ -185,7 +185,7 @@ public function getAliasPrefix() * * @return $this */ - public function setAliasPrefix($alias) + public function setAliasPrefix(string $alias): static { $this->aliasPrefix = $alias; @@ -200,7 +200,7 @@ public function setAliasPrefix($alias) * * @return bool */ - public function hasSelectableColumn(Model $subject, $column) + public function hasSelectableColumn(Model $subject, string $column): bool { if (! $this->selectableColumns->offsetExists($subject)) { $this->collectColumns($subject); @@ -221,7 +221,7 @@ public function hasSelectableColumn(Model $subject, $column) * * @return array */ - public function getSelectableColumns(Model $subject) + public function getSelectableColumns(Model $subject): array { if (! $this->selectableColumns->offsetExists($subject)) { $this->collectColumns($subject); @@ -237,7 +237,7 @@ public function getSelectableColumns(Model $subject) * * @return array Select columns suitable for {@link \ipl\Sql\Select::columns()} */ - public function getSelectColumns(Model $subject) + public function getSelectColumns(Model $subject): array { if (! $this->selectColumns->offsetExists($subject)) { $this->collectColumns($subject); @@ -253,7 +253,7 @@ public function getSelectColumns(Model $subject) * * @return array Column paths as keys (relative to $subject) and their meta data as values */ - public function getColumnDefinitions(Model $subject) + public function getColumnDefinitions(Model $subject): array { if (! $this->metaData->offsetExists($subject)) { $this->metaData->offsetSet($subject, $this->collectMetaData($subject)); @@ -307,7 +307,7 @@ public function getColumnDefinition(string $columnPath): ColumnDefinition * * @return string */ - public function qualifyColumnAlias($alias, $tableName) + public function qualifyColumnAlias(string $alias, string $tableName): string { return $tableName . '_' . $alias; } @@ -320,7 +320,7 @@ public function qualifyColumnAlias($alias, $tableName) * * @return string */ - public function qualifyColumn($column, $tableName) + public function qualifyColumn(string $column, string $tableName): string { return $tableName . '.' . $column; } @@ -333,20 +333,13 @@ public function qualifyColumn($column, $tableName) * * @return array * - * @throws InvalidArgumentException If $columns is not iterable * @throws InvalidArgumentException If $model is not passed and $columns is not a generator */ - public function qualifyColumns($columns, ?Model $model = null) + public function qualifyColumns(iterable $columns, ?Model $model = null): array { $target = $model ?: $this->query->getModel(); $targetAlias = $this->getAlias($target); - if (! is_iterable($columns)) { - throw new InvalidArgumentException( - sprintf('$columns is not iterable, got %s instead', get_php_type($columns)) - ); - } - $qualified = []; foreach ($columns as $alias => $column) { if (is_int($alias) && is_array($column)) { @@ -387,20 +380,13 @@ public function qualifyColumns($columns, ?Model $model = null) * * @return array * - * @throws InvalidArgumentException If $columns is not iterable * @throws InvalidArgumentException If $model is not passed and $columns is not a generator */ - public function qualifyColumnsAndAliases($columns, ?Model $model = null, $autoAlias = true) + public function qualifyColumnsAndAliases(iterable $columns, ?Model $model = null, bool $autoAlias = true): array { $target = $model ?: $this->query->getModel(); $targetAlias = $this->getAlias($target); - if (! is_iterable($columns)) { - throw new InvalidArgumentException( - sprintf('$columns is not iterable, got %s instead', get_php_type($columns)) - ); - } - $qualified = []; foreach ($columns as $alias => $column) { if (is_int($alias) && is_array($column)) { @@ -455,7 +441,7 @@ public function qualifyColumnsAndAliases($columns, ?Model $model = null, $autoAl * * @return string */ - public function qualifyPath($path, $tableName) + public function qualifyPath(string $path, string $tableName): string { $segments = explode('.', $path, 2); @@ -475,7 +461,7 @@ public function qualifyPath($path, $tableName) * * @return bool */ - public function isDistinctRelation($path) + public function isDistinctRelation(string $path): bool { foreach ($this->resolveRelations($path) as $relation) { if (! $relation->isOne()) { @@ -496,7 +482,7 @@ public function isDistinctRelation($path) * * @return Relation */ - public function resolveRelation($path, ?Model $subject = null) + public function resolveRelation(string $path, ?Model $subject = null): Relation { $subject = $subject ?: $this->query->getModel(); if (! $this->resolvedRelations->offsetExists($subject) || ! isset($this->resolvedRelations[$subject][$path])) { @@ -520,7 +506,7 @@ public function resolveRelation($path, ?Model $subject = null) * @throws InvalidArgumentException In case $path is not fully qualified * @throws InvalidRelationException In case a relation is unknown */ - public function resolveRelations($path, ?Model $subject = null) + public function resolveRelations(string $path, ?Model $subject = null): Generator { $relations = explode('.', $path); $subject = $subject ?: $this->query->getModel(); @@ -600,7 +586,7 @@ public function resolveRelations($path, ?Model $subject = null) * * @throws InvalidColumnException If a column does not exist */ - public function requireAndResolveColumns(array $columns, ?Model $model = null) + public function requireAndResolveColumns(array $columns, ?Model $model = null): Generator { $model = $model ?: $this->query->getModel(); $tableName = $model->getTableAlias(); @@ -739,8 +725,10 @@ public function requireAndResolveColumns(array $columns, ?Model $model = null) * Collect all selectable columns from the given model * * @param Model $subject + * + * @return void */ - protected function collectColumns(Model $subject) + protected function collectColumns(Model $subject): void { // Don't fail if Model::getColumns() also contains the primary key columns $columns = array_merge((array) $subject->getKeyName(), (array) $subject->getColumns()); @@ -769,7 +757,7 @@ protected function collectColumns(Model $subject) * * @return array */ - protected function collectMetaData(Model $subject) + protected function collectMetaData(Model $subject): array { $definitions = []; foreach ($subject->getColumnDefinitions() as $name => $data) { diff --git a/src/UnionQuery.php b/src/UnionQuery.php index a6405ce9..5d4204d1 100644 --- a/src/UnionQuery.php +++ b/src/UnionQuery.php @@ -6,15 +6,15 @@ class UnionQuery extends Query { - /** @var Query[] Underlying queries */ - private $unions; + /** @var ?Query[] Underlying queries */ + private ?array $unions = null; /** * Get the underlying queries * * @return Query[] */ - public function getUnions() + public function getUnions(): array { if ($this->unions === null) { $this->unions = []; @@ -35,7 +35,7 @@ public function getUnions() return $this->unions; } - public function getSelectBase() + public function getSelectBase(): Select { if ($this->selectBase === null) { $this->selectBase = new Select(); diff --git a/tests/RelationTest.php b/tests/RelationTest.php index 6a225c11..11486451 100644 --- a/tests/RelationTest.php +++ b/tests/RelationTest.php @@ -2,7 +2,6 @@ namespace ipl\Tests\Orm; -use InvalidArgumentException; use ipl\Orm\Relation; class RelationTest extends \PHPUnit\Framework\TestCase @@ -81,13 +80,6 @@ public function testGetTargetClassReturnsCorrectTargetClassIfSet() $this->assertSame($targetClass, $relation->getTargetClass()); } - public function testSetTargetClassThrowsInvalidArgumentExceptionIfNotString() - { - $this->expectException(InvalidArgumentException::class); - - (new Relation())->setTargetClass(new TestModel()); - } - public function testGetDefaultCandidateKeyReturnsEmptyArrayIfSourceModelsPrimaryKeyIsUnset() { $candidateKey = Relation::getDefaultCandidateKey(new TestModel()); From 5e20d357387bc9472c2d963409b5b68b80d6820c Mon Sep 17 00:00:00 2001 From: Bastian Lederer Date: Tue, 9 Dec 2025 09:06:35 +0100 Subject: [PATCH 2/6] Replace return type self with static --- src/Behavior/BoolCast.php | 6 +++--- src/ColumnDefinition.php | 6 +++--- src/Defaults.php | 2 +- src/Query.php | 2 +- src/Relation/BelongsToMany.php | 10 +++++----- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Behavior/BoolCast.php b/src/Behavior/BoolCast.php index 46a6c1f2..bc65a0e0 100644 --- a/src/Behavior/BoolCast.php +++ b/src/Behavior/BoolCast.php @@ -44,7 +44,7 @@ public function getFalseValue(): mixed * * @return $this */ - public function setFalseValue(mixed $falseValue): self + public function setFalseValue(mixed $falseValue): static { $this->falseValue = $falseValue; @@ -68,7 +68,7 @@ public function getTrueValue(): mixed * * @return $this */ - public function setTrueValue(mixed $trueValue): self + public function setTrueValue(mixed $trueValue): static { $this->trueValue = $trueValue; @@ -92,7 +92,7 @@ public function isStrict(): bool * * @return $this */ - public function setStrict(bool $strict): self + public function setStrict(bool $strict): static { $this->strict = $strict; diff --git a/src/ColumnDefinition.php b/src/ColumnDefinition.php index 8e4e5ce6..5bd3715b 100644 --- a/src/ColumnDefinition.php +++ b/src/ColumnDefinition.php @@ -50,7 +50,7 @@ public function getLabel(): ?string * * @return $this */ - public function setLabel(?string $label): self + public function setLabel(?string $label): static { $this->label = $label; @@ -62,9 +62,9 @@ public function setLabel(?string $label): self * * @param array $options * - * @return self + * @return static */ - public static function fromArray(array $options): self + public static function fromArray(array $options): static { if (! isset($options['name'])) { throw new InvalidArgumentException('$options must provide a name'); diff --git a/src/Defaults.php b/src/Defaults.php index 08172547..15deab08 100644 --- a/src/Defaults.php +++ b/src/Defaults.php @@ -31,7 +31,7 @@ public function getIterator(): Traversable * * @return $this */ - public function add(string $property, mixed $default): self + public function add(string $property, mixed $default): static { $this->defaults[$property] = $default; diff --git a/src/Query.php b/src/Query.php index fa37ad74..99b805a5 100644 --- a/src/Query.php +++ b/src/Query.php @@ -279,7 +279,7 @@ public function withColumns(string|array $columns): static * * @return $this */ - public function withoutColumns(string|array $columns): self + public function withoutColumns(string|array $columns): static { $tableName = $this->getModel()->getTableAlias(); diff --git a/src/Relation/BelongsToMany.php b/src/Relation/BelongsToMany.php index 4106949a..89956500 100644 --- a/src/Relation/BelongsToMany.php +++ b/src/Relation/BelongsToMany.php @@ -51,7 +51,7 @@ public function getThroughClass(): ?string * * @return $this */ - public function through(string $through): self + public function through(string $through): static { $this->throughClass = $through; @@ -75,7 +75,7 @@ public function getThroughAlias(): string * * @return $this */ - public function setThroughAlias(string $throughAlias): self + public function setThroughAlias(string $throughAlias): static { $this->throughAlias = $throughAlias; @@ -116,7 +116,7 @@ public function getThrough(): Model * * @return $this */ - public function setThrough(Model $through): self + public function setThrough(Model $through): static { $this->through = $through; @@ -140,7 +140,7 @@ public function getTargetForeignKey(): string|array|null * * @return $this */ - public function setTargetForeignKey(string|array $targetForeignKey): self + public function setTargetForeignKey(string|array $targetForeignKey): static { $this->targetForeignKey = $targetForeignKey; @@ -164,7 +164,7 @@ public function getTargetCandidateKey(): string|array|null * * @return $this */ - public function setTargetCandidateKey(string|array $targetCandidateKey): self + public function setTargetCandidateKey(string|array $targetCandidateKey): static { $this->targetCandidateKey = $targetCandidateKey; From a04c17f00c4d2d1489ef4e2a7f0ab8b3ac4a4ccf Mon Sep 17 00:00:00 2001 From: Bastian Lederer Date: Thu, 11 Dec 2025 15:23:12 +0100 Subject: [PATCH 3/6] Optimize imports --- src/Behavior/Binary.php | 2 -- src/ColumnDefinition.php | 1 - src/Hydrator.php | 1 - src/Relation/BelongsToMany.php | 2 -- src/Resolver.php | 3 --- tests/ResolverTest.php | 1 - 6 files changed, 10 deletions(-) diff --git a/src/Behavior/Binary.php b/src/Behavior/Binary.php index 78ffbaf8..75fd294b 100644 --- a/src/Behavior/Binary.php +++ b/src/Behavior/Binary.php @@ -11,8 +11,6 @@ use ipl\Stdlib\Filter\Condition; use UnexpectedValueException; -use function ipl\Stdlib\get_php_type; - /** * Support hex filters for binary columns and PHP resource (in) / bytea hex format (out) transformation for PostgreSQL */ diff --git a/src/ColumnDefinition.php b/src/ColumnDefinition.php index 5bd3715b..6278a2ef 100644 --- a/src/ColumnDefinition.php +++ b/src/ColumnDefinition.php @@ -3,7 +3,6 @@ namespace ipl\Orm; use InvalidArgumentException; -use LogicException; class ColumnDefinition { diff --git a/src/Hydrator.php b/src/Hydrator.php index f02026c2..2efd2098 100644 --- a/src/Hydrator.php +++ b/src/Hydrator.php @@ -2,7 +2,6 @@ namespace ipl\Orm; -use InvalidArgumentException; use ipl\Orm\Exception\InvalidRelationException; /** diff --git a/src/Relation/BelongsToMany.php b/src/Relation/BelongsToMany.php index 89956500..e69e5b02 100644 --- a/src/Relation/BelongsToMany.php +++ b/src/Relation/BelongsToMany.php @@ -7,8 +7,6 @@ use ipl\Orm\Relations; use LogicException; -use function ipl\Stdlib\get_php_type; - /** * Many-to-many relationship */ diff --git a/src/Resolver.php b/src/Resolver.php index dd8f0e30..3fa1f53a 100644 --- a/src/Resolver.php +++ b/src/Resolver.php @@ -10,14 +10,11 @@ use ipl\Orm\Exception\InvalidColumnException; use ipl\Orm\Exception\InvalidRelationException; use ipl\Orm\Relation\BelongsToMany; -use ipl\Orm\Relation\BelongsToOne; use ipl\Sql\ExpressionInterface; use LogicException; use OutOfBoundsException; use SplObjectStorage; -use function ipl\Stdlib\get_php_type; - /** * Column and relation resolver. Acts as glue between queries and models */ diff --git a/tests/ResolverTest.php b/tests/ResolverTest.php index c982051b..a7fbad43 100644 --- a/tests/ResolverTest.php +++ b/tests/ResolverTest.php @@ -3,7 +3,6 @@ namespace ipl\Tests\Orm; use ipl\Orm\Query; -use ipl\Orm\Resolver; use ipl\Sql\Expression; use ipl\Sql\QueryBuilder; use PHPUnit\Framework\TestCase; From 92c3a4aece2f1d4fd9593406f40f630f93b70d97 Mon Sep 17 00:00:00 2001 From: Bastian Lederer Date: Fri, 16 Jan 2026 08:06:08 +0100 Subject: [PATCH 4/6] Simplify FQN --- src/Common/PropertiesWithDefaults.php | 5 +++-- src/Hydrator.php | 5 +++-- src/Query.php | 2 +- src/Relation.php | 13 ++++++++----- src/Relation/BelongsToMany.php | 3 ++- src/Relations.php | 13 +++++++------ 6 files changed, 24 insertions(+), 17 deletions(-) diff --git a/src/Common/PropertiesWithDefaults.php b/src/Common/PropertiesWithDefaults.php index 6214162e..3ee43da4 100644 --- a/src/Common/PropertiesWithDefaults.php +++ b/src/Common/PropertiesWithDefaults.php @@ -4,11 +4,12 @@ use Closure; use Traversable; +use ipl\Stdlib\Properties; trait PropertiesWithDefaults { - use \ipl\Stdlib\Properties { - \ipl\Stdlib\Properties::getProperty as private parentGetProperty; + use Properties { + Properties::getProperty as private parentGetProperty; } protected function getProperty(string $key): mixed diff --git a/src/Hydrator.php b/src/Hydrator.php index 2efd2098..33032992 100644 --- a/src/Hydrator.php +++ b/src/Hydrator.php @@ -3,6 +3,7 @@ namespace ipl\Orm; use ipl\Orm\Exception\InvalidRelationException; +use InvalidArgumentException; /** * Hydrates raw database rows into concrete model instances. @@ -35,12 +36,12 @@ public function __construct(Query $query) * * @return $this * - * @throws \InvalidArgumentException If a hydrator for the given path already exists + * @throws InvalidArgumentException If a hydrator for the given path already exists */ public function add(string $path): static { if (isset($this->hydrators[$path])) { - throw new \InvalidArgumentException("Hydrator for path '$path' already exists"); + throw new InvalidArgumentException("Hydrator for path '$path' already exists"); } $resolver = $this->query->getResolver(); diff --git a/src/Query.php b/src/Query.php index 99b805a5..fbbfa9eb 100644 --- a/src/Query.php +++ b/src/Query.php @@ -698,7 +698,7 @@ public function peekAhead(bool $peekAhead = true): static /** * Yield the query's results * - * @return \Generator + * @return Generator */ public function yieldResults(): Generator { diff --git a/src/Relation.php b/src/Relation.php index 60ddfb88..2aca85c1 100644 --- a/src/Relation.php +++ b/src/Relation.php @@ -2,6 +2,9 @@ namespace ipl\Orm; +use Generator; +use UnexpectedValueException; + /** * Relations represent the connection between models, i.e. the association between rows in one or more tables * on the basis of matching key columns. The relationships are defined using candidate key-foreign key constructs. @@ -263,7 +266,7 @@ public function setJoinType(string $joinType): static * * @return array Candidate key-foreign key column name pairs * - * @throws \UnexpectedValueException If there's no candidate key to be found + * @throws UnexpectedValueException If there's no candidate key to be found * or the foreign key count does not match the candidate key count */ public function determineKeys(Model $source): array @@ -277,7 +280,7 @@ public function determineKeys(Model $source): array } if (empty($candidateKey)) { - throw new \UnexpectedValueException(sprintf( + throw new UnexpectedValueException(sprintf( "Can't join relation '%s' in model '%s'. No candidate key found.", $this->getName(), get_class($source) @@ -293,7 +296,7 @@ public function determineKeys(Model $source): array } if (count($foreignKey) !== count($candidateKey)) { - throw new \UnexpectedValueException(sprintf( + throw new UnexpectedValueException(sprintf( "Can't join relation '%s' in model '%s'." . " Foreign key count (%s) does not match candidate key count (%s).", $this->getName(), @@ -311,9 +314,9 @@ public function determineKeys(Model $source): array * * Yields a three-element array consisting of the source model, target model and the join keys. * - * @return \Generator + * @return Generator */ - public function resolve(): \Generator + public function resolve(): Generator { $source = $this->getSource(); diff --git a/src/Relation/BelongsToMany.php b/src/Relation/BelongsToMany.php index e69e5b02..6e6e0429 100644 --- a/src/Relation/BelongsToMany.php +++ b/src/Relation/BelongsToMany.php @@ -2,6 +2,7 @@ namespace ipl\Orm\Relation; +use Generator; use ipl\Orm\Model; use ipl\Orm\Relation; use ipl\Orm\Relations; @@ -169,7 +170,7 @@ public function setTargetCandidateKey(string|array $targetCandidateKey): static return $this; } - public function resolve(): \Generator + public function resolve(): Generator { $source = $this->getSource(); diff --git a/src/Relations.php b/src/Relations.php index d5809bf8..fb6d1a77 100644 --- a/src/Relations.php +++ b/src/Relations.php @@ -3,6 +3,7 @@ namespace ipl\Orm; use ArrayIterator; +use InvalidArgumentException; use ipl\Orm\Exception\InvalidRelationException; use ipl\Orm\Relation\BelongsTo; use ipl\Orm\Relation\BelongsToMany; @@ -41,7 +42,7 @@ public function has(string $name): bool * * @return Relation * - * @throws \InvalidArgumentException If the relation with the given name does not exist + * @throws InvalidArgumentException If the relation with the given name does not exist */ public function get(string $name): Relation { @@ -57,7 +58,7 @@ public function get(string $name): Relation * * @return $this * - * @throws \InvalidArgumentException If a relation with the given name already exists + * @throws InvalidArgumentException If a relation with the given name already exists */ public function add(Relation $relation): static { @@ -79,14 +80,14 @@ public function add(Relation $relation): static * * @return BelongsTo|BelongsToOne|BelongsToMany|HasMany|HasOne|Relation * - * @throws \InvalidArgumentException If the target model class is not of type string + * @throws InvalidArgumentException If the target model class is not of type string */ public function create(string $class, string $name, string $targetClass): Relation { $relation = new $class(); if (! $relation instanceof Relation) { - throw new \InvalidArgumentException(sprintf( + throw new InvalidArgumentException(sprintf( '%s() expects parameter 1 to be a subclass of %s, %s given', __METHOD__, Relation::class, @@ -97,7 +98,7 @@ public function create(string $class, string $name, string $targetClass): Relati // Test target model $target = new $targetClass(); if (! $target instanceof Model) { - throw new \InvalidArgumentException(sprintf( + throw new InvalidArgumentException(sprintf( '%s() expects parameter 3 to be a subclass of %s, %s given', __METHOD__, Model::class, @@ -219,7 +220,7 @@ public function getIterator(): Traversable protected function assertRelationDoesNotYetExist(string $name): void { if ($this->has($name)) { - throw new \InvalidArgumentException("Relation '$name' already exists"); + throw new InvalidArgumentException("Relation '$name' already exists"); } } From 0ca568afb3f6021b2939ce66a2b9f8c81a3f7d48 Mon Sep 17 00:00:00 2001 From: Bastian Lederer Date: Fri, 27 Feb 2026 11:06:53 +0100 Subject: [PATCH 5/6] Use array destructuring instead of `list()` --- src/Common/SortUtil.php | 2 +- src/Compat/FilterProcessor.php | 4 ++-- src/Hydrator.php | 4 ++-- src/Query.php | 8 ++++---- src/Resolver.php | 4 ++-- src/UnionQuery.php | 2 +- tests/BelongsToManyTest.php | 4 ++-- tests/SqlTest.php | 2 +- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/Common/SortUtil.php b/src/Common/SortUtil.php index e615cae7..2a5addf2 100644 --- a/src/Common/SortUtil.php +++ b/src/Common/SortUtil.php @@ -19,7 +19,7 @@ public static function createOrderBy(string|array $sort): array $orderBy = []; foreach ($columnsAndDirections as $columnAndDirection) { - list($column, $direction) = static::splitColumnAndDirection($columnAndDirection); + [$column, $direction] = static::splitColumnAndDirection($columnAndDirection); $orderBy[] = [$column, $direction]; } diff --git a/src/Compat/FilterProcessor.php b/src/Compat/FilterProcessor.php index 222c533c..f4f215e7 100644 --- a/src/Compat/FilterProcessor.php +++ b/src/Compat/FilterProcessor.php @@ -107,7 +107,7 @@ protected function requireAndResolveFilterColumns( $filter->metaData()->set('columnPath', $column); - list($relationPath, $columnName) = preg_split('/\.(?=[^.]+$)/', $column); + [$relationPath, $columnName] = preg_split('/\.(?=[^.]+$)/', $column); $subject = null; $relations = new AppendIterator(); @@ -275,7 +275,7 @@ protected function requireAndResolveFilterColumns( $subQueryFilters[] = [$baseFilters ?: $generalRules, $count, false]; } - foreach ($subQueryFilters as list($filters, $count, $negate)) { + foreach ($subQueryFilters as [$filters, $count, $negate]) { $subQueryFilter = null; if ($count !== null) { $aggregateFilter = Filter::any(); diff --git a/src/Hydrator.php b/src/Hydrator.php index 33032992..827bac49 100644 --- a/src/Hydrator.php +++ b/src/Hydrator.php @@ -111,7 +111,7 @@ public function hydrate(array $data, Model $model): Model $defaultsToApply = []; $columnToTargetMap = $this->columnToTargetMap; foreach ($this->hydrators as $path => $vars) { - list($target, $relation, $columnToPropertyMap, $defaults) = $vars; + [$target, $relation, $columnToPropertyMap, $defaults] = $vars; $subject = $model; if ($relation !== null) { @@ -200,7 +200,7 @@ public function hydrate(array $data, Model $model): Model } // Apply defaults last, otherwise we may evaluate them during hydration - foreach ($defaultsToApply as list($subject, $defaults)) { + foreach ($defaultsToApply as [$subject, $defaults]) { foreach ($defaults as $name => $default) { if (! $subject->hasProperty($name)) { $subject->$name = $default; diff --git a/src/Query.php b/src/Query.php index fbbfa9eb..14896d4c 100644 --- a/src/Query.php +++ b/src/Query.php @@ -496,7 +496,7 @@ public function assembleSelect(): Select continue; } - foreach ($relation->resolve() as list($source, $target, $relatedKeys)) { + foreach ($relation->resolve() as [$source, $target, $relatedKeys]) { /** @var Model $source */ /** @var Model $target */ @@ -739,7 +739,7 @@ protected function groupColumnsByTarget(Generator $columns): SplObjectStorage { $columnStorage = new SplObjectStorage(); - foreach ($columns as list($target, $alias, $column)) { + foreach ($columns as [$target, $alias, $column]) { if (! $columnStorage->offsetExists($target)) { $resolved = new ArrayObject(); $columnStorage->offsetSet($target, $resolved); @@ -787,7 +787,7 @@ protected function order(Select $select): static $selectedColumns = $select->getColumns(); foreach ($orderBy as $part) { - list($column, $direction) = $part; + [$column, $direction] = $part; if (! $column instanceof ExpressionInterface && isset($selectedColumns[$column])) { // If it's a custom alias, we have no other way of knowing it, @@ -800,7 +800,7 @@ protected function order(Select $select): static } } - foreach ($resolver->requireAndResolveColumns($columns) as list($model, $alias, $column)) { + foreach ($resolver->requireAndResolveColumns($columns) as [$model, $alias, $column]) { $direction = array_shift($directions); $selectColumns = $resolver->getSelectColumns($model); $tableName = $resolver->getAlias($model); diff --git a/src/Resolver.php b/src/Resolver.php index 3fa1f53a..ab8ecef7 100644 --- a/src/Resolver.php +++ b/src/Resolver.php @@ -341,7 +341,7 @@ public function qualifyColumns(iterable $columns, ?Model $model = null): array foreach ($columns as $alias => $column) { if (is_int($alias) && is_array($column)) { // $columns is $this->requireAndResolveColumns() - list($target, $alias, $columnName) = $column; + [$target, $alias, $columnName] = $column; $targetAlias = $this->getAlias($target); // Thanks to PHP 5.6 where `list` is evaluated from right to left. It will extract @@ -388,7 +388,7 @@ public function qualifyColumnsAndAliases(iterable $columns, ?Model $model = null foreach ($columns as $alias => $column) { if (is_int($alias) && is_array($column)) { // $columns is $this->requireAndResolveColumns() - list($target, $alias, $columnName) = $column; + [$target, $alias, $columnName] = $column; $targetAlias = $this->getAlias($target); // Thanks to PHP 5.6 where `list` is evaluated from right to left. It will extract diff --git a/src/UnionQuery.php b/src/UnionQuery.php index 5d4204d1..b1860c16 100644 --- a/src/UnionQuery.php +++ b/src/UnionQuery.php @@ -21,7 +21,7 @@ public function getUnions(): array /** @var UnionModel $model */ $model = $this->getModel(); - foreach ($model->getUnions() as list($target, $relations, $columns)) { + foreach ($model->getUnions() as [$target, $relations, $columns]) { /** @var class-string $target */ $query = $target::on($this->getDb()) ->columns($columns) diff --git a/tests/BelongsToManyTest.php b/tests/BelongsToManyTest.php index 10ebdebd..ce552f46 100644 --- a/tests/BelongsToManyTest.php +++ b/tests/BelongsToManyTest.php @@ -39,7 +39,7 @@ public function testResolveDefaultKeys() $relations ->get('user') ->setSource($model) - ->resolve() as list($from, $to, $keys) + ->resolve() as [$from, $to, $keys] ) { reset($keys); $actual[] = [ @@ -76,7 +76,7 @@ public function testResolveRespectsCustomKeysInTroughModels() $relations ->get('user_custom_keys') ->setSource($model) - ->resolve() as list($from, $to, $keys) + ->resolve() as [$from, $to, $keys] ) { reset($keys); $actual[] = [ diff --git a/tests/SqlTest.php b/tests/SqlTest.php index ef3f2a83..372429c1 100644 --- a/tests/SqlTest.php +++ b/tests/SqlTest.php @@ -382,7 +382,7 @@ public function assertSql($sql, $query, $values = null) // Reduce whitespaces to just one space $sql = preg_replace('/\s+/', ' ', trim($sql)); - list($stmt, $bind) = $this->queryBuilder->assemble($query); + [$stmt, $bind] = $this->queryBuilder->assemble($query); $this->assertSame($sql, $stmt); From cee39f314d2a9d5ce0a890fe1673ae1006abf2ca Mon Sep 17 00:00:00 2001 From: Bastian Lederer Date: Thu, 5 Mar 2026 11:22:07 +0100 Subject: [PATCH 6/6] Add missing `@throws` tags Where functions are already adjusted and `throws` tags are missing they are added --- src/ColumnDefinition.php | 2 ++ src/Relation/BelongsToMany.php | 2 ++ src/Relations.php | 4 ++++ src/Resolver.php | 2 ++ 4 files changed, 10 insertions(+) diff --git a/src/ColumnDefinition.php b/src/ColumnDefinition.php index 6278a2ef..b324a977 100644 --- a/src/ColumnDefinition.php +++ b/src/ColumnDefinition.php @@ -62,6 +62,8 @@ public function setLabel(?string $label): static * @param array $options * * @return static + * + * @throws InvalidArgumentException If the given options do not provide a name */ public static function fromArray(array $options): static { diff --git a/src/Relation/BelongsToMany.php b/src/Relation/BelongsToMany.php index 6e6e0429..edf5bf1b 100644 --- a/src/Relation/BelongsToMany.php +++ b/src/Relation/BelongsToMany.php @@ -85,6 +85,8 @@ public function setThroughAlias(string $throughAlias): static * Get the junction model * * @return Model + * + * @throws LogicException If no through class is configured */ public function getThrough(): Model { diff --git a/src/Relations.php b/src/Relations.php index fb6d1a77..e9240969 100644 --- a/src/Relations.php +++ b/src/Relations.php @@ -216,6 +216,8 @@ public function getIterator(): Traversable * @param string $name * * @return void + * + * @throws InvalidArgumentException */ protected function assertRelationDoesNotYetExist(string $name): void { @@ -228,6 +230,8 @@ protected function assertRelationDoesNotYetExist(string $name): void * Throw exception if a relation with the given name does not exist * * @param string $name + * + * @throws InvalidArgumentException */ protected function assertRelationExists(string $name): void { diff --git a/src/Resolver.php b/src/Resolver.php index ab8ecef7..e054e48a 100644 --- a/src/Resolver.php +++ b/src/Resolver.php @@ -753,6 +753,8 @@ protected function collectColumns(Model $subject): void * @param Model $subject * * @return array + * + * @throws LogicException If a column definition's name differs from its array index */ protected function collectMetaData(Model $subject): array {