• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

michalsn / codeigniter-nested-model / 16151277242

08 Jul 2025 06:22PM UTC coverage: 95.745%. First build
16151277242

Pull #10

github

web-flow
Merge 01ca956f7 into acbe29a15
Pull Request #10: feat: add ID transformation support for relations

10 of 12 new or added lines in 1 file covered. (83.33%)

405 of 423 relevant lines covered (95.74%)

11.66 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.06
/src/Relation.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Michalsn\CodeIgniterNestedModel;
6

7
use Closure;
8
use CodeIgniter\Entity\Entity;
9
use CodeIgniter\Model;
10
use Michalsn\CodeIgniterNestedModel\Enums\OrderTypes;
11
use Michalsn\CodeIgniterNestedModel\Enums\RelationTypes;
12
use Michalsn\CodeIgniterNestedModel\Exceptions\NestedModelException;
13
use ReflectionObject;
14

15
class Relation
16
{
17
    private ?Closure $conditions = null;
18
    private ?OfMany $ofMany      = null;
19
    private ?Through $through    = null;
20
    private ?Many $many          = null;
21

22
    /**
23
     * @var list<With>
24
     */
25
    private ?array $with = null;
26

27
    /**
28
     * @var list<mixed>
29
     */
30
    private array|Entity $data = [];
31

32
    public function __construct(
33
        public readonly RelationTypes $type,
34
        public readonly Model $model,
35
        public readonly string $foreignKey,
36
        public readonly string $primaryKey,
37
    ) {
38
    }
35✔
39

40
    public function setConditions(Closure $closure): static
41
    {
42
        $this->conditions = $closure;
3✔
43

44
        return $this;
3✔
45
    }
46

47
    public function applyConditions(): static
48
    {
49
        if ($this->conditions === null) {
23✔
50
            return $this;
20✔
51
        }
52

53
        $closure = $this->conditions;
3✔
54
        $closure($this->model);
3✔
55

56
        $this->conditions = null;
3✔
57

58
        return $this;
3✔
59
    }
60

61
    /**
62
     * @param list<mixed> $data
63
     */
64
    public function setData(array|Entity $data): static
65
    {
66
        $this->data = $data;
35✔
67

68
        return $this;
35✔
69
    }
70

71
    /**
72
     * @return list<mixed>
73
     */
74
    public function getData(): array|Entity
75
    {
76
        return ($this->type === RelationTypes::hasOne) ?
35✔
77
            [$this->data] :
35✔
78
            $this->data;
35✔
79
    }
80

81
    public function setWith(With $with): static
82
    {
83
        $this->with[] = $with;
35✔
84

85
        return $this;
35✔
86
    }
87

88
    public function applyWith(): static
89
    {
90
        if ($this->with === null) {
35✔
91
            return $this;
35✔
92
        }
93

94
        foreach ($this->with as &$item) {
35✔
95
            $this->model->with($item->name, $item->closure);
35✔
96
            unset($item);
35✔
97
        }
98

99
        return $this;
35✔
100
    }
101

102
    public function setThrough(Model $model, ?string $foreignKey = null, ?string $primaryKey = null): static
103
    {
104
        $this->through = new Through(
5✔
105
            $model,
5✔
106
            $foreignKey ?? get_foreign_key($model),
5✔
107
            $primaryKey ?? get_primary_key($model),
5✔
108
        );
5✔
109

110
        return $this;
5✔
111
    }
112

113
    public function hasThrough(): bool
114
    {
115
        return $this->through !== null;
35✔
116
    }
117

118
    public function setMany(string $pivotTable, string $pivotForeignKey, string $pivotRelatedKey): static
119
    {
120
        $this->many = new Many($pivotTable, $pivotForeignKey, $pivotRelatedKey);
4✔
121

122
        return $this;
4✔
123
    }
124

125
    public function hasMany(): bool
126
    {
127
        return $this->many !== null;
35✔
128
    }
129

130
    public function getMany(): ?Many
131
    {
132
        return $this->many;
×
133
    }
134

135
    public function applyRelation(array $id, string $primaryKey): static
136
    {
137
        if ($this->through !== null) {
24✔
138
            $this->model
4✔
139
                ->select(sprintf('%s.*', $this->model->getTable()))
4✔
140
                ->join(
4✔
141
                    $this->through->model->getTable(),
4✔
142
                    sprintf(
4✔
143
                        '%s.%s = %s.%s',
4✔
144
                        $this->through->model->getTable(),
4✔
145
                        $this->through->primaryKey,
4✔
146
                        $this->model->getTable(),
4✔
147
                        $this->foreignKey,
4✔
148
                    ),
4✔
149
                    'LEFT',
4✔
150
                )
4✔
151
                ->whereIn(
4✔
152
                    sprintf(
4✔
153
                        '%s.%s',
4✔
154
                        $this->through->model->getTable(),
4✔
155
                        $primaryKey,
4✔
156
                    ),
4✔
157
                    $id,
4✔
158
                );
4✔
159

160
            return $this;
4✔
161
        }
162

163
        if ($this->many !== null) {
20✔
164
            $this->model
4✔
165
                ->select(
4✔
166
                    sprintf(
4✔
167
                        '%s.*, %s.%s',
4✔
168
                        $this->model->getTable(),
4✔
169
                        $this->many->pivotTable,
4✔
170
                        $this->many->pivotForeignKey,
4✔
171
                    ),
4✔
172
                )
4✔
173
                ->join(
4✔
174
                    $this->many->pivotTable,
4✔
175
                    sprintf(
4✔
176
                        '%s.%s = %s.%s',
4✔
177
                        $this->many->pivotTable,
4✔
178
                        $this->many->pivotRelatedKey,
4✔
179
                        $this->model->getTable(),
4✔
180
                        get_primary_key($this->model),
4✔
181
                    ),
4✔
182
                    'LEFT',
4✔
183
                )
4✔
184
                ->whereIn(
4✔
185
                    sprintf(
4✔
186
                        '%s.%s',
4✔
187
                        $this->many->pivotTable,
4✔
188
                        $this->many->pivotForeignKey,
4✔
189
                    ),
4✔
190
                    $id,
4✔
191
                );
4✔
192

193
            return $this;
4✔
194
        }
195

196
        $this->model->whereIn(
16✔
197
            sprintf(
16✔
198
                '%s.%s',
16✔
199
                $this->model->getTable(),
16✔
200
                $this->foreignKey,
16✔
201
            ),
16✔
202
            $id,
16✔
203
        );
16✔
204

205
        return $this;
16✔
206
    }
207

208
    public function getOfMany(): ?OfMany
209
    {
210
        return $this->ofMany;
3✔
211
    }
212

213
    public function latestOfMany(): static
214
    {
215
        $this->setOrder($this->getOrderField($this->model), OrderTypes::DESC);
2✔
216

217
        return $this;
1✔
218
    }
219

220
    public function oldestOfMany(): static
221
    {
222
        $this->setOrder($this->getOrderField($this->model), OrderTypes::ASC);
1✔
223

224
        return $this;
1✔
225
    }
226

227
    public function ofMany(string $field, OrderTypes $order): static
228
    {
229
        $this->setOrder($field, $order);
2✔
230

231
        return $this;
2✔
232
    }
233

234
    private function setOrder(string $field, OrderTypes $order): void
235
    {
236
        if ($this->type !== RelationTypes::hasOne) {
5✔
237
            throw NestedModelException::forMethodNotSupported($this->type->name);
1✔
238
        }
239

240
        $this->model->orderBy($field, $order->value);
4✔
241

242
        $this->ofMany = new OfMany($field, $order);
4✔
243
    }
244

245
    private function getOrderField(Model $model): string
246
    {
247
        $refObj = new ReflectionObject($model);
3✔
248

249
        $refProp       = $refObj->getProperty('useTimestamps');
3✔
250
        $useTimestamps = $refProp->getValue($model);
3✔
251

252
        if ($useTimestamps) {
3✔
253
            $refProp = $refObj->getProperty('createdField');
3✔
254

255
            return $refProp->getValue($model);
3✔
256
        }
257

258
        $refProp = $refObj->getProperty('primaryKey');
×
259

260
        return $refProp->getValue($model);
×
261
    }
262

263
    public function filterResult(array|Entity $row, string $returnType): array|Entity
264
    {
265
        if ($row === [] || $this->type !== RelationTypes::belongsToMany) {
5✔
266
            return $row;
3✔
267
        }
268

269
        if ($returnType === 'array') {
2✔
270
            unset($row[$this->many->pivotForeignKey]);
×
271
        } else {
272
            unset($row->{$this->many->pivotForeignKey});
2✔
273
        }
274

275
        return $row;
2✔
276
    }
277

278
    public function filterResults(array|Entity $results, string $returnType): array|Entity
279
    {
280
        if ($this->type !== RelationTypes::belongsToMany) {
16✔
281
            return $results;
14✔
282
        }
283

284
        foreach ($results as &$row) {
2✔
285
            if ($returnType === 'array') {
2✔
286
                unset($row[$this->many->pivotForeignKey]);
×
287
            } else {
288
                unset($row->{$this->many->pivotForeignKey});
2✔
289
            }
290
        }
291

292
        return $results;
2✔
293
    }
294
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc