Skip to content

Commit 9275c2a

Browse files
authored
Merge pull request #1 from open-solid/collection
Add resource management classes for improved collection handling
2 parents bf675bf + 7c7e9e1 commit 9275c2a

6 files changed

Lines changed: 589 additions & 0 deletions

File tree

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenSolid\Core\Domain\Model;
6+
7+
/**
8+
* @template T of object
9+
*/
10+
final readonly class GetOrCreateResource
11+
{
12+
/**
13+
* @template TInstance of object
14+
*
15+
* @param TInstance $resource
16+
*
17+
* @return self<TInstance>
18+
*/
19+
public static function created(object $resource): self
20+
{
21+
return new self($resource, created: true);
22+
}
23+
24+
/**
25+
* @template TInstance of object
26+
*
27+
* @param TInstance $resource
28+
*
29+
* @return self<TInstance>
30+
*/
31+
public static function existing(object $resource): self
32+
{
33+
return new self($resource, existing: true);
34+
}
35+
36+
/**
37+
* @param T $resource
38+
*/
39+
private function __construct(
40+
public object $resource,
41+
public bool $created = false,
42+
public bool $existing = false,
43+
) {
44+
}
45+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenSolid\Core\Domain\Repository;
6+
7+
use Doctrine\Common\Collections\ReadableCollection;
8+
use Doctrine\Common\Collections\Selectable;
9+
10+
/**
11+
* @template TKey of array-key
12+
* @template TValue of object
13+
*
14+
* @extends ReadableCollection<TKey, TValue>
15+
* @extends Selectable<TKey, TValue>
16+
*/
17+
interface Collection extends ReadableCollection, Selectable
18+
{
19+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenSolid\Core\Domain\Repository;
6+
7+
use Doctrine\Common\Collections\ArrayCollection;
8+
9+
/**
10+
* @template TKey of array-key
11+
* @template TValue of object
12+
*
13+
* @extends ArrayCollection<TKey, TValue>
14+
*
15+
* @implements Collection<TKey, TValue>
16+
*/
17+
class InMemoryCollection extends ArrayCollection implements Collection
18+
{
19+
}
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenSolid\Core\Domain\Repository;
6+
7+
use Doctrine\Common\Collections\Collection as DoctrineCollection;
8+
use Doctrine\Common\Collections\Criteria;
9+
use Doctrine\Common\Collections\Selectable;
10+
11+
/**
12+
* @template TKey of array-key
13+
* @template TValue of object
14+
*
15+
* @implements Collection<TKey, TValue>
16+
*/
17+
final readonly class ReadonlyCollection implements Collection
18+
{
19+
/**
20+
* @param DoctrineCollection<TKey, TValue> $collection
21+
*/
22+
public function __construct(
23+
private DoctrineCollection $collection,
24+
) {
25+
}
26+
27+
public function getIterator(): \Traversable
28+
{
29+
return $this->collection->getIterator();
30+
}
31+
32+
public function count(): int
33+
{
34+
return $this->collection->count();
35+
}
36+
37+
/** @phpstan-assert-if-true TValue $element */
38+
public function contains(mixed $element): bool
39+
{
40+
return $this->collection->contains($element);
41+
}
42+
43+
public function isEmpty(): bool
44+
{
45+
return $this->collection->isEmpty();
46+
}
47+
48+
public function containsKey(int|string $key): bool
49+
{
50+
return $this->collection->containsKey($key);
51+
}
52+
53+
public function get(int|string $key): mixed
54+
{
55+
return $this->collection->get($key);
56+
}
57+
58+
public function getKeys(): array
59+
{
60+
return $this->collection->getKeys();
61+
}
62+
63+
public function getValues(): array
64+
{
65+
return $this->collection->getValues();
66+
}
67+
68+
public function toArray(): array
69+
{
70+
return $this->collection->toArray();
71+
}
72+
73+
public function first(): mixed
74+
{
75+
return $this->collection->first();
76+
}
77+
78+
public function last(): mixed
79+
{
80+
return $this->collection->last();
81+
}
82+
83+
public function key(): int|string|null
84+
{
85+
return $this->collection->key();
86+
}
87+
88+
public function current(): mixed
89+
{
90+
return $this->collection->current();
91+
}
92+
93+
public function next(): mixed
94+
{
95+
return $this->collection->next();
96+
}
97+
98+
public function slice(int $offset, ?int $length = null): array
99+
{
100+
return $this->collection->slice($offset, $length);
101+
}
102+
103+
public function exists(\Closure $p): bool
104+
{
105+
return $this->collection->exists($p);
106+
}
107+
108+
/**
109+
* @return self<TKey, TValue>
110+
*/
111+
public function filter(\Closure $p): self
112+
{
113+
return new self($this->collection->filter($p));
114+
}
115+
116+
/**
117+
* @template U of object
118+
*
119+
* @return self<TKey, U>
120+
*/
121+
public function map(\Closure $func): self
122+
{
123+
/** @var DoctrineCollection<TKey, U> $collection */
124+
$collection = $this->collection->map($func);
125+
126+
return new self($collection);
127+
}
128+
129+
public function partition(\Closure $p): array
130+
{
131+
return $this->collection->partition($p);
132+
}
133+
134+
public function forAll(\Closure $p): bool
135+
{
136+
return $this->collection->forAll($p);
137+
}
138+
139+
/** @phpstan-assert-if-true TValue $element */
140+
public function indexOf(mixed $element): int|string|false
141+
{
142+
return $this->collection->indexOf($element);
143+
}
144+
145+
public function findFirst(\Closure $p): mixed
146+
{
147+
return $this->collection->findFirst($p);
148+
}
149+
150+
public function reduce(\Closure $func, mixed $initial = null): mixed
151+
{
152+
return $this->collection->reduce($func, $initial);
153+
}
154+
155+
/**
156+
* @return self<TKey, TValue>
157+
*/
158+
public function matching(Criteria $criteria): self
159+
{
160+
if (!$this->collection instanceof Selectable) {
161+
throw new \LogicException(sprintf('Expected a selectable collection, got "%s".', $this->collection::class));
162+
}
163+
164+
/** @var DoctrineCollection<TKey, TValue>&Selectable<TKey, TValue> $collection */
165+
$collection = $this->collection->matching($criteria);
166+
167+
return new self($collection);
168+
}
169+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenSolid\Core\Infrastructure\Persistence\Doctrine\ORM;
6+
7+
use Doctrine\Common\Collections\Criteria;
8+
use Doctrine\ORM\EntityManagerInterface;
9+
use Doctrine\ORM\LazyCriteriaCollection as BaseLazyCriteriaCollection;
10+
use OpenSolid\Core\Domain\Repository\Collection;
11+
12+
/**
13+
* @template TKey of array-key
14+
* @template TValue of object
15+
*
16+
* @extends BaseLazyCriteriaCollection<TKey, TValue>
17+
*
18+
* @implements Collection<TKey, TValue>
19+
*/
20+
class LazyCriteriaCollection extends BaseLazyCriteriaCollection implements Collection
21+
{
22+
/**
23+
* @template T of object
24+
*
25+
* @param class-string<T> $className
26+
*
27+
* @return self<TKey, T>
28+
*/
29+
public static function for(EntityManagerInterface $em, string $className, Criteria $criteria): self
30+
{
31+
/** @var self<TKey, T> */
32+
return new self($em->getUnitOfWork()->getEntityPersister($className), $criteria);
33+
}
34+
}

0 commit comments

Comments
 (0)