Passed
Pull Request — master (#1)
by Kevin
02:39
created

RepositoryProxy::randomSet()   A

Complexity

Conditions 5
Paths 8

Size

Total Lines 23
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 5

Importance

Changes 0
Metric Value
cc 5
eloc 11
nc 8
nop 2
dl 0
loc 23
ccs 12
cts 12
cp 1
crap 5
rs 9.6111
c 0
b 0
f 0
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
    private ObjectRepository $repository;
18
19 84
    public function __construct(ObjectRepository $repository)
20
    {
21 84
        $this->repository = $repository;
22 84
    }
23
24 4
    public function __call(string $method, array $arguments)
25
    {
26 4
        return $this->proxyResult($this->repository->{$method}(...$arguments));
27
    }
28
29 20
    public function getCount(): int
30
    {
31 20
        if ($this->repository instanceof EntityRepository) {
32 20
            return $this->repository->count([]);
0 ignored issues
show
Bug introduced by
The method count() does not exist on Doctrine\Persistence\ObjectRepository. It seems like you code against a sub-type of said class. However, the method does not exist in Doctrine\Common\Persistence\ObjectRepository. Are you sure you never get one of those? ( Ignorable by Annotation )

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

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