Passed
Pull Request — master (#317)
by Sergei
07:52 queued 05:11
created

BaseActiveRecordTrait::getRelation()   B

Complexity

Conditions 8
Paths 8

Size

Total Lines 41
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 8

Importance

Changes 0
Metric Value
cc 8
eloc 21
nc 8
nop 2
dl 0
loc 41
ccs 20
cts 20
cp 1
crap 8
rs 8.4444
c 0
b 0
f 0

1 Method

Rating   Name   Duplication   Size   Complexity  
A BaseActiveRecordTrait::__unset() 0 9 4
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\ActiveRecord;
6
7
use Exception;
8
use ReflectionException;
9
use Throwable;
10
use Yiisoft\Db\Exception\InvalidArgumentException;
11
use Yiisoft\Db\Exception\InvalidCallException;
12
use Yiisoft\Db\Exception\InvalidConfigException;
13
use Yiisoft\Db\Exception\UnknownPropertyException;
14
15
use function array_key_exists;
16
use function method_exists;
17
use function property_exists;
18
use function ucfirst;
19
20
trait BaseActiveRecordTrait
21
{
22
    private static string|null $connectionId = null;
23
24
    /**
25
     * PHP getter magic method.
26
     *
27
     * This method is overridden so that attributes and related objects can be accessed like properties.
28
     *
29
     * @param string $name property name.
30
     *
31
     * @throws InvalidArgumentException|InvalidCallException|InvalidConfigException|ReflectionException|Throwable
32
     * @throws UnknownPropertyException
33
     *
34
     * @return mixed property value.
35
     *
36
     * {@see getAttribute()}
37
     */
38
    public function __get(string $name)
39
    {
40
        if (isset($this->attributes[$name]) || array_key_exists($name, $this->attributes)) {
41
            return $this->attributes[$name];
42
        }
43
44
        if ($this->hasAttribute($name)) {
0 ignored issues
show
Bug introduced by
It seems like hasAttribute() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

44
        if ($this->/** @scrutinizer ignore-call */ hasAttribute($name)) {
Loading history...
45
            return null;
46 406
        }
47
48 406
        if (isset($this->related[$name]) || array_key_exists($name, $this->related)) {
49 368
            return $this->related[$name];
50
        }
51
52 251
        /** @var mixed $value */
53 39
        $value = $this->checkRelation($name);
54
55
        if ($value instanceof ActiveQuery) {
56 241
            $this->setRelationDependencies($name, $value);
0 ignored issues
show
Bug introduced by
It seems like setRelationDependencies() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

56
            $this->/** @scrutinizer ignore-call */ 
57
                   setRelationDependencies($name, $value);
Loading history...
57 152
            return $this->related[$name] = $value->findFor($name, $this);
0 ignored issues
show
Bug Best Practice introduced by
The property related does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
Bug introduced by
$this of type Yiisoft\ActiveRecord\BaseActiveRecordTrait is incompatible with the type Yiisoft\ActiveRecord\ActiveRecordInterface expected by parameter $model of Yiisoft\ActiveRecord\ActiveQuery::findFor(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

57
            return $this->related[$name] = $value->findFor($name, /** @scrutinizer ignore-type */ $this);
Loading history...
58
        }
59
60 147
        return $value;
61
    }
62 131
63 101
    public function checkRelation(string $name): mixed
64 101
    {
65
        $getter = 'get' . ucfirst($name);
66
67 44
        if (method_exists($this, $getter)) {
68
            /** read property, e.g. getName() */
69
            return $this->$getter();
70 147
        }
71
72 147
        if (method_exists($this, 'set' . ucfirst($name))) {
73
            throw new InvalidCallException('Getting write-only property: ' . static::class . '::' . $name);
74 147
        }
75
76 139
        throw new UnknownPropertyException('Getting unknown property: ' . static::class . '::' . $name);
77
    }
78
79 8
    /**
80 4
     * Checks if a property value is null.
81
     *
82
     * This method overrides the parent implementation by checking if the named attribute is `null` or not.
83 4
     *
84
     * @param string $name the property name or the event name.
85
     *
86
     * @return bool whether the property value is null.
87
     */
88
    public function __isset(string $name): bool
89
    {
90
        try {
91
            return $this->__get($name) !== null;
92
        } catch (InvalidCallException|UnknownPropertyException) {
0 ignored issues
show
Coding Style introduced by
Expected at least 1 space before "|"; 0 found
Loading history...
Coding Style introduced by
Expected at least 1 space after "|"; 0 found
Loading history...
93
            return false;
94
        }
95
    }
96
97
    /**
98
     * Sets a component property to be null.
99
     *
100
     * This method overrides the parent implementation by clearing the specified attribute value.
101
     *
102 228
     * @param string $name the property name or the event name.
103
     */
104 228
    public function __unset(string $name): void
105
    {
106
        if ($this->hasAttribute($name)) {
107
            unset($this->attributes[$name]);
108 228
            if (!empty($this->relationsDependencies[$name])) {
0 ignored issues
show
Bug Best Practice introduced by
The property relationsDependencies does not exist on Yiisoft\ActiveRecord\BaseActiveRecordTrait. Since you implemented __get, consider adding a @property annotation.
Loading history...
109 4
                $this->resetDependentRelations($name);
0 ignored issues
show
Bug introduced by
It seems like resetDependentRelations() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

109
                $this->/** @scrutinizer ignore-call */ 
110
                       resetDependentRelations($name);
Loading history...
110 4
            }
111 4
        } elseif (array_key_exists($name, $this->related)) {
112
            unset($this->related[$name]);
113
        }
114 4
    }
115
116
    /**
117 224
     * PHP setter magic method.
118 4
     *
119 4
     * This method is overridden so that AR attributes can be accessed like properties.
120
     *
121
     * @param string $name property name.
122 4
     *
123
     * @throws InvalidCallException
124
     */
125 220
    public function __set(string $name, mixed $value): void
126
    {
127 220
        if ($this->hasAttribute($name)) {
128 220
            if (
129
                !empty($this->relationsDependencies[$name])
0 ignored issues
show
Bug Best Practice introduced by
The property relationsDependencies does not exist on Yiisoft\ActiveRecord\BaseActiveRecordTrait. Since you implemented __get, consider adding a @property annotation.
Loading history...
130 220
                && (!array_key_exists($name, $this->attributes) || $this->attributes[$name] !== $value)
131 4
            ) {
132 4
                $this->resetDependentRelations($name);
133 4
            }
134 4
            $this->attributes[$name] = $value;
0 ignored issues
show
Bug Best Practice introduced by
The property attributes does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
135
            return;
136
        }
137
138 4
        if (method_exists($this, 'get' . ucfirst($name))) {
139
            throw new InvalidCallException('Setting read-only property: ' . static::class . '::' . $name);
140
        }
141
142 216
        throw new UnknownPropertyException('Setting unknown property: ' . static::class . '::' . $name);
143
    }
144
145
    /**
146
     * Returns a value indicating whether a property is defined for this component.
147
     *
148
     * A property is defined if:
149
     *
150
     * - the class has a getter or setter method associated with the specified name (in this case, property name is
151
     *   case-insensitive).
152
     * - the class has a member variable with the specified name (when `$checkVars` is true).
153
     * - an attached behavior has a property of the given name (when `$checkBehaviors` is true).
154 81
     *
155
     * @param string $name the property name.
156
     * @param bool $checkVars whether to treat member variables as properties.
157 81
     *
158 8
     * @return bool whether the property is defined.
159 8
     *
160
     * {@see canGetProperty()}
161
     * {@see canSetProperty()}
162
     */
163
    public function hasProperty(string $name, bool $checkVars = true): bool
164
    {
165
        return $this->canGetProperty($name, $checkVars)
166
            || $this->canSetProperty($name, false);
167
    }
168
169
    public function canGetProperty(string $name, bool $checkVars = true): bool
170 24
    {
171
        if (method_exists($this, 'get' . ucfirst($name)) || ($checkVars && property_exists($this, $name))) {
172 24
            return true;
173 14
        }
174 14
175 14
        try {
176
            return $this->hasAttribute($name);
177 10
        } catch (Exception) {
178 10
            /** `hasAttribute()` may fail on base/abstract classes in case automatic attribute list fetching used */
179
            return false;
180 24
        }
181
    }
182
183
    public function canSetProperty(string $name, bool $checkVars = true): bool
184
    {
185
        if (method_exists($this, 'set' . ucfirst($name)) || ($checkVars && property_exists($this, $name))) {
186
            return true;
187
        }
188
189
        try {
190
            return $this->hasAttribute($name);
191
        } catch (Exception) {
192 186
            /** `hasAttribute()` may fail on base/abstract classes in case automatic attribute list fetching used */
193
            return false;
194 186
        }
195
    }
196
}
197