Skip to content

Commit 0194fc2

Browse files
committed
Make hydrators persistent with owned EntityFactory
Hydrators are now long-lived objects that own their EntityFactory instead of receiving it as a parameter on every call. The mapper derives its entityFactory and style from the hydrator, inverting the previous dependency direction. - Add entityFactory property to Hydrator interface - Split hydrate() (returns root entity) from hydrateAll() (returns full graph) - Remove $hydrator property and hydrateFrom() from Collection - Remove defaultHydrator()/resolveHydrator() from AbstractMapper - Mapper constructor now takes Hydrator instead of EntityFactory
1 parent f95b89c commit 0194fc2

15 files changed

Lines changed: 315 additions & 184 deletions

src/AbstractMapper.php

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,12 @@ abstract class AbstractMapper
3030
/** @var array<string, Collection> */
3131
private array $collections = [];
3232

33+
public EntityFactory $entityFactory { get => $this->hydrator->entityFactory; }
34+
3335
public Styles\Stylable $style { get => $this->entityFactory->style; }
3436

3537
public function __construct(
36-
public readonly EntityFactory $entityFactory = new EntityFactory(),
38+
public readonly Hydrator $hydrator,
3739
) {
3840
$this->tracked = new SplObjectStorage();
3941
$this->pending = new SplObjectStorage();
@@ -130,8 +132,6 @@ public function registerCollection(string $alias, Collection $collection): void
130132
$this->collections[$alias] = $collection;
131133
}
132134

133-
abstract protected function defaultHydrator(Collection $collection): Hydrator;
134-
135135
/**
136136
* @param array<string, mixed> $columns
137137
*
@@ -153,11 +153,6 @@ protected function filterColumns(array $columns, Collection $collection): array
153153
return array_intersect_key($columns, array_flip([...$collection->filters, $id]));
154154
}
155155

156-
protected function resolveHydrator(Collection $collection): Hydrator
157-
{
158-
return $collection->hydrator ?? $this->defaultHydrator($collection);
159-
}
160-
161156
protected function registerInIdentityMap(object $entity, Collection $coll): void
162157
{
163158
if ($coll->name === null) {

src/Collections/Collection.php

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
use ArrayAccess;
88
use Respect\Data\AbstractMapper;
99
use Respect\Data\CollectionNotBound;
10-
use Respect\Data\Hydrator;
1110

1211
/** @implements ArrayAccess<string, Collection> */
1312
class Collection implements ArrayAccess
@@ -16,8 +15,6 @@ class Collection implements ArrayAccess
1615

1716
public private(set) AbstractMapper|null $mapper = null;
1817

19-
public private(set) Hydrator|null $hydrator = null;
20-
2118
public private(set) Collection|null $parent = null;
2219

2320
public private(set) Collection|null $connectsTo = null;
@@ -101,13 +98,6 @@ public function bindMapper(AbstractMapper $mapper): static
10198
return $this;
10299
}
103100

104-
public function hydrateFrom(Hydrator $hydrator): static
105-
{
106-
$this->hydrator = $hydrator;
107-
108-
return $this;
109-
}
110-
111101
public function stack(Collection $collection): static
112102
{
113103
$tail = $this->last ?? $this;

src/Hydrator.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,17 @@
1010
/** Transforms raw backend data into entity instances mapped to their collections */
1111
interface Hydrator
1212
{
13-
/** @return SplObjectStorage<object, Collection>|false */
13+
public EntityFactory $entityFactory { get; }
14+
15+
/** Returns just the root entity */
1416
public function hydrate(
1517
mixed $raw,
1618
Collection $collection,
17-
EntityFactory $entityFactory,
19+
): object|false;
20+
21+
/** @return SplObjectStorage<object, Collection>|false */
22+
public function hydrateAll(
23+
mixed $raw,
24+
Collection $collection,
1825
): SplObjectStorage|false;
1926
}

src/Hydrators/Base.php

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,29 @@
1313
/** Base hydrator providing collection-tree entity wiring */
1414
abstract class Base implements Hydrator
1515
{
16+
public function __construct(
17+
public readonly EntityFactory $entityFactory = new EntityFactory(),
18+
) {
19+
}
20+
21+
public function hydrate(
22+
mixed $raw,
23+
Collection $collection,
24+
): object|false {
25+
$entities = $this->hydrateAll($raw, $collection);
26+
if ($entities === false) {
27+
return false;
28+
}
29+
30+
$entities->rewind();
31+
32+
return $entities->current();
33+
}
34+
1635
/** @param SplObjectStorage<object, Collection> $entities */
17-
protected function wireRelationships(SplObjectStorage $entities, EntityFactory $entityFactory): void
36+
protected function wireRelationships(SplObjectStorage $entities): void
1837
{
19-
$style = $entityFactory->style;
38+
$style = $this->entityFactory->style;
2039
$others = clone $entities;
2140

2241
foreach ($entities as $entity) {
@@ -40,12 +59,12 @@ protected function wireRelationships(SplObjectStorage $entities, EntityFactory $
4059
continue;
4160
}
4261

43-
$id = $entityFactory->get($other, $style->identifier($otherColl->name));
62+
$id = $this->entityFactory->get($other, $style->identifier($otherColl->name));
4463
if ($id === null) {
4564
continue;
4665
}
4766

48-
$entityFactory->set($entity, $relationName, $other);
67+
$this->entityFactory->set($entity, $relationName, $other);
4968
}
5069
}
5170
}
@@ -57,13 +76,12 @@ protected function wireRelationships(SplObjectStorage $entities, EntityFactory $
5776
*/
5877
protected function resolveEntityClass(
5978
Collection $collection,
60-
EntityFactory $entityFactory,
6179
object|array $row,
6280
): string {
6381
if ($collection instanceof Typed) {
64-
return $collection->resolveEntityClass($entityFactory, $row);
82+
return $collection->resolveEntityClass($this->entityFactory, $row);
6583
}
6684

67-
return $entityFactory->resolveClass((string) $collection->name);
85+
return $this->entityFactory->resolveClass((string) $collection->name);
6886
}
6987
}

src/Hydrators/Nested.php

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
namespace Respect\Data\Hydrators;
66

77
use Respect\Data\Collections\Collection;
8-
use Respect\Data\EntityFactory;
98
use SplObjectStorage;
109

1110
use function is_array;
@@ -14,10 +13,9 @@
1413
final class Nested extends Base
1514
{
1615
/** @return SplObjectStorage<object, Collection>|false */
17-
public function hydrate(
16+
public function hydrateAll(
1817
mixed $raw,
1918
Collection $collection,
20-
EntityFactory $entityFactory,
2119
): SplObjectStorage|false {
2220
if (!is_array($raw)) {
2321
return false;
@@ -26,10 +24,10 @@ public function hydrate(
2624
/** @var SplObjectStorage<object, Collection> $entities */
2725
$entities = new SplObjectStorage();
2826

29-
$this->hydrateNode($raw, $collection, $entityFactory, $entities);
27+
$this->hydrateNode($raw, $collection, $entities);
3028

3129
if ($entities->count() > 1) {
32-
$this->wireRelationships($entities, $entityFactory);
30+
$this->wireRelationships($entities);
3331
}
3432

3533
return $entities;
@@ -42,29 +40,28 @@ public function hydrate(
4240
private function hydrateNode(
4341
array $data,
4442
Collection $collection,
45-
EntityFactory $entityFactory,
4643
SplObjectStorage $entities,
4744
): void {
48-
$entity = $entityFactory->create(
49-
$this->resolveEntityClass($collection, $entityFactory, $data),
45+
$entity = $this->entityFactory->create(
46+
$this->resolveEntityClass($collection, $data),
5047
);
5148

5249
foreach ($data as $key => $value) {
5350
if (is_array($value)) {
5451
continue;
5552
}
5653

57-
$entityFactory->set($entity, $key, $value);
54+
$this->entityFactory->set($entity, $key, $value);
5855
}
5956

6057
$entities[$entity] = $collection;
6158

6259
if ($collection->connectsTo !== null) {
63-
$this->hydrateChild($data, $collection->connectsTo, $entityFactory, $entities);
60+
$this->hydrateChild($data, $collection->connectsTo, $entities);
6461
}
6562

6663
foreach ($collection->children as $child) {
67-
$this->hydrateChild($data, $child, $entityFactory, $entities);
64+
$this->hydrateChild($data, $child, $entities);
6865
}
6966
}
7067

@@ -75,7 +72,6 @@ private function hydrateNode(
7572
private function hydrateChild(
7673
array $parentData,
7774
Collection $child,
78-
EntityFactory $entityFactory,
7975
SplObjectStorage $entities,
8076
): void {
8177
$key = $child->name;
@@ -85,6 +81,6 @@ private function hydrateChild(
8581

8682
/** @var array<string, mixed> $childData */
8783
$childData = $parentData[$key];
88-
$this->hydrateNode($childData, $child, $entityFactory, $entities);
84+
$this->hydrateNode($childData, $child, $entities);
8985
}
9086
}

src/Hydrators/PrestyledAssoc.php

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
use Respect\Data\Collections\Collection;
1010
use Respect\Data\Collections\Composite;
1111
use Respect\Data\Collections\Filtered;
12-
use Respect\Data\EntityFactory;
1312
use SplObjectStorage;
1413

1514
use function array_keys;
@@ -31,10 +30,9 @@ final class PrestyledAssoc extends Base
3130
private Collection|null $cachedCollection = null;
3231

3332
/** @return SplObjectStorage<object, Collection>|false */
34-
public function hydrate(
33+
public function hydrateAll(
3534
mixed $raw,
3635
Collection $collection,
37-
EntityFactory $entityFactory,
3836
): SplObjectStorage|false {
3937
if (!$raw || !is_array($raw)) {
4038
return false;
@@ -59,19 +57,19 @@ public function hydrate(
5957

6058
if (!isset($instances[$basePrefix])) {
6159
$coll = $collMap[$basePrefix];
62-
$class = $this->resolveEntityClass($coll, $entityFactory, $props);
63-
$instances[$basePrefix] = $entityFactory->create($class);
60+
$class = $this->resolveEntityClass($coll, $props);
61+
$instances[$basePrefix] = $this->entityFactory->create($class);
6462
$entities[$instances[$basePrefix]] = $coll;
6563
}
6664

6765
$entity = $instances[$basePrefix];
6866
foreach ($props as $prop => $value) {
69-
$entityFactory->set($entity, $prop, $value, styled: true);
67+
$this->entityFactory->set($entity, $prop, $value, styled: true);
7068
}
7169
}
7270

7371
if ($entities->count() > 1) {
74-
$this->wireRelationships($entities, $entityFactory);
72+
$this->wireRelationships($entities);
7573
}
7674

7775
return $entities;

0 commit comments

Comments
 (0)