Passed
Push — master ( f9418a...1c4364 )
by Kevin
21:24 queued 16:40
created

RepositoryProxy   A

Complexity

Total Complexity 36

Size/Duplication

Total Lines 252
Duplicated Lines 0 %

Test Coverage

Coverage 97.59%

Importance

Changes 3
Bugs 1 Features 1
Metric Value
wmc 36
eloc 59
c 3
b 1
f 1
dl 0
loc 252
ccs 81
cts 83
cp 0.9759
rs 9.52

24 Methods

Rating   Name   Duplication   Size   Complexity  
A __call() 0 3 1
A __construct() 0 3 1
A getCount() 0 7 2
A assertCountGreaterThanOrEqual() 0 5 1
A assertExists() 0 5 1
A assertNotExists() 0 5 1
A assertCountLessThan() 0 5 1
A assertEmpty() 0 3 1
A assertCount() 0 5 1
A assertCountGreaterThan() 0 5 1
A assertCountLessThanOrEqual() 0 5 1
A findAll() 0 3 1
A truncate() 0 9 2
A normalizeCriteria() 0 7 2
A last() 0 3 1
A first() 0 3 1
A random() 0 3 1
A proxyResult() 0 11 4
A find() 0 11 3
A findOneBy() 0 3 1
A getClassName() 0 3 1
A randomRange() 0 19 4
A findBy() 0 3 1
A randomSet() 0 7 2
1
<?php
2
3
namespace Zenstruck\Foundry;
4
5
use Doctrine\ORM\EntityManagerInterface;
6
use Doctrine\ORM\EntityRepository;
7
use Doctrine\Persistence\ObjectRepository;
8
use PHPUnit\Framework\Assert;
9
10
/**
11
 * @mixin EntityRepository
12
 *
13
 * @author Kevin Bond <[email protected]>
14
 */
15
final class RepositoryProxy implements ObjectRepository
16
{
17
    /** @var ObjectRepository */
18
    private $repository;
19
20 272
    public function __construct(ObjectRepository $repository)
21
    {
22 272
        $this->repository = $repository;
23 272
    }
24
25 8
    public function __call(string $method, array $arguments)
26
    {
27 8
        return $this->proxyResult($this->repository->{$method}(...$arguments));
28
    }
29
30 96
    public function getCount(): int
31
    {
32 96
        if ($this->repository instanceof EntityRepository) {
33 96
            return $this->repository->count([]);
34
        }
35
36
        return \count($this->findAll());
37
    }
38
39 32
    public function assertEmpty(string $message = ''): self
40
    {
41 32
        return $this->assertCount(0, $message);
42
    }
43
44 96
    public function assertCount(int $expectedCount, string $message = ''): self
45
    {
46 96
        Assert::assertSame($expectedCount, $this->getCount(), $message);
47
48 96
        return $this;
49
    }
50
51 8
    public function assertCountGreaterThan(int $expected, string $message = ''): self
52
    {
53 8
        Assert::assertGreaterThan($expected, $this->getCount(), $message);
54
55 8
        return $this;
56
    }
57
58 8
    public function assertCountGreaterThanOrEqual(int $expected, string $message = ''): self
59
    {
60 8
        Assert::assertGreaterThanOrEqual($expected, $this->getCount(), $message);
61
62 8
        return $this;
63
    }
64
65 8
    public function assertCountLessThan(int $expected, string $message = ''): self
66
    {
67 8
        Assert::assertLessThan($expected, $this->getCount(), $message);
68
69 8
        return $this;
70
    }
71
72 8
    public function assertCountLessThanOrEqual(int $expected, string $message = ''): self
73
    {
74 8
        Assert::assertLessThanOrEqual($expected, $this->getCount(), $message);
75
76 8
        return $this;
77
    }
78
79
    /**
80
     * @param object|array|mixed $criteria
81
     */
82 16
    public function assertExists($criteria, string $message = ''): self
83
    {
84 16
        Assert::assertNotNull($this->find($criteria), $message);
85
86 16
        return $this;
87
    }
88
89
    /**
90
     * @param object|array|mixed $criteria
91
     */
92 8
    public function assertNotExists($criteria, string $message = ''): self
93
    {
94 8
        Assert::assertNull($this->find($criteria), $message);
95
96 8
        return $this;
97
    }
98
99
    /**
100
     * @return Proxy|object|null
101
     */
102 16
    public function first(string $sortedField = 'id'): ?Proxy
103
    {
104 16
        return $this->findBy([], [$sortedField => 'ASC'], 1)[0] ?? null;
105
    }
106
107
    /**
108
     * @return Proxy|object|null
109
     */
110 16
    public function last(string $sortedField = 'id'): ?Proxy
111
    {
112 16
        return $this->findBy([], [$sortedField => 'DESC'], 1)[0] ?? null;
113
    }
114
115
    /**
116
     * Remove all rows.
117
     */
118 8
    public function truncate(): void
119
    {
120 8
        $om = Factory::configuration()->objectManagerFor($this->getClassName());
121
122 8
        if (!$om instanceof EntityManagerInterface) {
123
            throw new \RuntimeException('This operation is only available when using doctrine/orm');
124
        }
125
126 8
        $om->createQuery("DELETE {$this->getClassName()} e")->execute();
127 8
    }
128
129
    /**
130
     * Fetch one random object.
131
     *
132
     * @return Proxy|object
133
     *
134
     * @throws \RuntimeException if no objects are persisted
135
     */
136 32
    public function random(): Proxy
137
    {
138 32
        return $this->randomSet(1)[0];
139
    }
140
141
    /**
142
     * Fetch a random set of objects.
143
     *
144
     * @param int $number The number of objects to return
145
     *
146
     * @return Proxy[]|object[]
147
     *
148
     * @throws \RuntimeException         if not enough persisted objects to satisfy the number requested
149
     * @throws \InvalidArgumentException if number is less than zero
150
     */
151 64
    public function randomSet(int $number): array
152
    {
153 64
        if ($number < 0) {
154 8
            throw new \InvalidArgumentException(\sprintf('$number must be positive (%d given).', $number));
155
        }
156
157 56
        return $this->randomRange($number, $number);
158
    }
159
160
    /**
161
     * Fetch a random range of objects.
162
     *
163
     * @param int $min The minimum number of objects to return
164
     * @param int $max The maximum number of objects to return
165
     *
166
     * @return Proxy[]|object[]
167
     *
168
     * @throws \RuntimeException         if not enough persisted objects to satisfy the max
169
     * @throws \InvalidArgumentException if min is less than zero
170
     * @throws \InvalidArgumentException if max is less than min
171
     */
172 96
    public function randomRange(int $min, int $max): array
173
    {
174 96
        if ($min < 0) {
175 8
            throw new \InvalidArgumentException(\sprintf('$min must be positive (%d given).', $min));
176
        }
177
178 88
        if ($max < $min) {
179 8
            throw new \InvalidArgumentException(\sprintf('$max (%d) cannot be less than $min (%d).', $max, $min));
180
        }
181
182 80
        $all = \array_values($this->findAll());
183
184 80
        \shuffle($all);
185
186 80
        if (\count($all) < $max) {
187 24
            throw new \RuntimeException(\sprintf('At least %d "%s" object(s) must have been persisted (%d persisted).', $max, $this->getClassName(), \count($all)));
188
        }
189
190 56
        return \array_slice($all, 0, \random_int($min, $max));
191
    }
192
193
    /**
194
     * @param object|array|mixed $criteria
195
     *
196
     * @return Proxy|object|null
197
     */
198 32
    public function find($criteria): ?Proxy
199
    {
200 32
        if ($criteria instanceof Proxy) {
201 8
            $criteria = $criteria->object();
202
        }
203
204 32
        if (!\is_array($criteria)) {
205 8
            return $this->proxyResult($this->repository->find($criteria));
206
        }
207
208 32
        return $this->findOneBy($criteria);
209
    }
210
211
    /**
212
     * @return Proxy[]|object[]
213
     */
214 88
    public function findAll(): array
215
    {
216 88
        return $this->proxyResult($this->repository->findAll());
217
    }
218
219
    /**
220
     * @return Proxy[]|object[]
221
     */
222 24
    public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array
223
    {
224 24
        return $this->proxyResult($this->repository->findBy(self::normalizeCriteria($criteria), $orderBy, $limit, $offset));
225
    }
226
227
    /**
228
     * @param array|null $orderBy Doctrine\ORM\EntityRepository adds this optional parameter
229
     *
230
     * @return Proxy|object|null
231
     */
232 40
    public function findOneBy(array $criteria, ?array $orderBy = null): ?Proxy
233
    {
234 40
        return $this->proxyResult($this->repository->findOneBy(self::normalizeCriteria($criteria), $orderBy));
0 ignored issues
show
Unused Code introduced by
The call to Doctrine\Persistence\ObjectRepository::findOneBy() has too many arguments starting with $orderBy. ( Ignorable by Annotation )

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

234
        return $this->proxyResult($this->repository->/** @scrutinizer ignore-call */ findOneBy(self::normalizeCriteria($criteria), $orderBy));

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
235
    }
236
237 144
    public function getClassName(): string
238
    {
239 144
        return $this->repository->getClassName();
240
    }
241
242
    /**
243
     * @param mixed $result
244
     *
245
     * @return Proxy|Proxy[]|object|object[]|mixed
246
     */
247 152
    private function proxyResult($result)
248
    {
249 152
        if (\is_object($result) && \is_a($result, $this->getClassName())) {
250 128
            return Proxy::createFromPersisted($result);
251
        }
252
253 128
        if (\is_array($result)) {
254 104
            return \array_map([$this, 'proxyResult'], $result);
255
        }
256
257 24
        return $result;
258
    }
259
260 64
    private static function normalizeCriteria(array $criteria): array
261
    {
262 64
        return \array_map(
263
            function($value) {
264 32
                return $value instanceof Proxy ? $value->object() : $value;
265 64
            },
266 64
            $criteria
267
        );
268
    }
269
}
270