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

valkyrjaio / valkyrja / 12764393313

14 Jan 2025 09:07AM UTC coverage: 47.402% (-0.009%) from 47.411%
12764393313

push

github

MelechMizrachi
Fix styling issue.

0 of 1 new or added line in 1 file covered. (0.0%)

1 existing line in 1 file now uncovered.

5173 of 10913 relevant lines covered (47.4%)

19.07 hits per line

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

0.0
/src/Valkyrja/Orm/Repository/CacheRepository.php
1
<?php
2

3
declare(strict_types=1);
4

5
/*
6
 * This file is part of the Valkyrja Framework package.
7
 *
8
 * (c) Melech Mizrachi <melechmizrachi@gmail.com>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13

14
namespace Valkyrja\Orm\Repository;
15

16
use JsonException;
17
use Valkyrja\Cache\Contract\Cache;
18
use Valkyrja\Cache\Driver\Contract\Driver as CacheDriver;
19
use Valkyrja\Orm\Contract\Orm;
20
use Valkyrja\Orm\Driver\Contract\Driver;
21
use Valkyrja\Orm\Entity\Contract\Entity;
22
use Valkyrja\Orm\Entity\Contract\SoftDeleteEntity;
23
use Valkyrja\Orm\Exception\EntityNotFoundException;
24
use Valkyrja\Orm\QueryBuilder\Contract\QueryBuilder;
25
use Valkyrja\Orm\Repository\Contract\CacheRepository as Contract;
26
use Valkyrja\Orm\Repository\Enum\StoreType;
27
use Valkyrja\Type\BuiltIn\Support\Arr;
28
use Valkyrja\Type\BuiltIn\Support\Obj;
29

30
use function is_array;
31
use function md5;
32
use function serialize;
33
use function spl_object_id;
34
use function unserialize;
35

36
/**
37
 * Class CacheRepository.
38
 *
39
 * @author Melech Mizrachi
40
 */
41
class CacheRepository extends Repository implements Contract
42
{
43
    /**
44
     * The cache store.
45
     *
46
     * @var CacheDriver
47
     */
48
    protected CacheDriver $store;
49

50
    /**
51
     * The id of a findOne (to tag if null returned).
52
     *
53
     * @var int|string|null
54
     */
55
    protected int|string|null $id;
56

57
    /**
58
     * The entities awaiting to be stored.
59
     *
60
     * @var Entity[]
61
     */
62
    protected array $storeEntities = [];
63

64
    /**
65
     * The entities awaiting to be forgotten.
66
     *
67
     * @var Entity[]
68
     */
69
    protected array $forgetEntities = [];
70

71
    /**
72
     * Repository constructor.
73
     *
74
     * @param Orm                  $orm    The orm manager
75
     * @param Driver               $driver The driver
76
     * @param Cache                $cache  The cache service
77
     * @param class-string<Entity> $entity The entity class name
78
     */
79
    public function __construct(
80
        Orm $orm,
81
        Driver $driver,
82
        protected Cache $cache,
83
        string $entity
84
    ) {
85
        $this->store = $cache->use();
×
86

87
        parent::__construct($orm, $driver, $entity);
×
88
    }
89

90
    /**
91
     * @inheritDoc
92
     */
93
    public function findOne(int|string $id): static
94
    {
95
        parent::findOne($id);
×
96

97
        $this->id = $id;
×
98

99
        return $this;
×
100
    }
101

102
    /**
103
     * @inheritDoc
104
     */
105
    public function where(
106
        string $column,
107
        string|null $operator = null,
108
        mixed $value = null,
109
        bool $setType = true
110
    ): static {
111
        if (! ($value instanceof QueryBuilder) && $column === $this->entity::getIdField()) {
×
112
            $this->id = $value;
×
113
        }
114

115
        parent::where($column, $operator, $value, $setType);
×
116

117
        return $this;
×
118
    }
119

120
    /**
121
     * @inheritDoc
122
     *
123
     * @throws JsonException
124
     */
125
    public function getResult(): array
126
    {
127
        $cacheKey = $this->getCacheKey();
×
128

129
        if (($results = $this->store->get($cacheKey)) !== null && $results !== '') {
×
130
            $results = unserialize(base64_decode($results, true), ['allowed_classes' => true]);
×
131

132
            if (method_exists($this, 'setRelationshipsOnEntities')) {
×
133
                $this->setRelationshipsOnEntities(...$results);
×
134
            }
135

136
            return $results;
×
137
        }
138

139
        $results = $this->retriever->getResult();
×
140

141
        $this->cacheResults($cacheKey, $results);
×
142

143
        if (method_exists($this, 'setRelationshipsOnEntities')) {
×
144
            $this->setRelationshipsOnEntities(...$results);
×
145
        }
146

147
        $this->id = null;
×
148

149
        return $results;
×
150
    }
151

152
    /**
153
     * @inheritDoc
154
     */
155
    public function getOneOrFail(): Entity
156
    {
157
        $results = $this->getOneOrNull();
×
158

159
        if ($results === null) {
×
160
            throw new EntityNotFoundException('Entity Not Found');
×
161
        }
162

163
        return $results;
×
164
    }
165

166
    /**
167
     * @inheritDoc
168
     *
169
     * @throws JsonException
170
     */
171
    public function getCount(): int
172
    {
173
        $cacheKey = $this->getCacheKey();
×
174

175
        if (($results = $this->store->get($cacheKey)) !== null && $results !== '') {
×
176
            return (int) $results;
×
177
        }
178

179
        $results = parent::getCount();
×
180

181
        $this->store->forever($cacheKey, (string) $results);
×
182

183
        $this->store->getTagger($this->entity)->tag($cacheKey);
×
184

185
        return $results;
×
186
    }
187

188
    /**
189
     * @inheritDoc
190
     */
191
    public function create(Entity $entity, bool $defer = true): void
192
    {
193
        parent::create($entity, $defer);
×
194

195
        $this->deferOrCache(StoreType::store, $entity, $defer);
×
196
    }
197

198
    /**
199
     * @inheritDoc
200
     */
201
    public function save(Entity $entity, bool $defer = true): void
202
    {
203
        parent::save($entity, $defer);
×
204

205
        $this->deferOrCache(StoreType::store, $entity, $defer);
×
206
    }
207

208
    /**
209
     * @inheritDoc
210
     */
211
    public function delete(Entity $entity, bool $defer = true): void
212
    {
213
        parent::delete($entity, $defer);
×
214

215
        $this->deferOrCache(StoreType::forget, $entity, $defer);
×
216
    }
217

218
    /**
219
     * @inheritDoc
220
     *
221
     * @param SoftDeleteEntity $entity The entity
222
     */
223
    public function softDelete(SoftDeleteEntity $entity, bool $defer = true): void
224
    {
225
        parent::softDelete($entity, $defer);
×
226

227
        $this->deferOrCache(StoreType::store, $entity, $defer);
×
228
    }
229

230
    /**
231
     * @inheritDoc
232
     *
233
     * @param Entity|null $entity The entity instance to remove
234
     */
235
    public function clear(Entity|null $entity = null): void
236
    {
237
        parent::clear($entity);
×
238

239
        if ($entity === null) {
×
240
            $this->clearDeferred();
×
241

242
            return;
×
243
        }
244

245
        // Get the id of the object
246
        $id = spl_object_id($entity);
×
247

248
        // If the model is set to be stored
249
        if (isset($this->storeEntities[$id])) {
×
250
            // Unset it
251
            unset($this->storeEntities[$id]);
×
252

253
            return;
×
254
        }
255

256
        // If the model is set to be forgotten
257
        if (isset($this->forgetEntities[$id])) {
×
258
            // Unset it
259
            unset($this->forgetEntities[$id]);
×
260
        }
261
    }
262

263
    /**
264
     * @inheritDoc
265
     */
266
    public function persist(): bool
267
    {
268
        $persist = parent::persist();
×
269

270
        $this->persistSave();
×
271
        $this->persistDelete();
×
272
        $this->clearDeferred();
×
273

274
        return $persist;
×
275
    }
276

277
    /**
278
     * Get cache key.
279
     *
280
     * @throws JsonException
281
     *
282
     * @return string
283
     */
284
    protected function getCacheKey(): string
285
    {
286
        return md5(
×
287
            Arr::toString(Obj::getAllProperties($this->retriever))
×
288
            . Arr::toString(Obj::getAllProperties($this->retriever->getQueryBuilder()))
×
289
        );
×
290
    }
291

292
    /**
293
     * Defer or cache.
294
     *
295
     * @param StoreType $type
296
     * @param Entity    $entity
297
     * @param bool      $defer [optional]
298
     *
299
     * @return void
300
     */
301
    protected function deferOrCache(StoreType $type, Entity $entity, bool $defer = true): void
302
    {
303
        if ($defer) {
×
304
            $this->setDeferredEntity($type, $entity);
×
305

306
            return;
×
307
        }
308

309
        $this->forgetEntity($entity);
×
310
    }
311

312
    /**
313
     * Set a deferred entity.
314
     *
315
     * @param StoreType $type
316
     * @param Entity    $entity
317
     *
318
     * @return void
319
     */
320
    protected function setDeferredEntity(StoreType $type, Entity $entity): void
321
    {
322
        $id = spl_object_id($entity);
×
323

324
        match ($type) {
×
NEW
325
            StoreType::store  => $this->storeEntities[$id]  = $entity,
×
326
            StoreType::forget => $this->forgetEntities[$id] = $entity,
×
327
        };
×
328
    }
329

330
    /**
331
     * Forget entity in cache.
332
     *
333
     * @param Entity $entity
334
     *
335
     * @return void
336
     */
337
    protected function forgetEntity(Entity $entity): void
338
    {
339
        $id = $this->getEntityCacheKey($entity);
×
340

341
        $this->store->getTagger($id)->flush();
×
342
    }
343

344
    /**
345
     * Get entity cache key.
346
     *
347
     * @param Entity $entity
348
     *
349
     * @return string
350
     */
351
    protected function getEntityCacheKey(Entity $entity): string
352
    {
353
        return $entity::class . $entity->__get($entity::getIdField());
×
354
    }
355

356
    /**
357
     * Clear deferred entities.
358
     *
359
     * @return void
360
     */
361
    protected function clearDeferred(): void
362
    {
363
        $this->storeEntities  = [];
×
364
        $this->forgetEntities = [];
×
365
    }
366

367
    /**
368
     * Persist entities to be saved.
369
     *
370
     * @return void
371
     */
372
    protected function persistSave(): void
373
    {
374
        foreach ($this->storeEntities as $sid => $entity) {
×
375
            $this->forgetEntity($entity);
×
376

377
            unset($this->storeEntities[$sid]);
×
378
        }
379
    }
380

381
    /**
382
     * Persist entities to be deleted.
383
     *
384
     * @return void
385
     */
386
    protected function persistDelete(): void
387
    {
388
        foreach ($this->forgetEntities as $sid => $entity) {
×
389
            $this->forgetEntity($entity);
×
390

391
            unset($this->forgetEntities[$sid]);
×
392
        }
393
    }
394

395
    /**
396
     * Cache results.
397
     *
398
     * @param string                   $cacheKey
399
     * @param int|Entity|Entity[]|null $results
400
     *
401
     * @return void
402
     */
403
    protected function cacheResults(string $cacheKey, Entity|array|int|null $results): void
404
    {
405
        $id     = $this->id;
×
406
        $tags   = $id !== null && $id !== ''
×
407
            ? [(string) $this->id]
×
408
            : [];
×
409
        $tags[] = $this->entity;
×
410

411
        if (is_array($results)) {
×
412
            $tags = [];
×
413

414
            foreach ($results as $result) {
×
415
                $tags[] = $this->getEntityCacheKey($result);
×
416
            }
417
        }
418

419
        $this->store->forever($cacheKey, base64_encode(serialize($results)));
×
420

421
        $this->store->getTagger(...$tags)->tag($cacheKey);
×
422
    }
423
}
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