1: | <?php declare(strict_types=1); |
2: | |
3: | namespace Salient\Contract\Sync; |
4: | |
5: | use Salient\Contract\Core\Provider\ProviderContextInterface; |
6: | use Salient\Contract\Sync\Exception\InvalidFilterSignatureExceptionInterface; |
7: | use DateTimeInterface; |
8: | |
9: | /** |
10: | * The context within which sync entities are instantiated by a provider |
11: | * |
12: | * @extends ProviderContextInterface<SyncProviderInterface,SyncEntityInterface> |
13: | */ |
14: | interface SyncContextInterface extends ProviderContextInterface |
15: | { |
16: | /** |
17: | * @param bool $detectRecursion If `true`, check if the context has already |
18: | * been propagated for `$entity` and return the result via |
19: | * {@see SyncContextInterface::recursionDetected()}. |
20: | */ |
21: | public function pushEntity($entity, bool $detectRecursion = false); |
22: | |
23: | /** |
24: | * Check if recursion was detected during the last call to pushEntity() |
25: | * |
26: | * @phpstan-assert-if-true !null $this->getLastEntity() |
27: | */ |
28: | public function recursionDetected(): bool; |
29: | |
30: | /** |
31: | * Check if a sync operation has been applied to the context |
32: | * |
33: | * @phpstan-assert-if-true !null $this->getEntityType() |
34: | * @phpstan-assert-if-true !null $this->getOperation() |
35: | */ |
36: | public function hasOperation(): bool; |
37: | |
38: | /** |
39: | * Get the sync operation applied to the context |
40: | * |
41: | * @return SyncOperation::*|null |
42: | */ |
43: | public function getOperation(): ?int; |
44: | |
45: | /** |
46: | * Check if the context has an unclaimed filter applied via non-mandatory |
47: | * sync operation arguments |
48: | * |
49: | * @param string|null $key If `null`, check if the context has any unclaimed |
50: | * filters. |
51: | */ |
52: | public function hasFilter(?string $key = null): bool; |
53: | |
54: | /** |
55: | * Get the value of an unclaimed filter applied to the context via |
56: | * non-mandatory sync operation arguments |
57: | * |
58: | * If `$orValue` is `true` and the context has a value for `$key`, it is |
59: | * returned if there is no matching filter, otherwise `null` is returned. |
60: | * |
61: | * @return (int|string|float|bool|null)[]|int|string|float|bool|null |
62: | */ |
63: | public function getFilter(string $key, bool $orValue = true); |
64: | |
65: | /** |
66: | * Claim the value of an unclaimed filter applied via non-mandatory sync |
67: | * operation arguments, removing it from the context |
68: | * |
69: | * This method deliberately breaks the context's immutability contract. |
70: | * |
71: | * If `$orValue` is `true` and the context has a value for `$key`, it is |
72: | * returned if there is no matching filter, otherwise `null` is returned. |
73: | * |
74: | * @return (int|string|float|bool|null)[]|int|string|float|bool|null |
75: | */ |
76: | public function claimFilter(string $key, bool $orValue = true); |
77: | |
78: | /** |
79: | * Get unclaimed filters applied to the context via non-mandatory sync |
80: | * operation arguments |
81: | * |
82: | * @return array<string,(int|string|float|bool|null)[]|int|string|float|bool|null> |
83: | */ |
84: | public function getFilters(): array; |
85: | |
86: | /** |
87: | * Get an instance with the given sync operation and filters derived from |
88: | * its non-mandatory arguments |
89: | * |
90: | * An exception is thrown if non-mandatory arguments in `$args` don't match |
91: | * one of the following signatures. |
92: | * |
93: | * 1. An associative array (`fn(..., array<string,mixed> $filters)`) |
94: | * |
95: | * - Keys are trimmed |
96: | * - Keys that contain letters or numbers, optionally with inner |
97: | * whitespace, underscores or hyphens, are converted to snake_case |
98: | * |
99: | * 2. A list of identifiers (`fn(..., int ...$ids)` or `fn(..., string ...$ids)`) |
100: | * |
101: | * - Becomes `[ 'id' => $ids ]` |
102: | * |
103: | * 3. A list of entities (`fn(..., SyncEntityInterface ...$entities)`) |
104: | * |
105: | * - Grouped by snake_case {@see ServiceAwareInterface::getService()} |
106: | * short names |
107: | * - Example: `[ 'faculty' => [42, 71], 'faculty_user' => [101] ]` |
108: | * |
109: | * 4. No arguments (`fn(...)`) |
110: | * |
111: | * - Becomes `[]` |
112: | * |
113: | * In all cases: |
114: | * |
115: | * - {@see SyncEntityInterface} objects are replaced with their identifiers |
116: | * - An exception is thrown if any {@see SyncEntityInterface} objects do not |
117: | * have an identifier ({@see SyncEntityInterface::getId()} returns `null`) |
118: | * or do not have the same provider as the context |
119: | * - {@see DateTimeInterface} instances are formatted by the provider's date |
120: | * formatter. |
121: | * - The result is surfaced via {@see SyncContextInterface::hasFilter()}, |
122: | * {@see SyncContextInterface::getFilter()}, |
123: | * {@see SyncContextInterface::claimFilter()} and |
124: | * {@see SyncContextInterface::getFilters()}. |
125: | * |
126: | * {@see SyncContextInterface::claimFilter()} should generally be used to |
127: | * prevent sync operation failures caused by unclaimed filters. |
128: | * |
129: | * @param SyncOperation::* $operation |
130: | * @param class-string<SyncEntityInterface> $entityType |
131: | * @param mixed ...$args Sync operation arguments, not including the |
132: | * {@see SyncContextInterface} argument. |
133: | * @return static |
134: | * @throws InvalidFilterSignatureExceptionInterface |
135: | */ |
136: | public function withOperation(int $operation, string $entityType, ...$args); |
137: | |
138: | /** |
139: | * Get the deferral policy applied to the context |
140: | * |
141: | * @return DeferralPolicy::* |
142: | */ |
143: | public function getDeferralPolicy(): int; |
144: | |
145: | /** |
146: | * Get an instance with the given deferral policy |
147: | * |
148: | * @param DeferralPolicy::* $policy |
149: | * @return static |
150: | */ |
151: | public function withDeferralPolicy(int $policy); |
152: | |
153: | /** |
154: | * Get the hydration policy applied to the context, optionally scoped by |
155: | * sync entity type |
156: | * |
157: | * @param class-string<SyncEntityInterface>|null $entityType |
158: | * @return HydrationPolicy::* |
159: | */ |
160: | public function getHydrationPolicy(?string $entityType): int; |
161: | |
162: | /** |
163: | * Get an instance with the given hydration policy, optionally scoped by |
164: | * sync entity type and/or depth |
165: | * |
166: | * @param HydrationPolicy::* $policy |
167: | * @param class-string<SyncEntityInterface>|null $entityType Limit the scope |
168: | * of the change to an entity type. |
169: | * @param array<int<1,max>>|int<1,max>|null $depth Limit the scope of the |
170: | * change to entities at a given `$depth` from the current context. |
171: | * @return static |
172: | */ |
173: | public function withHydrationPolicy( |
174: | int $policy, |
175: | ?string $entityType = null, |
176: | $depth = null |
177: | ); |
178: | |
179: | /** |
180: | * Get the offline mode applied to the context |
181: | * |
182: | * @return bool|null - `null` (default): entities are returned from the |
183: | * local entity store if possible, otherwise they are retrieved from the |
184: | * provider. |
185: | * - `true`: entities are returned from the local entity store without |
186: | * falling back to retrieval from the provider. |
187: | * - `false`: entities are retrieved from the provider without consulting |
188: | * the local entity store. |
189: | */ |
190: | public function getOffline(): ?bool; |
191: | |
192: | /** |
193: | * Get an instance with the given offline mode |
194: | * |
195: | * @param bool|null $offline - `null` (default): return entities from the |
196: | * local entity store if possible, otherwise retrieve them from the |
197: | * provider. |
198: | * - `true`: return entities from the local entity store without falling |
199: | * back to retrieval from the provider. |
200: | * - `false`: retrieve entities from the provider without consulting the |
201: | * local entity store. |
202: | * @return static |
203: | */ |
204: | public function withOffline(?bool $offline); |
205: | } |
206: |