1: | <?php declare(strict_types=1); |
2: | |
3: | namespace Salient\Sync\Support; |
4: | |
5: | use Salient\Contract\Sync\DeferredEntityInterface; |
6: | use Salient\Contract\Sync\SyncContextInterface; |
7: | use Salient\Contract\Sync\SyncEntityInterface; |
8: | use Salient\Contract\Sync\SyncEntityLinkType as LinkType; |
9: | use Salient\Contract\Sync\SyncProviderInterface; |
10: | use Salient\Contract\Sync\SyncStoreInterface; |
11: | use Closure; |
12: | use LogicException; |
13: | |
14: | |
15: | |
16: | |
17: | |
18: | |
19: | |
20: | |
21: | |
22: | |
23: | final class DeferredEntity implements DeferredEntityInterface |
24: | { |
25: | private SyncProviderInterface $Provider; |
26: | private ?SyncContextInterface $Context; |
27: | |
28: | private string $Entity; |
29: | |
30: | private $EntityId; |
31: | |
32: | private $Replace = null; |
33: | |
34: | private ?Closure $Callback = null; |
35: | |
36: | private ?SyncEntityInterface $Resolved = null; |
37: | |
38: | |
39: | |
40: | |
41: | |
42: | |
43: | |
44: | |
45: | |
46: | private function __construct( |
47: | SyncProviderInterface $provider, |
48: | ?SyncContextInterface $context, |
49: | string $entity, |
50: | $entityId, |
51: | &$replace, |
52: | ?Closure $callback = null |
53: | ) { |
54: | $this->Provider = $provider; |
55: | $this->Context = $context; |
56: | $this->Entity = $entity; |
57: | $this->EntityId = $entityId; |
58: | |
59: | if ($callback) { |
60: | $this->Callback = $callback; |
61: | } else { |
62: | $this->Replace = &$replace; |
63: | $this->Replace = $this; |
64: | } |
65: | |
66: | $this |
67: | ->getStore() |
68: | ->registerEntity($entity) |
69: | ->deferEntity( |
70: | $this->Provider->getProviderId(), |
71: | $entity, |
72: | $entityId, |
73: | $this, |
74: | ); |
75: | } |
76: | |
77: | |
78: | |
79: | |
80: | public function toLink(int $type = LinkType::DEFAULT, bool $compact = true): array |
81: | { |
82: | switch ($type) { |
83: | case LinkType::DEFAULT: |
84: | case LinkType::FRIENDLY: |
85: | return [ |
86: | '@type' => $this->getTypeUri($compact), |
87: | '@id' => $this->EntityId, |
88: | ]; |
89: | |
90: | case LinkType::COMPACT: |
91: | return [ |
92: | '@id' => $this->getUri($compact), |
93: | ]; |
94: | |
95: | default: |
96: | throw new LogicException("Invalid link type: $type"); |
97: | } |
98: | } |
99: | |
100: | |
101: | |
102: | |
103: | public function getUri(bool $compact = true): string |
104: | { |
105: | return sprintf('%s/%s', $this->getTypeUri($compact), $this->EntityId); |
106: | } |
107: | |
108: | private function getTypeUri(bool $compact): string |
109: | { |
110: | $typeUri = $this->getStore()->getEntityUri($this->Entity, $compact); |
111: | |
112: | return $typeUri |
113: | ?? '/' . str_replace('\\', '/', ltrim($this->Entity, '\\')); |
114: | } |
115: | |
116: | |
117: | |
118: | |
119: | public function resolve(): SyncEntityInterface |
120: | { |
121: | if ($this->Resolved !== null) { |
122: | return $this->Resolved; |
123: | } |
124: | |
125: | return $this |
126: | ->Provider |
127: | ->with($this->Entity, $this->Context) |
128: | ->get($this->EntityId); |
129: | } |
130: | |
131: | |
132: | |
133: | |
134: | public function replace(SyncEntityInterface $entity): void |
135: | { |
136: | if ($this->Resolved !== null) { |
137: | |
138: | throw new LogicException('Entity already resolved'); |
139: | |
140: | } |
141: | |
142: | $this->Resolved = $entity; |
143: | |
144: | if ($this->Callback) { |
145: | ($this->Callback)($entity); |
146: | return; |
147: | } |
148: | |
149: | $this->Replace = $entity; |
150: | unset($this->Replace); |
151: | } |
152: | |
153: | |
154: | |
155: | |
156: | public static function defer( |
157: | SyncProviderInterface $provider, |
158: | ?SyncContextInterface $context, |
159: | string $entity, |
160: | $entityId, |
161: | &$replace = null, |
162: | ?Closure $callback = null |
163: | ): void { |
164: | new self( |
165: | $provider, |
166: | $context, |
167: | $entity, |
168: | $entityId, |
169: | $replace, |
170: | $callback, |
171: | ); |
172: | } |
173: | |
174: | |
175: | |
176: | |
177: | public static function deferList( |
178: | SyncProviderInterface $provider, |
179: | ?SyncContextInterface $context, |
180: | string $entity, |
181: | array $entityIds, |
182: | &$replace = null, |
183: | ?Closure $callback = null |
184: | ): void { |
185: | if ($callback) { |
186: | foreach ($entityIds as $entityId) { |
187: | |
188: | new self( |
189: | $provider, |
190: | $context, |
191: | $entity, |
192: | $entityId, |
193: | $null, |
194: | $callback, |
195: | ); |
196: | } |
197: | return; |
198: | } |
199: | |
200: | $list = []; |
201: | $i = 0; |
202: | foreach ($entityIds as $entityId) { |
203: | new self( |
204: | $provider, |
205: | $context, |
206: | $entity, |
207: | $entityId, |
208: | |
209: | $list[$i++], |
210: | ); |
211: | } |
212: | $replace = $list; |
213: | } |
214: | |
215: | |
216: | |
217: | |
218: | public function getContext(): ?SyncContextInterface |
219: | { |
220: | return $this->Context; |
221: | } |
222: | |
223: | private function getStore(): SyncStoreInterface |
224: | { |
225: | return $this->Provider->getStore(); |
226: | } |
227: | |
228: | |
229: | |
230: | |
231: | public function __set(string $name, $value): void |
232: | { |
233: | $entity = $this->resolve(); |
234: | $entity->{$name} = $value; |
235: | } |
236: | |
237: | |
238: | |
239: | |
240: | public function __get(string $name) |
241: | { |
242: | $entity = $this->resolve(); |
243: | return $entity->{$name}; |
244: | } |
245: | |
246: | public function __isset(string $name): bool |
247: | { |
248: | $entity = $this->resolve(); |
249: | return isset($entity->{$name}); |
250: | } |
251: | |
252: | public function __unset(string $name): void |
253: | { |
254: | $entity = $this->resolve(); |
255: | unset($entity->{$name}); |
256: | } |
257: | } |
258: | |