Skip to content

Commit 636f72f

Browse files
committed
feat: introduce MercureHelper::addTopic()/AddTopics()
Also drop MercureHelper::buildUrl as this is confusing and not needed
1 parent 5420be8 commit 636f72f

File tree

3 files changed

+311
-98
lines changed

3 files changed

+311
-98
lines changed

README.md

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -578,23 +578,20 @@ eventSource.onmessage = (event) => {
578578
</script>
579579
```
580580
581-
> [!WARNING]
582-
> **Important:** The `url()` method **only sets authorization when the `$subscribe` parameter is provided**. If you're already handling authorization in your controller (via `MercureComponent`), you can use either:
581+
> [!NOTE]
582+
> **Separation of Concerns:** The `url()` method **only sets authorization when the `$subscribe` parameter is provided**. If you're already handling authorization in your controller (via `MercureComponent`), simply omit the `$subscribe` parameter:
583583
>
584584
> ```php
585585
> // Authorization already set in controller
586586
> $this->Mercure->authorize(['/books/123']);
587587
>
588-
> // In template: either of these works (no duplicate authorization)
589-
> const url1 = '<?= $this->Mercure->url(['/books/123']) ?>'; // No $subscribe = no auth
590-
> const url2 = '<?= $this->Mercure->getHubUrl(['/books/123']) ?>'; // Explicit
588+
> // In template: just get the URL (no duplicate authorization)
589+
> const url = '<?= $this->Mercure->url(['/books/123']) ?>';
591590
> ```
592-
>
593-
> Use `getHubUrl()` when you want to be explicit that authorization is handled elsewhere.
594591
595592
#### Setting Default Topics
596593
597-
You can configure default topics that will be automatically merged with any topics you provide to `url()` or `getHubUrl()`. This is useful when you want certain topics (like notifications or global alerts) to be included in every subscription:
594+
You can configure default topics that will be automatically merged with any topics you provide to `url()`. This is useful when you want certain topics (like notifications or global alerts) to be included in every subscription:
598595
599596
```php
600597
// In your controller or AppView
@@ -612,18 +609,23 @@ public function initialize(): void
612609
}
613610
```
614611
615-
Now every call to `url()` or `getHubUrl()` will automatically include these default topics:
612+
Now every call to `url()` will automatically include these default topics:
616613
617614
```php
618615
// In your template
619616
<script>
620617
// This will subscribe to: /notifications, /alerts, AND /books/123
621618
const url = '<?= $this->Mercure->url(['/books/123']) ?>';
622619
const eventSource = new EventSource(url, { withCredentials: true });
623-
624-
// Access configured defaults if needed
625-
const defaults = <?= json_encode($this->Mercure->getConfig('defaultTopics', [])) ?>;
626620
</script>
621+
622+
// You can also add topics dynamically:
623+
$this->Mercure->addTopic('/user/' . $userId . '/messages');
624+
$this->Mercure->addTopics(['/books/456', '/comments/789']);
625+
626+
// These will be merged with configured defaults
627+
const url = '<?= $this->Mercure->url(['/books/123']) ?>';
628+
// Result includes: /notifications, /alerts, /user/{id}/messages, /books/456, /comments/789, AND /books/123
627629
```
628630
629631
#### Using the Facade (Alternative)
@@ -999,7 +1001,8 @@ Static facade for direct authorization management (alternative to component).
9991001
| Method | Returns | Description |
10001002
|--------|---------|-------------|
10011003
| `url(array\|string\|null $topics, array $subscribe, array $additionalClaims)` | `string` | Get hub URL **and optionally authorize** (only sets cookie when `$subscribe` is provided). Merges with default topics if configured. |
1002-
| `getHubUrl(array $topics, array $options)` | `string` | Get hub URL **without authorization** (explicit, no cookie ever set). Merges with default topics if configured. |
1004+
| `addTopic(string $topic)` | `$this` | Add a single topic to default topics (fluent interface) |
1005+
| `addTopics(array $topics)` | `$this` | Add multiple topics to default topics (fluent interface) |
10031006
| `authorize(array $subscribe, array $additionalClaims)` | `void` | Set authorization cookie explicitly |
10041007
| `clearAuthorization()` | `void` | Clear authorization cookie |
10051008
| `discover()` | `void` | Add Mercure discovery Link header |
@@ -1010,7 +1013,7 @@ Static facade for direct authorization management (alternative to component).
10101013
10111014
| Option | Type | Default | Description |
10121015
|--------|------|---------|-------------|
1013-
| `defaultTopics` | `array` | `[]` | Topics to automatically merge with every subscription |
1016+
| `defaultTopics` | `array` | `[]` | Topics to automatically merge with every subscription (read-only, not mutated by `addTopic()`/`addTopics()`) |
10141017
10151018
### Update
10161019

src/View/Helper/MercureHelper.php

Lines changed: 59 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@
3434
* // Default topics will be merged with provided topics
3535
* $hubUrl = $this->Mercure->url(['/books/123']); // Result: ['/books/123', '/notifications', '/alerts']
3636
*
37+
* // Add topics dynamically
38+
* $this->Mercure->addTopic('/user/123/messages');
39+
* $this->Mercure->addTopics(['/books/456', '/comments/789']);
40+
*
3741
* // Simple usage: Just get the hub URL with default topics
3842
* $hubUrl = $this->Mercure->url();
3943
*
@@ -53,9 +57,8 @@
5357
* // Add Mercure discovery header
5458
* $this->Mercure->discover();
5559
*
56-
* // Legacy API (still supported):
60+
* // Manual authorization (if needed separately):
5761
* $this->Mercure->authorize(['/books/123', '/notifications']);
58-
* $hubUrl = $this->Mercure->getHubUrl(['/books/123']);
5962
* $this->Mercure->clearAuthorization();
6063
* ```
6164
*/
@@ -71,19 +74,69 @@ class MercureHelper extends Helper
7174
];
7275

7376
/**
74-
* Merge provided topics with default topics (removing duplicates)
77+
* Runtime topics (includes default topics + dynamically added topics)
78+
*
79+
* @var array<string>
80+
*/
81+
protected array $topics = [];
82+
83+
/**
84+
* Initialize callback
85+
*
86+
* @param array<string, mixed> $config Configuration
87+
*/
88+
public function initialize(array $config): void
89+
{
90+
parent::initialize($config);
91+
92+
// Initialize topics from config
93+
$defaultTopics = $this->getConfig('defaultTopics', []);
94+
$this->topics = is_array($defaultTopics) ? $defaultTopics : [];
95+
}
96+
97+
/**
98+
* Add a single topic to the helper's topics
99+
*
100+
* @param string $topic Topic to add
101+
* @return $this
102+
*/
103+
public function addTopic(string $topic)
104+
{
105+
if (!in_array($topic, $this->topics, true)) {
106+
$this->topics[] = $topic;
107+
}
108+
109+
return $this;
110+
}
111+
112+
/**
113+
* Add multiple topics to the helper's topics
114+
*
115+
* @param array<string> $topics Topics to add
116+
* @return $this
117+
*/
118+
public function addTopics(array $topics)
119+
{
120+
foreach ($topics as $topic) {
121+
$this->addTopic($topic);
122+
}
123+
124+
return $this;
125+
}
126+
127+
/**
128+
* Merge provided topics with helper's topics (removing duplicates)
75129
*
76130
* @param array<string> $topics Provided topics
77131
* @return array<string>
78132
*/
79133
protected function mergeTopics(array $topics): array
80134
{
81-
$defaultTopics = $this->getConfig('defaultTopics', []);
82-
if ($defaultTopics === []) {
135+
if ($this->topics === []) {
83136
return $topics;
84137
}
85138

86-
return array_values(array_unique(array_merge($defaultTopics, $topics)));
139+
return array_values(array_unique(array_merge($this->topics, $topics)));
87140
}
88141

89142
/**
@@ -128,31 +181,6 @@ public function url(array|string|null $topics = null, array $subscribe = [], arr
128181
return $this->buildSubscriptionUrl($hubUrl, $topics, []);
129182
}
130183

131-
/**
132-
* Get the Mercure hub URL
133-
*
134-
* Optionally builds a URL with topic query parameters for EventSource connections.
135-
* Default topics (if configured) will be automatically merged with provided topics.
136-
*
137-
* @param array<string> $topics Optional topics to subscribe to
138-
* @param array<string, mixed> $options Additional query parameters
139-
* @return string Hub URL
140-
* @throws \Mercure\Exception\MercureException
141-
*/
142-
public function getHubUrl(array $topics = [], array $options = []): string
143-
{
144-
// Merge with default topics
145-
$topics = $this->mergeTopics($topics);
146-
147-
$hubUrl = ConfigurationHelper::getPublicUrl();
148-
149-
if ($topics === [] && $options === []) {
150-
return $hubUrl;
151-
}
152-
153-
return $this->buildSubscriptionUrl($hubUrl, $topics, $options);
154-
}
155-
156184
/**
157185
* Set authorization cookie for subscriber access to private topics
158186
*

0 commit comments

Comments
 (0)