Passed
Push — master ( d2535c...dde631 )
by Sergei
03:06
created

MagicRelationsTrait::relationQuery()   B

Complexity

Conditions 8
Paths 8

Size

Total Lines 41
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 21
c 0
b 0
f 0
nc 8
nop 2
dl 0
loc 41
rs 8.4444
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\ActiveRecord\Trait;
6
7
use Error;
8
use ReflectionException;
9
use ReflectionMethod;
10
use Yiisoft\ActiveRecord\ActiveQueryInterface;
11
use Yiisoft\ActiveRecord\ActiveRecordInterface;
12
use Yiisoft\Db\Exception\InvalidArgumentException;
13
14
use function lcfirst;
15
use function method_exists;
16
use function substr;
17
use function ucfirst;
18
19
/**
20
 * Trait to define {@see ActiveRecordInterface::relationQuery()} method to access relation queries of an ActiveRecord
21
 * instance.
22
 */
23
trait MagicRelationsTrait
24
{
25
    /**
26
     * Returns the relation object with the specified name.
27
     *
28
     * A relation is defined by a getter method which returns an {@see ActiveQueryInterface} object.
29
     *
30
     * It can be declared in either the Active Record class itself or one of its behaviors.
31
     *
32
     * @param string $name the relation name, e.g. `orders` for a relation defined via `getOrders()` method
33
     * (case-sensitive).
34
     * @param bool $throwException whether to throw exception if the relation does not exist.
35
     *
36
     * @throws InvalidArgumentException if the named relation does not exist.
37
     * @throws ReflectionException
38
     *
39
     * @return ActiveQueryInterface|null the relational query object. If the relation does not exist and
40
     * `$throwException` is `false`, `null` will be returned.
41
     */
42
    public function relationQuery(string $name, bool $throwException = true): ActiveQueryInterface|null
43
    {
44
        $getter = 'get' . ucfirst($name);
45
46
        try {
47
            /** the relation could be defined in a behavior */
48
            $relation = $this->$getter();
49
        } catch (Error) {
50
            if ($throwException) {
51
                throw new InvalidArgumentException(static::class . ' has no relation named "' . $name . '".');
52
            }
53
54
            return null;
55
        }
56
57
        if (!$relation instanceof ActiveQueryInterface) {
58
            if ($throwException) {
59
                throw new InvalidArgumentException(static::class . ' has no relation named "' . $name . '".');
60
            }
61
62
            return null;
63
        }
64
65
        if (method_exists($this, $getter)) {
66
            /** relation name is case sensitive, trying to validate it when the relation is defined within this class */
67
            $method = new ReflectionMethod($this, $getter);
68
            $realName = lcfirst(substr($method->getName(), 3));
69
70
            if ($realName !== $name) {
71
                if ($throwException) {
72
                    throw new InvalidArgumentException(
73
                        'Relation names are case sensitive. ' . static::class
74
                        . " has a relation named \"$realName\" instead of \"$name\"."
75
                    );
76
                }
77
78
                return null;
79
            }
80
        }
81
82
        return $relation;
83
    }
84
}
85