Passed
Pull Request — master (#1578)
by Tarmo
10:46
created

BaseRepository   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 186
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 59
dl 0
loc 186
ccs 55
cts 55
cp 1
rs 10
c 0
b 0
f 0
wmc 19

11 Methods

Rating   Name   Duplication   Size   Complexity  
A addInnerJoin() 0 7 2
A addLeftJoin() 0 7 2
A addCallback() 0 11 2
A getSearchColumns() 0 3 1
A getEntityName() 0 3 1
A save() 0 12 2
A remove() 0 12 2
A processCallbacks() 0 9 2
A addJoinToQuery() 0 8 2
A processJoins() 0 9 2
A processQueryBuilder() 0 11 1
1
<?php
2
declare(strict_types = 1);
3
/**
4
 * /src/Repository/BaseRepository.php
5
 *
6
 * @author TLe, Tarmo Leppänen <[email protected]>
7
 */
8
9
namespace App\Repository;
10
11
use App\Entity\Interfaces\EntityInterface;
12
use App\Repository\Interfaces\BaseRepositoryInterface;
13
use App\Repository\Traits\RepositoryMethodsTrait;
14
use App\Repository\Traits\RepositoryWrappersTrait;
15
use Doctrine\ORM\EntityManager;
16
use Doctrine\ORM\QueryBuilder;
17
use Doctrine\Persistence\ManagerRegistry;
18
use function array_map;
19
use function array_merge;
20
use function array_unshift;
21
use function implode;
22
use function in_array;
23
use function serialize;
24
use function sha1;
25
use function spl_object_hash;
26
27
/**
28
 * Class BaseRepository
29
 *
30
 * @package App\Repository
31
 * @author TLe, Tarmo Leppänen <[email protected]>
32
 */
33
abstract class BaseRepository implements BaseRepositoryInterface
34
{
35
    use RepositoryMethodsTrait;
36
    use RepositoryWrappersTrait;
37
38
    private const INNER_JOIN = 'innerJoin';
39
    private const LEFT_JOIN = 'leftJoin';
40
41
    /**
42
     * @psalm-var class-string
43
     */
44
    protected static string $entityName;
45
46
    /**
47
     * @var array<int, string>
48
     */
49
    protected static array $searchColumns = [];
50
    protected static EntityManager $entityManager;
51
52
    protected ManagerRegistry $managerRegistry;
53
54
    /**
55
     * Joins that need to attach to queries, this is needed for to prevent duplicate joins on those.
56
     *
57
     * @var array<string, array<array<int, scalar>>>
58
     */
59
    private static array $joins = [
60
        self::INNER_JOIN => [],
61
        self::LEFT_JOIN => [],
62
    ];
63
64
    /**
65
     * @var array<string, array<int, string>>
66
     */
67
    private static array $processedJoins = [
68
        self::INNER_JOIN => [],
69
        self::LEFT_JOIN => [],
70
    ];
71
72
    /**
73
     * @var array<int, array{0: callable, 1: array<mixed>}>
74
     */
75
    private static array $callbacks = [];
76
77
    /**
78
     * @var array<int, string>
79
     */
80
    private static array $processedCallbacks = [];
81
82
    /**
83
     * @psalm-return class-string
84
     */
85 711
    public function getEntityName(): string
86
    {
87 711
        return static::$entityName;
88
    }
89
90 156
    public function getSearchColumns(): array
91
    {
92 156
        return static::$searchColumns;
93
    }
94
95 546
    public function save(EntityInterface $entity, ?bool $flush = null): self
96
    {
97 546
        $flush ??= true;
98
99
        // Persist on database
100 546
        $this->getEntityManager()->persist($entity);
101
102 546
        if ($flush) {
103 546
            $this->getEntityManager()->flush();
104
        }
105
106 544
        return $this;
107
    }
108
109 13
    public function remove(EntityInterface $entity, ?bool $flush = null): self
110
    {
111 13
        $flush ??= true;
112
113
        // Remove from database
114 13
        $this->getEntityManager()->remove($entity);
115
116 13
        if ($flush) {
117 13
            $this->getEntityManager()->flush();
118
        }
119
120 13
        return $this;
121
    }
122
123 156
    public function processQueryBuilder(QueryBuilder $queryBuilder): void
124
    {
125
        // Reset processed joins and callbacks
126 156
        self::$processedJoins = [
127
            self::INNER_JOIN => [],
128
            self::LEFT_JOIN => [],
129
        ];
130 156
        self::$processedCallbacks = [];
131
132 156
        $this->processJoins($queryBuilder);
133 156
        $this->processCallbacks($queryBuilder);
134 156
    }
135
136 6
    public function addLeftJoin(array $parameters): self
137
    {
138 6
        if ($parameters !== []) {
139 4
            $this->addJoinToQuery(self::LEFT_JOIN, $parameters);
140
        }
141
142 6
        return $this;
143
    }
144
145 6
    public function addInnerJoin(array $parameters): self
146
    {
147 6
        if ($parameters !== []) {
148 4
            $this->addJoinToQuery(self::INNER_JOIN, $parameters);
149
        }
150
151 6
        return $this;
152
    }
153
154 2
    public function addCallback(callable $callable, ?array $args = null): self
155
    {
156 2
        $args ??= [];
157 2
        $hash = sha1(serialize(array_merge([spl_object_hash((object)$callable)], $args)));
158
159 2
        if (!in_array($hash, self::$processedCallbacks, true)) {
160 2
            self::$callbacks[] = [$callable, $args];
161 2
            self::$processedCallbacks[] = $hash;
162
        }
163
164 2
        return $this;
165
    }
166
167
    /**
168
     * Process defined joins for current QueryBuilder instance.
169
     */
170 156
    protected function processJoins(QueryBuilder $queryBuilder): void
171
    {
172 156
        foreach (self::$joins as $joinType => $joins) {
173 156
            array_map(
174 156
                static fn (array $joinParameters): QueryBuilder => $queryBuilder->{$joinType}(...$joinParameters),
175 156
                $joins,
176
            );
177
178 156
            self::$joins[$joinType] = [];
179
        }
180 156
    }
181
182
    /**
183
     * Process defined callbacks for current QueryBuilder instance.
184
     */
185 156
    protected function processCallbacks(QueryBuilder $queryBuilder): void
186
    {
187 156
        foreach (self::$callbacks as [$callback, $args]) {
188 2
            array_unshift($args, $queryBuilder);
189
190 2
            $callback(...$args);
191
        }
192
193 156
        self::$callbacks = [];
194 156
    }
195
196
    /**
197
     * Method to add defined join(s) to current QueryBuilder query. This will
198
     * keep track of attached join(s) so any of those are not added multiple
199
     * times to QueryBuilder.
200
     *
201
     * @note processJoins() method must be called for joins to actually be
202
     *       added to QueryBuilder. processQueryBuilder() method calls this
203
     *       method automatically.
204
     *
205
     * @see QueryBuilder::leftJoin()
206
     * @see QueryBuilder::innerJoin()
207
     *
208
     * @param string $type Join type; leftJoin, innerJoin or join
209
     * @param array<int, scalar> $parameters Query builder join parameters
210
     */
211 8
    private function addJoinToQuery(string $type, array $parameters): void
212
    {
213 8
        $comparison = implode('|', $parameters);
214
215 8
        if (!in_array($comparison, self::$processedJoins[$type], true)) {
216 8
            self::$joins[$type][] = $parameters;
217
218 8
            self::$processedJoins[$type][] = $comparison;
219
        }
220 8
    }
221
}
222