Repository   A
last analyzed

Complexity

Total Complexity 35

Size/Duplication

Total Lines 206
Duplicated Lines 0 %

Test Coverage

Coverage 80.26%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 61
c 2
b 0
f 0
dl 0
loc 206
ccs 61
cts 76
cp 0.8026
rs 9.6
wmc 35

12 Methods

Rating   Name   Duplication   Size   Complexity  
A findOneBy() 0 5 2
A getClassName() 0 3 1
A matchCriteria() 0 12 5
A __construct() 0 10 2
A findByCriteria() 0 5 1
A find() 0 13 4
A findBy() 0 21 5
A applyOrderBy() 0 20 6
A load() 0 12 5
A createEntity() 0 3 1
A findAll() 0 5 1
A count() 0 11 2
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Derafu: Biblioteca PHP (Núcleo).
7
 * Copyright (C) Derafu <https://www.derafu.org>
8
 *
9
 * Este programa es software libre: usted puede redistribuirlo y/o modificarlo
10
 * bajo los términos de la Licencia Pública General Affero de GNU publicada por
11
 * la Fundación para el Software Libre, ya sea la versión 3 de la Licencia, o
12
 * (a su elección) cualquier versión posterior de la misma.
13
 *
14
 * Este programa se distribuye con la esperanza de que sea útil, pero SIN
15
 * GARANTÍA ALGUNA; ni siquiera la garantía implícita MERCANTIL o de APTITUD
16
 * PARA UN PROPÓSITO DETERMINADO. Consulte los detalles de la Licencia Pública
17
 * General Affero de GNU para obtener una información más detallada.
18
 *
19
 * Debería haber recibido una copia de la Licencia Pública General Affero de GNU
20
 * junto a este programa.
21
 *
22
 * En caso contrario, consulte <http://www.gnu.org/licenses/agpl.html>.
23
 */
24
25
namespace Derafu\Lib\Core\Support\Store;
26
27
use ArrayAccess;
28
use ArrayObject;
29
use Derafu\Lib\Core\Helper\Arr;
30
use Derafu\Lib\Core\Helper\Factory;
31
use Derafu\Lib\Core\Support\Store\Abstract\AbstractStore;
32
use Derafu\Lib\Core\Support\Store\Contract\RepositoryInterface;
33
use Doctrine\Common\Collections\Criteria;
34
use InvalidArgumentException;
35
use stdClass;
36
37
/**
38
 * Clase para repositorios de objetos/entidades.
39
 *
40
 * Proporciona métodos estándar para acceder y buscar objetos/entidades desde
41
 * una fuente de datos.
42
 */
43
class Repository extends AbstractStore implements RepositoryInterface
44
{
45
    /**
46
     * Clase de la entidad donde se colocarán los datos que se obtengan a través
47
     * del repositorio.
48
     *
49
     * @var string
50
     */
51
    protected string $entityClass = stdClass::class;
52
53
    /**
54
     * Constructor del repositorio.
55
     *
56
     * @param string|array|ArrayAccess $source Arreglo de datos o ruta al archivo PHP.
57
     * @param string|null $entityClass Clase de la entidad que este repositorio usa.
58
     * @param string|null $idAttribute Nombre del atributo ID que se debe
59
     * agregar a los datos cuando se carga el repositorio.
60
     */
61 9
    public function __construct(
62
        string|array|ArrayAccess $source,
63
        ?string $entityClass = null,
64
        ?string $idAttribute = null
65
    ) {
66 9
        if ($entityClass !== null) {
67
            $this->entityClass = $entityClass;
68
        }
69
70 9
        $this->load($source, $idAttribute);
71
    }
72
73
    /**
74
     * Carga los datos del repositorio.
75
     *
76
     * @param string|array|ArrayAccess|ArrayObject $source
77
     * @param string|null $idAttribute
78
     * @return void
79
     */
80 9
    protected function load(
81
        string|array|ArrayAccess|ArrayObject $source,
82
        ?string $idAttribute = null
83
    ): void {
84 9
        $data = is_string($source) ? require $source : $source;
0 ignored issues
show
introduced by
The condition is_string($source) is always false.
Loading history...
85 9
        if (!is_array($data)) {
0 ignored issues
show
introduced by
The condition is_array($data) is always true.
Loading history...
86
            $data = $this->createFrom($data)->toArray();
87
        }
88 9
        if ($idAttribute && is_array($data)) {
89 9
            $data = Arr::addIdAttribute($source, $idAttribute);
90
        }
91 9
        $this->data = $this->createFrom($data);
92
    }
93
94
    /**
95
     * {@inheritDoc}
96
     */
97 2
    public function find($id, $lockMode = null, $lockVersion = null): ?object
98
    {
99 2
        if (!is_string($id) && !is_int($id)) {
100
            throw new InvalidArgumentException(sprintf(
101
                'En el método %s:find($id) se pasó un $id de tipo %s y solo se permiten string e int.',
102
                static::class,
103
                get_debug_type($id)
104
            ));
105
        }
106
107 2
        return isset($this->data[$id])
108 1
            ? $this->createEntity($this->data[$id])
0 ignored issues
show
Bug introduced by
$this->data[$id] of type Doctrine\Common\Collections\T|null is incompatible with the type array expected by parameter $data of Derafu\Lib\Core\Support\...ository::createEntity(). ( Ignorable by Annotation )

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

108
            ? $this->createEntity(/** @scrutinizer ignore-type */ $this->data[$id])
Loading history...
109 2
            : null
110 2
        ;
111
    }
112
113
    /**
114
     * {@inheritDoc}
115
     */
116 1
    public function findAll(): array
117
    {
118 1
        return array_values($this->data->map(
119 1
            fn ($item) => $this->createEntity($item)
120 1
        )->toArray());
121
    }
122
123
    /**
124
     * {@inheritDoc}
125
     */
126 4
    public function findBy(
127
        array $criteria,
128
        ?array $orderBy = null,
129
        ?int $limit = null,
130
        ?int $offset = null
131
    ): array {
132 4
        $results = $this->data->filter(
133 4
            fn ($item) => $this->matchCriteria($item, $criteria)
134 4
        )->toArray();
135
136 4
        if ($orderBy) {
137 2
            $results = $this->applyOrderBy($results, $orderBy);
138
        }
139
140 4
        if ($offset !== null || $limit !== null) {
141 3
            $results = array_slice($results, $offset ?: 0, $limit);
142
        }
143
144 4
        return array_values(array_map(
145 4
            fn ($item) => $this->createEntity($item),
146 4
            $results
147 4
        ));
148
    }
149
150
    /**
151
     * {@inheritDoc}
152
     */
153 2
    public function findOneBy(array $criteria, ?array $orderBy = null): ?object
154
    {
155 2
        $results = $this->findBy($criteria, $orderBy, 1);
156
157 2
        return empty($results) ? null : reset($results);
158
    }
159
160
    /**
161
     * {@inheritDoc}
162
     */
163 1
    public function count(array $criteria = []): int
164
    {
165 1
        if (empty($criteria)) {
166 1
            return count($this->data);
167
        }
168
169
        $results = $this->data->filter(
170
            fn ($item) => $this->matchCriteria($item, $criteria)
171
        );
172
173
        return $results->count();
174
    }
175
176
    /**
177
     * {@inheritDoc}
178
     */
179 1
    public function findByCriteria(Criteria $criteria): array
180
    {
181 1
        return parent::matching($criteria)->map(
182 1
            fn ($item) => $this->createEntity($item)
183 1
        )->getValues();
184
    }
185
186
    /**
187
     * {@inheritDoc}
188
     */
189
    public function getClassName(): string
190
    {
191
        return $this->entityClass;
192
    }
193
194
    /**
195
     * Verifica si un item cumple con los criterios de búsqueda.
196
     */
197 4
    protected function matchCriteria(array $item, array $criteria): bool
198
    {
199 4
        foreach ($criteria as $field => $value) {
200 4
            if (!is_array($value)) {
201 4
                $value = [$value];
202
            }
203 4
            if (!isset($item[$field]) || !in_array($item[$field], $value)) {
204 4
                return false;
205
            }
206
        }
207
208 3
        return true;
209
    }
210
211
    /**
212
     * Ordena los resultados según los criterios especificados.
213
     *
214
     * @param array $results Resultados a ordenar.
215
     * @param array $orderBy Criterios de ordenamiento ['campo' => 'ASC|DESC'].
216
     * @return array Resultados ordenados.
217
     */
218 2
    protected function applyOrderBy(array $results, array $orderBy): array
219
    {
220 2
        uasort($results, function ($a, $b) use ($orderBy) {
221 2
            foreach ($orderBy as $field => $direction) {
222 2
                if (!isset($a[$field]) || !isset($b[$field])) {
223
                    continue;
224
                }
225
226 2
                $compare = $direction === 'DESC'
227 1
                    ? -1 * ($a[$field] <=> $b[$field])
228 1
                    : $a[$field] <=> $b[$field];
229
230 2
                if ($compare !== 0) {
231 2
                    return $compare;
232
                }
233
            }
234
            return 0;
235 2
        });
236
237 2
        return $results;
238
    }
239
240
    /**
241
     * Crea una entidad a partir de los datos.
242
     *
243
     * @param array $data Datos que se asignarán a la entidad.
244
     * @return object Instancia de la entidad con los datos cargados.
245
     */
246 6
    protected function createEntity(array $data): object
247
    {
248 6
        return Factory::create($data, $this->entityClass);
249
    }
250
}
251