Skip to content

Commit e9f81cb

Browse files
committed
add nested model trait
0 parents  commit e9f81cb

File tree

1 file changed

+142
-0
lines changed

1 file changed

+142
-0
lines changed

src/NestedModelTrait.php

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
<?php
2+
3+
namespace Michalsn\CodeIgniterNestedModel;
4+
5+
use CodeIgniter\Model;
6+
use InvalidArgumentException;
7+
use ReflectionObject;
8+
9+
trait NestedModelTrait
10+
{
11+
protected array $validRelations = ['hasOne', 'hasMany'];
12+
protected array $activeRelations = [];
13+
protected array $clausesForRelations = [];
14+
15+
/**
16+
* Set defined relation for usage.
17+
*/
18+
public function with(string $name, ?callable $clause = null): static
19+
{
20+
if (! $this->relations[$name]) {
21+
throw new InvalidArgumentException(sprintf('Incorrect relation name: %s', $name));
22+
}
23+
24+
$this->activeRelations[$name] = $this->relations[$name];
25+
$this->clausesForRelations[$name] = $clause;
26+
27+
if (! isset($this->afterFind[0]) || $this->afterFind[0] !== 'applyRelations') {
28+
array_unshift($this->afterFind, 'applyRelations');
29+
}
30+
31+
return $this;
32+
}
33+
34+
/**
35+
* Register relations via event.
36+
*/
37+
protected function applyRelations(array $data): array
38+
{
39+
if (empty($data['data'])) {
40+
return $data;
41+
}
42+
43+
foreach ($this->activeRelations as $name => $relation) {
44+
if (! in_array($relation[0], $this->validRelations)) {
45+
throw new InvalidArgumentException(sprintf('Incorrect relation type: %s', $relation[0]));
46+
}
47+
$this->applyRelation($name, $relation[0], $relation[1], $relation[2] ?? null, $relation[3] ?? null, $data);
48+
}
49+
50+
$this->activeRelations = [];
51+
$this->clausesForRelations = [];
52+
53+
return $data;
54+
}
55+
56+
/**
57+
* Fire the relation.
58+
*/
59+
protected function applyRelation(string $name, string $relationType, string $model, ?string $foreignKey, ?string $localKey, &$data)
60+
{
61+
$modelInstance = model($model);
62+
63+
$foreignKey = $foreignKey ?? $this->getRelationForeignKey($modelInstance);
64+
$localKey = $localKey ?? $this->primaryKey;
65+
66+
$ids = $this->getRelationIds($data, $localKey);
67+
68+
$modelInstance->whereIn($foreignKey, $ids);
69+
70+
if (! empty($this->clausesForRelations[$name]) && is_callable($this->clausesForRelations[$name])) {
71+
$this->clausesForRelations[$name]();
72+
}
73+
74+
if ($data['singleton']) {
75+
if ($relationType === 'hasOne') {
76+
$results = $modelInstance->first();
77+
} else {
78+
$results = $modelInstance->findAll();
79+
}
80+
81+
if ($this->tempReturnType === 'array') {
82+
$data['data'][$name] = $results;
83+
} else {
84+
$data['data']->{$name} = $results;
85+
}
86+
} else {
87+
$results = $modelInstance->findAll();
88+
89+
$relatedData = [];
90+
91+
if ($relationType === 'hasOne') {
92+
foreach ($results as $row) {
93+
$relatedData[$this->tempReturnType === 'array' ? $row[$foreignKey] : $row->{$foreignKey}] = $row;
94+
}
95+
} else {
96+
foreach ($results as $row) {
97+
$relatedData[$this->tempReturnType === 'array' ? $row[$foreignKey] : $row->{$foreignKey}][] = $row;
98+
}
99+
}
100+
101+
foreach ($data['data'] as &$row) {
102+
if ($this->tempReturnType === 'array') {
103+
$row[$name] = $relatedData[$row[$localKey]] ?? [];
104+
} else {
105+
$row->{$name} = $relatedData[$row->{$localKey}] ?? [];
106+
}
107+
}
108+
109+
$relatedData = null;
110+
}
111+
}
112+
113+
/**
114+
* Get IDs required by relation.
115+
*/
116+
protected function getRelationIds(array $data, ?string $localKey): array
117+
{
118+
$key = $localKey ?? $this->primaryKey;
119+
120+
if ($data['singleton']) {
121+
return $this->tempReturnType === 'array'
122+
? [$data['data'][$key]]
123+
: [$data['data']->{$key}];
124+
}
125+
126+
return array_column($data['data'], $key);
127+
}
128+
129+
/**
130+
* Guess foreign key.
131+
*/
132+
protected function getRelationForeignKey(Model $model): string
133+
{
134+
helper('inflector');
135+
136+
$refObj = new ReflectionObject($model);
137+
$refProp = $refObj->getProperty('primaryKey');
138+
$refProp->setAccessible(true);
139+
140+
return sprintf('%s_%s', singular($this->table), $refProp->getValue($model));
141+
}
142+
}

0 commit comments

Comments
 (0)