Passed
Pull Request — master (#1)
by Kevin
12:50
created

RepositoryProxy::assertCount()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 6
ccs 3
cts 3
cp 1
crap 1
rs 10
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 92
    public function __construct(ObjectRepository $repository)
21
    {
22 92
        $this->repository = $repository;
23 92
    }
24
25 4
    public function __call(string $method, array $arguments)
26
    {
27 4
        return $this->proxyResult($this->repository->{$method}(...$arguments));
28
    }
29
30 20
    public function getCount(): int
31
    {
32 20
        if ($this->repository instanceof EntityRepository) {
33 20
            return $this->repository->count([]);
34
        }
35
36
        return \count($this->findAll());
37
    }
38
39 8
    public function assertEmpty(): self
40
    {
41 8
        return $this->assertCount(0);
42
    }
43
44 20
    public function assertCount(int $expectedCount): self
45
    {
46
        // todo add message
47 20
        Assert::assertSame($expectedCount, $this->getCount());
48
49 20
        return $this;
50
    }
51
52 4
    public function assertCountGreaterThan(int $expected): self
53
    {
54
        // todo add message
55 4
        Assert::assertGreaterThan($expected, $this->getCount());
56
57 4
        return $this;
58
    }
59
60 4
    public function assertCountGreaterThanOrEqual(int $expected): self
61
    {
62
        // todo add message
63 4
        Assert::assertGreaterThanOrEqual($expected, $this->getCount());
64
65 4
        return $this;
66
    }
67
68 4
    public function assertCountLessThan(int $expected): self
69
    {
70
        // todo add message
71 4
        Assert::assertLessThan($expected, $this->getCount());
72
73 4
        return $this;
74
    }
75
76 4
    public function assertCountLessThanOrEqual(int $expected): self
77
    {
78
        // todo add message
79 4
        Assert::assertLessThanOrEqual($expected, $this->getCount());
80
81 4
        return $this;
82
    }
83
84
    /**
85
     * @param object|array|mixed $criteria
86
     */
87 8
    public function assertExists($criteria): self
88
    {
89
        // todo add message
90 8
        Assert::assertNotNull($this->find($criteria));
91
92 8
        return $this;
93
    }
94
95
    /**
96
     * @param object|array|mixed $criteria
97
     */
98 4
    public function assertNotExists($criteria): self
99
    {
100
        // todo add message
101 4
        Assert::assertNull($this->find($criteria));
102
103 4
        return $this;
104
    }
105
106
    /**
107
     * @return Proxy|object|null
108
     */
109 4
    public function first(): ?Proxy
110
    {
111 4
        return $this->findOneBy([]);
112
    }
113
114
    /**
115
     * Remove all rows.
116
     */
117 4
    public function truncate(): void
118
    {
119 4
        $om = Factory::configuration()->objectManagerFor($this->getClassName());
120
121 4
        if (!$om instanceof EntityManagerInterface) {
122
            throw new \RuntimeException('This operation is only available when using doctrine/orm');
123
        }
124
125 4
        $om->createQuery("DELETE {$this->getClassName()} e")->execute();
126 4
    }
127
128
    /**
129
     * Fetch one random object.
130
     *
131
     * @return Proxy|object
132
     *
133
     * @throws \RuntimeException if no objects are persisted
134
     */
135 12
    public function random(): Proxy
136
    {
137 12
        return $this->randomSet(1)[0];
138
    }
139
140
    /**
141
     * Fetch a random set of objects.
142
     *
143
     * @param int $number The number of objects to return
144
     *
145
     * @return Proxy[]|object[]
146
     *
147
     * @throws \RuntimeException         if not enough persisted objects to satisfy the number requested
148
     * @throws \InvalidArgumentException if number is less than zero
149
     */
150 28
    public function randomSet(int $number): array
151
    {
152 28
        if ($number < 0) {
153 4
            throw new \InvalidArgumentException(\sprintf('$number must be positive (%d given).', $number));
154
        }
155
156 24
        return $this->randomRange($number, $number);
157
    }
158
159
    /**
160
     * Fetch a random range of objects.
161
     *
162
     * @param int $min The minimum number of objects to return
163
     * @param int $max The maximum number of objects to return
164
     *
165
     * @return Proxy[]|object[]
166
     *
167
     * @throws \RuntimeException         if not enough persisted objects to satisfy the max
168
     * @throws \InvalidArgumentException if min is less than zero
169
     * @throws \InvalidArgumentException if max is less than min
170
     */
171 44
    public function randomRange(int $min, int $max): array
172
    {
173 44
        if ($min < 0) {
174 4
            throw new \InvalidArgumentException(\sprintf('$min must be positive (%d given).', $min));
175
        }
176
177 40
        if ($max < $min) {
178 4
            throw new \InvalidArgumentException(\sprintf('$max (%d) cannot be less than $min (%d).', $max, $min));
179
        }
180
181 36
        $all = \array_values($this->findAll());
182
183 36
        \shuffle($all);
184
185 36
        if (\count($all) < $max) {
186 12
            throw new \RuntimeException(\sprintf('At least %d "%s" object(s) must have been persisted (%d persisted).', $max, $this->getClassName(), \count($all)));
187
        }
188
189 24
        return \array_slice($all, 0, \random_int($min, $max));
190
    }
191
192
    /**
193
     * @param object|array|mixed $criteria
194
     *
195
     * @return Proxy|object|null
196
     */
197 16
    public function find($criteria): ?Proxy
198
    {
199 16
        if ($criteria instanceof Proxy) {
200 4
            $criteria = $criteria->object();
201
        }
202
203 16
        if (!\is_array($criteria)) {
204 4
            return $this->proxyResult($this->repository->find($criteria));
205
        }
206
207 16
        return $this->findOneBy($criteria);
208
    }
209
210
    /**
211
     * @return Proxy[]|object[]
212
     */
213 40
    public function findAll(): array
214
    {
215 40
        return $this->proxyResult($this->repository->findAll());
216
    }
217
218
    /**
219
     * @return Proxy[]|object[]
220
     */
221 4
    public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array
222
    {
223 4
        return $this->proxyResult($this->repository->findBy(self::normalizeCriteria($criteria), $orderBy, $limit, $offset));
224
    }
225
226
    /**
227
     * @return Proxy|object|null
228
     */
229 20
    public function findOneBy(array $criteria): ?Proxy
230
    {
231 20
        return $this->proxyResult($this->repository->findOneBy(self::normalizeCriteria($criteria)));
232
    }
233
234 60
    public function getClassName(): string
235
    {
236 60
        return $this->repository->getClassName();
237
    }
238
239
    /**
240
     * @param mixed $result
241
     *
242
     * @return Proxy|Proxy[]|object|object[]|mixed
243
     */
244 60
    private function proxyResult($result)
245
    {
246 60
        if (\is_object($result) && $this->getClassName() === \get_class($result)) {
247 52
            return Proxy::persisted($result);
248
        }
249
250 52
        if (\is_array($result)) {
251 40
            return \array_map([$this, 'proxyResult'], $result);
252
        }
253
254 12
        return $result;
255
    }
256
257 20
    private static function normalizeCriteria(array $criteria): array
258
    {
259 20
        return \array_map(
260
            function($value) {
261 16
                return $value instanceof Proxy ? $value->object() : $value;
262 20
            },
263 20
            $criteria
264
        );
265
    }
266
}
267