Passed
Push — master ( 1ee97e...5adf30 )
by Kevin
02:54
created

Proxy::createFromPersisted()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 1
dl 0
loc 6
ccs 4
cts 4
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Zenstruck\Foundry;
4
5
use Doctrine\Persistence\ObjectManager;
6
use PHPUnit\Framework\Assert;
7
8
/**
9
 * @author Kevin Bond <[email protected]>
10
 */
11
final class Proxy
12
{
13
    /** @var object */
14
    private $object;
15
16
    /** @var string */
17
    private $class;
18
19
    /** @var bool */
20
    private $autoRefresh = false;
21
22
    /** @var bool */
23
    private $persisted = false;
24
25
    /**
26
     * @internal
27
     */
28 258
    public function __construct(object $object)
29
    {
30 258
        $this->object = $object;
31 258
        $this->class = \get_class($object);
32 258
    }
33
34 126
    public function __call(string $method, array $arguments)
35
    {
36 126
        return $this->object()->{$method}(...$arguments);
37
    }
38
39 4
    public function __get(string $name)
40
    {
41 4
        return $this->object()->{$name};
42
    }
43
44 4
    public function __set(string $name, $value): void
45
    {
46 4
        $this->object()->{$name} = $value;
47 4
    }
48
49 4
    public function __unset(string $name): void
50
    {
51 4
        unset($this->object()->{$name});
52 4
    }
53
54 4
    public function __isset(string $name): bool
55
    {
56 4
        return isset($this->object()->{$name});
57
    }
58
59 8
    public function __toString(): string
60
    {
61 8
        if (!\method_exists($this->object, '__toString')) {
62 4
            if (\PHP_VERSION_ID < 70400) {
63
                return '(no __toString)';
64
            }
65
66 4
            throw new \RuntimeException(\sprintf('Proxied object "%s" cannot be converted to a string.', $this->class));
67
        }
68
69 4
        return $this->object()->__toString();
70
    }
71
72
    /**
73
     * @internal
74
     */
75 52
    public static function createFromPersisted(object $object): self
76
    {
77 52
        $proxy = new self($object);
78 52
        $proxy->persisted = $proxy->autoRefresh = true;
79
80 52
        return $proxy;
81
    }
82
83 178
    public function isPersisted(): bool
84
    {
85 178
        return $this->persisted;
86
    }
87
88 158
    public function object(): object
89
    {
90 158
        if ($this->autoRefresh && $this->persisted) {
91 78
            $this->refresh();
92
        }
93
94 158
        return $this->object;
95
    }
96
97 186
    public function save(): self
98
    {
99 186
        $this->objectManager()->persist($this->object);
100 186
        $this->objectManager()->flush();
101 186
        $this->autoRefresh = $this->persisted = true;
102
103 186
        return $this;
104
    }
105
106 4
    public function remove(): self
107
    {
108 4
        $this->objectManager()->remove($this->object);
109 4
        $this->objectManager()->flush();
110 4
        $this->autoRefresh = $this->persisted = false;
111
112 4
        return $this;
113
    }
114
115 86
    public function refresh(): self
116
    {
117 86
        if (!$this->persisted) {
118 4
            throw new \RuntimeException(\sprintf('Cannot refresh unpersisted object (%s).', $this->class));
119
        }
120
121 82
        if ($this->objectManager()->contains($this->object)) {
122 78
            $this->objectManager()->refresh($this->object);
123
124 78
            return $this;
125
        }
126
127 8
        if (!$object = $this->fetchObject()) {
128 4
            throw new \RuntimeException('The object no longer exists.');
129
        }
130
131 4
        $this->object = $object;
132
133 4
        return $this;
134
    }
135
136
    /**
137
     * @param mixed $value
138
     */
139 12
    public function forceSet(string $property, $value): self
140
    {
141 12
        return $this->forceSetAll([$property => $value]);
142
    }
143
144 12
    public function forceSetAll(array $properties): self
145
    {
146 12
        $object = $this->object();
147
148 12
        foreach ($properties as $property => $value) {
149 12
            Instantiator::forceSet($object, $property, $value);
150
        }
151
152 12
        return $this;
153
    }
154
155
    /**
156
     * @return mixed
157
     */
158 4
    public function forceGet(string $property)
159
    {
160 4
        return Instantiator::forceGet($this->object(), $property);
161
    }
162
163 4
    public function repository(): RepositoryProxy
164
    {
165 4
        return Factory::configuration()->repositoryFor($this->class);
166
    }
167
168 182
    public function enableAutoRefresh(): self
169
    {
170 182
        if (!$this->persisted) {
171
            throw new \RuntimeException(\sprintf('Cannot enable auto-refresh on unpersisted object (%s).', $this->class));
172
        }
173
174 182
        $this->autoRefresh = true;
175
176 182
        return $this;
177
    }
178
179 186
    public function disableAutoRefresh(): self
180
    {
181 186
        $this->autoRefresh = false;
182
183 186
        return $this;
184
    }
185
186 4
    public function withoutAutoRefresh(callable $callback): self
187
    {
188 4
        $this->disableAutoRefresh();
189
190 4
        $this->executeCallback($callback);
191
192 4
        return $this->isPersisted() ? $this->enableAutoRefresh() : $this;
193
    }
194
195 4
    public function assertPersisted(string $message = 'The object is not persisted.'): self
196
    {
197 4
        Assert::assertNotNull($this->fetchObject(), $message);
198
199 4
        return $this;
200
    }
201
202 4
    public function assertNotPersisted(string $message = 'The object is persisted but it should not be.'): self
203
    {
204 4
        Assert::assertNull($this->fetchObject(), $message);
205
206 4
        return $this;
207
    }
208
209
    /**
210
     * @internal
211
     */
212 8
    public function executeCallback(callable $callback, ...$arguments): void
213
    {
214 8
        $object = $this;
215 8
        $parameters = (new \ReflectionFunction($callback))->getParameters();
216
217 8
        if (isset($parameters[0]) && $parameters[0]->getType() && $this->class === $parameters[0]->getType()->getName()) {
218 8
            $object = $object->object();
219
        }
220
221 8
        $callback($object, ...$arguments);
222 8
    }
223
224 16
    private function fetchObject(): ?object
225
    {
226 16
        $id = $this->objectManager()->getClassMetadata($this->class)->getIdentifierValues($this->object);
227
228 16
        return empty($id) ? null : $this->objectManager()->find($this->class, $id);
229
    }
230
231 186
    private function objectManager(): ObjectManager
232
    {
233 186
        return Factory::configuration()->objectManagerFor($this->class);
234
    }
235
}
236