1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* components |
4
|
|
|
* |
5
|
|
|
* @author Wolfy-J |
6
|
|
|
*/ |
7
|
|
|
namespace Spiral\ORM\Entities\Relations; |
8
|
|
|
|
9
|
|
|
use Spiral\Database\Exceptions\QueryException; |
10
|
|
|
use Spiral\ORM\Exceptions\RelationException; |
11
|
|
|
use Spiral\ORM\Exceptions\SelectorException; |
12
|
|
|
use Spiral\ORM\ORMInterface; |
13
|
|
|
use Spiral\ORM\Record; |
14
|
|
|
use Spiral\ORM\RecordInterface; |
15
|
|
|
|
16
|
|
|
/** |
17
|
|
|
* Provides ability to create cached instances of related data. |
18
|
|
|
*/ |
19
|
|
|
abstract class SingularRelation extends AbstractRelation |
20
|
|
|
{ |
21
|
|
|
/** |
22
|
|
|
* Create placeholder model when relation is empty. |
23
|
|
|
*/ |
24
|
|
|
const CREATE_PLACEHOLDER = false; |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* @var RecordInterface |
28
|
|
|
*/ |
29
|
|
|
protected $instance; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* {@inheritdoc} |
33
|
|
|
* |
34
|
|
|
* Returns associated parent or NULL if none associated. |
35
|
|
|
*/ |
36
|
|
|
public function getRelated() |
37
|
|
|
{ |
38
|
|
|
if ($this->instance instanceof RecordInterface) { |
39
|
|
|
return $this->instance; |
40
|
|
|
} |
41
|
|
|
|
42
|
|
|
if (!$this->isLoaded()) { |
43
|
|
|
//Lazy loading our relation data |
44
|
|
|
$this->loadData(); |
45
|
|
|
} |
46
|
|
|
|
47
|
|
|
if (empty($this->data)) { |
48
|
|
|
if (static::CREATE_PLACEHOLDER) { |
49
|
|
|
//Stub instance |
50
|
|
|
return $this->instance = $this->orm->make( |
51
|
|
|
$this->class, |
52
|
|
|
[], |
53
|
|
|
ORMInterface::STATE_NEW |
54
|
|
|
); |
55
|
|
|
} |
56
|
|
|
|
57
|
|
|
return null; |
58
|
|
|
} |
59
|
|
|
|
60
|
|
|
//Create instance based on loaded data |
61
|
|
|
return $this->instance = $this->orm->make( |
62
|
|
|
$this->class, |
63
|
|
|
$this->data, |
64
|
|
|
ORMInterface::STATE_LOADED, |
65
|
|
|
true |
66
|
|
|
); |
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* Must load related data using appropriate method. |
71
|
|
|
*/ |
72
|
|
|
/** |
73
|
|
|
* {@inheritdoc} |
74
|
|
|
* |
75
|
|
|
* @throws SelectorException |
76
|
|
|
* @throws QueryException |
77
|
|
|
*/ |
78
|
|
|
protected function loadData() |
79
|
|
|
{ |
80
|
|
|
$this->loaded = true; |
81
|
|
|
|
82
|
|
|
$innerKey = $this->schema[Record::INNER_KEY]; |
83
|
|
|
if (empty($this->parent->getField($innerKey))) { |
84
|
|
|
//Unable to load |
85
|
|
|
return; |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
$this->data = $this->orm->selector($this->class)->where( |
89
|
|
|
$this->schema[Record::OUTER_KEY], |
90
|
|
|
$this->parent->getField($innerKey) |
|
|
|
|
91
|
|
|
)->fetchData(); |
92
|
|
|
|
93
|
|
|
if (!empty($this->data[0])) { |
94
|
|
|
//Use first result |
95
|
|
|
$this->data = $this->data[0]; |
96
|
|
|
} |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
/** |
100
|
|
|
* @param $value |
101
|
|
|
*/ |
102
|
|
|
protected function assertValid($value) |
103
|
|
|
{ |
104
|
|
|
if (is_null($value)) { |
105
|
|
|
if (!$this->schema[Record::NULLABLE]) { |
106
|
|
|
throw new RelationException("Relation is not nullable"); |
107
|
|
|
} |
108
|
|
|
} elseif (!is_a($value, $this->class, false)) { |
109
|
|
|
throw new RelationException( |
110
|
|
|
"Must be an instance of '{$this->class}', '" . get_class($value) . "' given" |
111
|
|
|
); |
112
|
|
|
} |
113
|
|
|
} |
114
|
|
|
} |
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignore
PhpDoc annotation to the duplicate definition and it will be ignored.