Skip to content

Commit

Permalink
Create MagicRelationsTrait (#317)
Browse files Browse the repository at this point in the history
  • Loading branch information
Tigrov authored May 19, 2024
1 parent f8febec commit 13c782f
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 64 deletions.
2 changes: 2 additions & 0 deletions src/ActiveRecord.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Yiisoft\ActiveRecord\Trait\ArrayableTrait;
use Yiisoft\ActiveRecord\Trait\ArrayAccessTrait;
use Yiisoft\ActiveRecord\Trait\ArrayIteratorTrait;
use Yiisoft\ActiveRecord\Trait\MagicRelationsTrait;
use Yiisoft\Arrays\ArrayableInterface;
use Yiisoft\Db\Exception\Exception;
use Yiisoft\Db\Exception\InvalidArgumentException;
Expand Down Expand Up @@ -95,6 +96,7 @@ class ActiveRecord extends BaseActiveRecord implements ArrayableInterface, Array
use ArrayableTrait;
use ArrayAccessTrait;
use ArrayIteratorTrait;
use MagicRelationsTrait;

/**
* The insert operation. This is mainly used when overriding {@see transactions()} to specify which operations are
Expand Down
64 changes: 0 additions & 64 deletions src/BaseActiveRecordTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,17 @@

namespace Yiisoft\ActiveRecord;

use Error;
use Exception;
use ReflectionException;
use ReflectionMethod;
use Throwable;
use Yiisoft\Db\Exception\InvalidArgumentException;
use Yiisoft\Db\Exception\InvalidCallException;
use Yiisoft\Db\Exception\InvalidConfigException;
use Yiisoft\Db\Exception\UnknownPropertyException;

use function array_key_exists;
use function lcfirst;
use function method_exists;
use function property_exists;
use function substr;
use function ucfirst;

trait BaseActiveRecordTrait
Expand Down Expand Up @@ -80,66 +76,6 @@ public function checkRelation(string $name): mixed
throw new UnknownPropertyException('Getting unknown property: ' . static::class . '::' . $name);
}

/**
* Returns the relation object with the specified name.
*
* A relation is defined by a getter method which returns an {@see ActiveQueryInterface} object.
*
* It can be declared in either the Active Record class itself or one of its behaviors.
*
* @param string $name the relation name, e.g. `orders` for a relation defined via `getOrders()` method
* (case-sensitive).
* @param bool $throwException whether to throw exception if the relation does not exist.
*
* @throws InvalidArgumentException if the named relation does not exist.
* @throws ReflectionException
*
* @return ActiveQueryInterface|null the relational query object. If the relation does not exist and
* `$throwException` is `false`, `null` will be returned.
*/
public function getRelation(string $name, bool $throwException = true): ActiveQueryInterface|null
{
$getter = 'get' . ucfirst($name);

try {
/** the relation could be defined in a behavior */
$relation = $this->$getter();
} catch (Error) {
if ($throwException) {
throw new InvalidArgumentException(static::class . ' has no relation named "' . $name . '".');
}

return null;
}

if (!$relation instanceof ActiveQueryInterface) {
if ($throwException) {
throw new InvalidArgumentException(static::class . ' has no relation named "' . $name . '".');
}

return null;
}

if (method_exists($this, $getter)) {
/** relation name is case sensitive, trying to validate it when the relation is defined within this class */
$method = new ReflectionMethod($this, $getter);
$realName = lcfirst(substr($method->getName(), 3));

if ($realName !== $name) {
if ($throwException) {
throw new InvalidArgumentException(
'Relation names are case sensitive. ' . static::class
. " has a relation named \"$realName\" instead of \"$name\"."
);
}

return null;
}
}

return $relation;
}

/**
* Checks if a property value is null.
*
Expand Down
79 changes: 79 additions & 0 deletions src/Trait/MagicRelationsTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php

declare(strict_types=1);

namespace Yiisoft\ActiveRecord\Trait;

use Error;
use ReflectionException;
use ReflectionMethod;
use Yiisoft\ActiveRecord\ActiveQueryInterface;
use Yiisoft\Db\Exception\InvalidArgumentException;

use function lcfirst;
use function method_exists;
use function substr;
use function ucfirst;

trait MagicRelationsTrait
{
/**
* Returns the relation object with the specified name.
*
* A relation is defined by a getter method which returns an {@see ActiveQueryInterface} object.
*
* It can be declared in either the Active Record class itself or one of its behaviors.
*
* @param string $name the relation name, e.g. `orders` for a relation defined via `getOrders()` method
* (case-sensitive).
* @param bool $throwException whether to throw exception if the relation does not exist.
*
* @throws InvalidArgumentException if the named relation does not exist.
* @throws ReflectionException
*
* @return ActiveQueryInterface|null the relational query object. If the relation does not exist and
* `$throwException` is `false`, `null` will be returned.
*/
public function getRelation(string $name, bool $throwException = true): ActiveQueryInterface|null
{
$getter = 'get' . ucfirst($name);

try {
/** the relation could be defined in a behavior */
$relation = $this->$getter();
} catch (Error) {
if ($throwException) {
throw new InvalidArgumentException(static::class . ' has no relation named "' . $name . '".');
}

return null;
}

if (!$relation instanceof ActiveQueryInterface) {
if ($throwException) {
throw new InvalidArgumentException(static::class . ' has no relation named "' . $name . '".');
}

return null;
}

if (method_exists($this, $getter)) {
/** relation name is case sensitive, trying to validate it when the relation is defined within this class */
$method = new ReflectionMethod($this, $getter);
$realName = lcfirst(substr($method->getName(), 3));

if ($realName !== $name) {
if ($throwException) {
throw new InvalidArgumentException(
'Relation names are case sensitive. ' . static::class
. " has a relation named \"$realName\" instead of \"$name\"."
);
}

return null;
}
}

return $relation;
}
}

0 comments on commit 13c782f

Please sign in to comment.