Passed
Push — master ( d8c5da...177fc5 )
by Jesse
01:27
created

Extract::fromOnly()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 9
nc 4
nop 2
dl 0
loc 13
rs 9.9666
c 0
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
4
namespace Stratadox\EntityState;
5
6
use function array_key_exists as alreadyAdded;
7
use function array_shift;
8
use function count;
9
use function get_class as classOfThe;
10
use Stratadox\EntityState\Internal\AcceptsNewEntities;
11
use Stratadox\EntityState\Internal\CollectionExtractor;
12
use Stratadox\EntityState\Internal\EntityReferenceExtractor;
13
use Stratadox\EntityState\Internal\ExtractionRequest;
14
use Stratadox\EntityState\Internal\Extractor;
15
use Stratadox\EntityState\Internal\NewEntityDetector;
16
use Stratadox\EntityState\Internal\ObjectExtractor;
17
use Stratadox\EntityState\Internal\PropertyExtractor;
18
use Stratadox\EntityState\Internal\ScalarExtractor;
19
use Stratadox\EntityState\Internal\ShouldStringify;
20
use Stratadox\EntityState\Internal\Stringifier;
21
use Stratadox\IdentityMap\MapsObjectsByIdentity as Map;
22
use Stratadox\IdentityMap\NoSuchObject;
23
24
/**
25
 * Extract the state of the entities.
26
 *
27
 * @author Stratadox
28
 */
29
final class Extract implements ExtractsEntityState, AcceptsNewEntities
30
{
31
    private $extractor;
32
    private $newEntities = [];
33
34
    private function __construct(Extractor $extractor)
35
    {
36
        $this->extractor = $extractor;
37
    }
38
39
    /**
40
     * Produces a state extractor.
41
     *
42
     * @return ExtractsEntityState
43
     */
44
    public static function state(): ExtractsEntityState
45
    {
46
        return new Extract(
47
            PropertyExtractor::using(
48
                CollectionExtractor::withAlternative(
49
                    EntityReferenceExtractor::withAlternative(
50
                        ObjectExtractor::withAlternative(
51
                            ScalarExtractor::asLastResort()
52
                        )
53
                    )
54
                )
55
            )
56
        );
57
    }
58
59
    /**
60
     * Produces a state extractor that converts certain types to string.
61
     *
62
     * @param mixed ...$classes
63
     * @return ExtractsEntityState
64
     */
65
    public static function stringifying(...$classes): ExtractsEntityState
66
    {
67
        return new Extract(
68
            PropertyExtractor::using(
69
                CollectionExtractor::withAlternative(
70
                    Stringifier::withCondition(
71
                        ShouldStringify::these(...$classes),
72
                        EntityReferenceExtractor::withAlternative(
73
                            ObjectExtractor::withAlternative(
74
                                ScalarExtractor::asLastResort()
75
                            )
76
                        )
77
                    )
78
                )
79
            )
80
        );
81
    }
82
83
    /** @inheritdoc */
84
    public function consideringIt(
85
        DefinesEntityType ...$asEntities
86
    ): ExtractsEntityState {
87
        $new = clone $this;
88
        $new->extractor = NewEntityDetector::with($new, $this->extractor, ...$asEntities);
89
        return $new;
90
    }
91
92
    /** @inheritdoc */
93
    public function from(Map $map): State
94
    {
95
        return $this->fromOnly($map, ...$map->objects());
96
    }
97
98
    /** @inheritdoc */
99
    public function fromOnly(Map $map, object ...$objects): State
100
    {
101
        $states = [];
102
        foreach ($objects as $object) {
103
            $states[] = $this->stateOfThe($object, $map, $map->idOf($object));
104
        }
105
        while (count($this->newEntities)) {
106
            $newEntity = array_shift($this->newEntities);
107
            [$object, $id] = $newEntity;
108
            $map = $map->add($id, $object);
109
            $states[] = $this->stateOfThe($object, $map, $id);
110
        }
111
        return StateRepresentation::with(EntityStates::list(...$states), $map);
112
    }
113
114
    /**
115
     * @inheritdoc
116
     * @internal
117
     */
118
    public function addAsNewEntity(object $newEntity, string $id): void
119
    {
120
        $identifier = classOfThe($newEntity) . '~' . $id;
121
        if (!alreadyAdded($identifier, $this->newEntities)) {
122
            $this->newEntities[$identifier] = [$newEntity, $id];
123
        }
124
    }
125
126
    /** @throws NoSuchObject */
127
    private function stateOfThe(
128
        object $entity,
129
        Map $map,
130
        string $id
131
    ): RepresentsEntity {
132
        return EntityState::ofThe(classOfThe($entity), $id, PropertyStates::list(
133
            ...$this->extractor->extract(
134
                ExtractionRequest::for($entity, $map)
135
            )
136
        ));
137
    }
138
}
139