Completed
Push — master ( 7b59b8...7688b3 )
by Tarmo
17s queued 11s
created

BaseRepository::processJoins()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 5
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 9
ccs 6
cts 6
cp 1
crap 2
rs 10
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;
0 ignored issues
show
Bug introduced by
The type App\Repository\Traits\RepositoryWrappersTrait was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

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