Passed
Push — master ( d25e4d...dba989 )
by Mr
06:54
created

EntityTrait::evaluatePath()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 5

Importance

Changes 0
Metric Value
eloc 8
c 0
b 0
f 0
dl 0
loc 14
ccs 9
cts 9
cp 1
rs 9.6111
cc 5
nc 5
nop 1
crap 5
1
<?php declare(strict_types=1);
2
/**
3
 * This file is part of the daikon-cqrs/entity project.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
9
namespace Daikon\Entity;
10
11
use Assert\Assert;
12
use Daikon\Entity\Path\ValuePathParser;
13
use Daikon\Entity\Path\ValuePathPart;
14
use Daikon\ValueObject\ValueObjectInterface;
15
use Daikon\ValueObject\ValueObjectMap;
16
17
trait EntityTrait
18
{
19
    private ValueObjectMap $valueObjectMap;
20
21
    private ValuePathParser $pathParser;
22
23 10
    private function __construct(array $state = [])
24
    {
25 10
        $this->pathParser = ValuePathParser::create();
26
27 10
        $objects = [];
28 10
        foreach ($this->getAttributeMap() as $name => $attribute) {
0 ignored issues
show
Bug introduced by
It seems like getAttributeMap() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

28
        foreach ($this->/** @scrutinizer ignore-call */ getAttributeMap() as $name => $attribute) {
Loading history...
29 10
            if (array_key_exists($name, $state)) {
30 10
                $objects[$name] = $attribute->makeValue($state[$name]);
31
            }
32
        }
33
34 10
        $this->valueObjectMap = new ValueObjectMap($objects);
35 10
    }
36
37
    /** @param mixed $state */
38 10
    public static function fromNative($state): self
39
    {
40 10
        return new static($state);
41
    }
42
43 1
    public function toNative(): array
44
    {
45 1
        $entityState = $this->valueObjectMap->toNative();
46 1
        $entityState[EntityInterface::TYPE_KEY] = static::class;
47 1
        return $entityState;
48
    }
49
50 1
    public function isSameAs(EntityInterface $entity): bool
51
    {
52 1
        return $this->getIdentity()->equals($entity->getIdentity());
0 ignored issues
show
Bug introduced by
It seems like getIdentity() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

52
        return $this->/** @scrutinizer ignore-call */ getIdentity()->equals($entity->getIdentity());
Loading history...
53
    }
54
55 3
    public function has(string $name): bool
56
    {
57 3
        $has = $this->getAttributeMap()->has($name);
58 3
        Assert::that($has)->true("Attribute '$name' is not known to the entity ".static::class);
59 2
        return $this->valueObjectMap->has($name);
60
    }
61
62 6
    public function get(string $name, $default = null): ?ValueObjectInterface
63
    {
64 6
        if (mb_strpos($name, '.')) {
65 2
            return $this->evaluatePath($name);
66
        }
67
68 6
        $attribute = $this->getAttributeMap()->get($name, null);
69 6
        Assert::that($attribute)->notNull("Attribute '$name' is not known to the entity ".static::class);
70
        /** @psalm-suppress PossiblyNullArgument */
71 5
        $attributeType = get_class($attribute);
72 5
        Assert::that($default)->nullOr()->isInstanceOf($attributeType, sprintf(
73 5
            "Default type for '$name' must be null or $attributeType, but got '%s'",
74 5
            is_object($default) ? get_class($default) : @gettype($default)
75
        ));
76
77 5
        return $this->valueObjectMap->get($name, $default);
78
    }
79
80 1
    public function withValue(string $name, $value): self
81
    {
82 1
        $copy = clone $this;
83 1
        $copy->valueObjectMap = $copy->valueObjectMap->with($name, $this->makeValue($name, $value));
84 1
        return $copy;
85
    }
86
87 1
    public function withValues(iterable $values): self
88
    {
89 1
        $copy = clone $this;
90 1
        foreach ($values as $name => $value) {
91 1
            $object = $this->makeValue($name, $value);
92 1
            $copy->valueObjectMap = $copy->valueObjectMap->with($name, $object);
93
        }
94 1
        return $copy;
95
    }
96
97
    /** @param static $comparator */
98
    public function equals($comparator): bool
99
    {
100
        /**
101
         * @psalm-suppress RedundantConditionGivenDocblockType
102
         * @psalm-suppress DocblockTypeContradiction
103
         */
104
        Assert::that($comparator)->isInstanceOf(static::class, sprintf(
105
            "Invalid comparator type '%s' given to ".static::class,
106
            is_object($comparator) ? get_class($comparator) : @gettype($comparator)
107
        ));
108
        return (new EntityDiff)($this, $comparator)->isEmpty();
109
    }
110
111
    public function __toString(): string
112
    {
113
        return (string)$this->getIdentity();
114
    }
115
116
    /** @param mixed $value */
117 2
    private function makeValue(string $name, $value): ValueObjectInterface
118
    {
119 2
        $attribute = $this->getAttributeMap()->get($name, null);
120 2
        Assert::that($attribute)->isInstanceOf(
121 2
            AttributeInterface::class,
122 2
            sprintf("Attribute '%s' is unknown to entity %s", $name, static::class)
123
        );
124
        /** @var AttributeInterface $attribute */
125 2
        return $attribute->makeValue($value);
126
    }
127
128 2
    private function evaluatePath(string $valuePath): ?ValueObjectInterface
129
    {
130 2
        $entity = $this;
131
        /** @var ValuePathPart $pathPart */
132 2
        foreach ($this->pathParser->parse($valuePath) as $pathPart) {
133 2
            $value = $entity ? $entity->get($pathPart->getAttributeName()) : null;
134 1
            if ($value && $pathPart->hasPosition()) {
135 1
                Assert::that($value)->isInstanceOf(EntityListInterface::class, 'Trying to traverse non-entity list');
136
                /** @var EntityListInterface $value */
137 1
                $entity = $value->get($pathPart->getPosition());
138 1
                $value = $entity;
139
            }
140
        }
141 1
        return $value ?? null;
142
    }
143
144
    public function __get(string $attribute)
145
    {
146
        return $this->get($attribute);
147
    }
148
149 10
    public function __clone()
150
    {
151 10
        $this->pathParser = clone $this->pathParser;
152 10
        $this->valueObjectMap = clone $this->valueObjectMap;
153 10
    }
154
}
155