|
33 | 33 | * public function view($id) |
34 | 34 | * { |
35 | 35 | * $book = $this->Books->get($id); |
| 36 | + * $userId = $this->Authentication->getIdentity()->id; |
36 | 37 | * |
37 | 38 | * // Add topics for this view (available in MercureHelper) |
38 | 39 | * $this->Mercure->addTopic("/books/{$id}"); |
39 | 40 | * |
40 | | - * // Authorize subscriber |
41 | | - * $this->Mercure->authorize(["/books/{$id}"]); |
| 41 | + * // Fluent authorization with builder pattern |
| 42 | + * $this->Mercure |
| 43 | + * ->addSubscribe("/books/{$id}", ['sub' => $userId]) |
| 44 | + * ->addSubscribe("/notifications/*", ['role' => 'user']) |
| 45 | + * ->authorize() |
| 46 | + * ->discover(); |
| 47 | + * |
| 48 | + * // Or direct authorization (backward compatible) |
| 49 | + * $this->Mercure->authorize(["/books/{$id}"], ['sub' => $userId]); |
42 | 50 | * |
43 | | - * // Or chain methods |
| 51 | + * // Chain topics and authorization together |
44 | 52 | * $this->Mercure |
45 | 53 | * ->addTopic("/books/{$id}") |
46 | 54 | * ->addTopic("/user/{$userId}/updates") |
47 | | - * ->authorize(["/books/{$id}"]) |
| 55 | + * ->addSubscribe("/books/{$id}", ['sub' => $userId]) |
| 56 | + * ->authorize() |
48 | 57 | * ->discover(); |
49 | 58 | * |
50 | 59 | * $this->set('book', $book); |
@@ -95,6 +104,20 @@ class MercureComponent extends Component |
95 | 104 |
|
96 | 105 | protected PublisherInterface $publisherService; |
97 | 106 |
|
| 107 | + /** |
| 108 | + * Subscribe topics accumulated via addSubscribe() |
| 109 | + * |
| 110 | + * @var array<string> |
| 111 | + */ |
| 112 | + protected array $subscribe = []; |
| 113 | + |
| 114 | + /** |
| 115 | + * Additional JWT claims accumulated via addSubscribe() |
| 116 | + * |
| 117 | + * @var array<string, mixed> |
| 118 | + */ |
| 119 | + protected array $additionalClaims = []; |
| 120 | + |
98 | 121 | /** |
99 | 122 | * Default configuration |
100 | 123 | * |
@@ -148,35 +171,174 @@ public function beforeRender(EventInterface $event): void |
148 | 171 | } |
149 | 172 | } |
150 | 173 |
|
| 174 | + /** |
| 175 | + * Add a single topic to authorize for subscription |
| 176 | + * |
| 177 | + * Accumulates topics and claims that will be used when authorize() is called. |
| 178 | + * Claims are merged across multiple calls. |
| 179 | + * |
| 180 | + * Example: |
| 181 | + * ``` |
| 182 | + * // Topic only |
| 183 | + * $this->Mercure->addSubscribe('/books/123'); |
| 184 | + * |
| 185 | + * // Topic with claims |
| 186 | + * $this->Mercure->addSubscribe('/books/123', ['sub' => $userId, 'role' => 'admin']); |
| 187 | + * |
| 188 | + * // Build up gradually |
| 189 | + * $this->Mercure |
| 190 | + * ->addSubscribe('/books/123', ['sub' => $userId]) |
| 191 | + * ->addSubscribe('/notifications/*', ['role' => 'admin']) |
| 192 | + * ->authorize(); |
| 193 | + * ``` |
| 194 | + * |
| 195 | + * @param string $topic Topic pattern (e.g., '/books/123', '/notifications/*') |
| 196 | + * @param array<string, mixed> $additionalClaims JWT claims to merge |
| 197 | + * @return $this For method chaining |
| 198 | + */ |
| 199 | + public function addSubscribe(string $topic, array $additionalClaims = []): static |
| 200 | + { |
| 201 | + $this->subscribe[] = $topic; |
| 202 | + $this->additionalClaims = array_merge($this->additionalClaims, $additionalClaims); |
| 203 | + |
| 204 | + return $this; |
| 205 | + } |
| 206 | + |
| 207 | + /** |
| 208 | + * Add multiple topics to authorize for subscription |
| 209 | + * |
| 210 | + * Accumulates topics and claims that will be used when authorize() is called. |
| 211 | + * Claims are merged with any existing accumulated claims. |
| 212 | + * |
| 213 | + * Example: |
| 214 | + * ``` |
| 215 | + * // Topics only |
| 216 | + * $this->Mercure->addSubscribes(['/books/123', '/notifications/*']); |
| 217 | + * |
| 218 | + * // Topics with claims |
| 219 | + * $this->Mercure->addSubscribes( |
| 220 | + * ['/books/123', '/notifications/*'], |
| 221 | + * ['sub' => $userId, 'role' => 'admin'] |
| 222 | + * ); |
| 223 | + * ``` |
| 224 | + * |
| 225 | + * @param array<string> $topics Array of topic patterns |
| 226 | + * @param array<string, mixed> $additionalClaims JWT claims to merge |
| 227 | + * @return $this For method chaining |
| 228 | + */ |
| 229 | + public function addSubscribes(array $topics, array $additionalClaims = []): static |
| 230 | + { |
| 231 | + $this->subscribe = array_merge($this->subscribe, $topics); |
| 232 | + $this->additionalClaims = array_merge($this->additionalClaims, $additionalClaims); |
| 233 | + |
| 234 | + return $this; |
| 235 | + } |
| 236 | + |
| 237 | + /** |
| 238 | + * Get accumulated subscribe topics |
| 239 | + * |
| 240 | + * @return array<string> |
| 241 | + */ |
| 242 | + public function getSubscribe(): array |
| 243 | + { |
| 244 | + return $this->subscribe; |
| 245 | + } |
| 246 | + |
| 247 | + /** |
| 248 | + * Get accumulated additional claims |
| 249 | + * |
| 250 | + * @return array<string, mixed> |
| 251 | + */ |
| 252 | + public function getAdditionalClaims(): array |
| 253 | + { |
| 254 | + return $this->additionalClaims; |
| 255 | + } |
| 256 | + |
| 257 | + /** |
| 258 | + * Reset accumulated subscribe topics |
| 259 | + * |
| 260 | + * @return $this For method chaining |
| 261 | + */ |
| 262 | + public function resetSubscribe(): static |
| 263 | + { |
| 264 | + $this->subscribe = []; |
| 265 | + |
| 266 | + return $this; |
| 267 | + } |
| 268 | + |
| 269 | + /** |
| 270 | + * Reset accumulated additional claims |
| 271 | + * |
| 272 | + * @return $this For method chaining |
| 273 | + */ |
| 274 | + public function resetAdditionalClaims(): static |
| 275 | + { |
| 276 | + $this->additionalClaims = []; |
| 277 | + |
| 278 | + return $this; |
| 279 | + } |
| 280 | + |
151 | 281 | /** |
152 | 282 | * Authorize subscriber for private topics |
153 | 283 | * |
154 | 284 | * Sets an authorization cookie that allows the subscriber to access |
155 | 285 | * private Mercure topics. The cookie must be set before establishing |
156 | 286 | * the EventSource connection. |
157 | 287 | * |
| 288 | + * Supports both direct parameters and accumulated state via builder methods. |
| 289 | + * Parameters are merged with accumulated state. Accumulated state is automatically |
| 290 | + * reset after authorization. |
| 291 | + * |
158 | 292 | * Example: |
159 | 293 | * ``` |
| 294 | + * // Direct parameters (backward compatible) |
160 | 295 | * $this->Mercure->authorize(['/feeds/123', '/notifications/*']); |
161 | 296 | * |
162 | 297 | * // With additional JWT claims |
163 | 298 | * $this->Mercure->authorize( |
164 | 299 | * subscribe: ['/feeds/123'], |
165 | 300 | * additionalClaims: ['sub' => $userId, 'aud' => 'my-app'] |
166 | 301 | * ); |
| 302 | + * |
| 303 | + * // Using accumulated state |
| 304 | + * $this->Mercure |
| 305 | + * ->addSubscribe('/books/123', ['sub' => $userId]) |
| 306 | + * ->authorize(); |
| 307 | + * |
| 308 | + * // Mixed (parameters merged with accumulated) |
| 309 | + * $this->Mercure |
| 310 | + * ->addSubscribe('/books/123') |
| 311 | + * ->authorize(['/notifications/*'], ['sub' => $userId]); |
| 312 | + * |
| 313 | + * // Chain with topics and discovery |
| 314 | + * $this->Mercure |
| 315 | + * ->addTopic('/books/123') |
| 316 | + * ->addSubscribe('/books/123', ['sub' => $userId]) |
| 317 | + * ->authorize() |
| 318 | + * ->discover(); |
167 | 319 | * ``` |
168 | 320 | * |
169 | | - * @param array<string> $subscribe Topics the subscriber can access |
170 | | - * @param array<string, mixed> $additionalClaims Additional JWT claims to include |
| 321 | + * @param array<string> $subscribe Topics the subscriber can access (merged with addSubscribe()) |
| 322 | + * @param array<string, mixed> $additionalClaims Additional JWT claims to include (merged with accumulated claims) |
171 | 323 | * @return $this For method chaining |
172 | 324 | * @throws \Mercure\Exception\MercureException |
173 | 325 | */ |
174 | 326 | public function authorize(array $subscribe = [], array $additionalClaims = []): static |
175 | 327 | { |
| 328 | + // Merge parameter topics with accumulated topics |
| 329 | + $allSubscribe = array_unique(array_merge($this->subscribe, $subscribe)); |
| 330 | + |
| 331 | + // Merge parameter claims with accumulated claims (parameters take precedence) |
| 332 | + $allClaims = array_merge($this->additionalClaims, $additionalClaims); |
| 333 | + |
176 | 334 | $response = $this->getController()->getResponse(); |
177 | | - $response = $this->authorizationService->setCookie($response, $subscribe, $additionalClaims); |
| 335 | + $response = $this->authorizationService->setCookie($response, $allSubscribe, $allClaims); |
178 | 336 | $this->getController()->setResponse($response); |
179 | 337 |
|
| 338 | + // Reset accumulated state after authorization |
| 339 | + $this->resetSubscribe(); |
| 340 | + $this->resetAdditionalClaims(); |
| 341 | + |
180 | 342 | return $this; |
181 | 343 | } |
182 | 344 |
|
|
0 commit comments