Skip to content

Commit

Permalink
[Turbo] Add support for providing multiple mercure topics to `turbo_s…
Browse files Browse the repository at this point in the history
…tream_listen`
  • Loading branch information
norkunas committed Nov 26, 2024
1 parent 3ea19c1 commit 8067164
Show file tree
Hide file tree
Showing 7 changed files with 49 additions and 16 deletions.
1 change: 1 addition & 0 deletions src/Turbo/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- Add `<twig:Turbo:Stream>` component
- Add `<twig:Turbo:Frame>` component
- Add support for custom actions in `TurboStream` and `TurboStreamResponse`
- Add support for providing multiple mercure topics to `turbo_stream_listen`

## 2.21.0

Expand Down
3 changes: 3 additions & 0 deletions src/Turbo/assets/dist/turbo_stream_controller.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@ import { Controller } from '@hotwired/stimulus';
export default class extends Controller {
static values: {
topic: StringConstructor;
topics: ArrayConstructor;
hub: StringConstructor;
};
es: EventSource | undefined;
url: string | undefined;
readonly topicValue: string;
readonly topicsValue: string[];
readonly hubValue: string;
readonly hasHubValue: boolean;
readonly hasTopicValue: boolean;
readonly hasTopicsValue: boolean;
initialize(): void;
connect(): void;
disconnect(): void;
Expand Down
14 changes: 11 additions & 3 deletions src/Turbo/assets/dist/turbo_stream_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,19 @@ class default_1 extends Controller {
const errorMessages = [];
if (!this.hasHubValue)
errorMessages.push('A "hub" value pointing to the Mercure hub must be provided.');
if (!this.hasTopicValue)
errorMessages.push('A "topic" value must be provided.');
if (!this.hasTopicValue && !this.hasTopicsValue)
errorMessages.push('Either "topic" or "topics" value must be provided.');
if (errorMessages.length)
throw new Error(errorMessages.join(' '));
const u = new URL(this.hubValue);
u.searchParams.append('topic', this.topicValue);
if (this.hasTopicValue) {
u.searchParams.append('topic', this.topicValue);
}
else {
this.topicsValue.forEach((topic) => {
u.searchParams.append('topic', topic);
});
}
this.url = u.toString();
}
connect() {
Expand All @@ -29,6 +36,7 @@ class default_1 extends Controller {
}
default_1.values = {
topic: String,
topics: Array,
hub: String,
};

Expand Down
14 changes: 12 additions & 2 deletions src/Turbo/assets/src/turbo_stream_controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,34 @@ import { connectStreamSource, disconnectStreamSource } from '@hotwired/turbo';
export default class extends Controller {
static values = {
topic: String,
topics: Array,
hub: String,
};
es: EventSource | undefined;
url: string | undefined;

declare readonly topicValue: string;
declare readonly topicsValue: string[];
declare readonly hubValue: string;
declare readonly hasHubValue: boolean;
declare readonly hasTopicValue: boolean;
declare readonly hasTopicsValue: boolean;

initialize() {
const errorMessages: string[] = [];
if (!this.hasHubValue) errorMessages.push('A "hub" value pointing to the Mercure hub must be provided.');
if (!this.hasTopicValue) errorMessages.push('A "topic" value must be provided.');
if (!this.hasTopicValue && !this.hasTopicsValue)
errorMessages.push('Either "topic" or "topics" value must be provided.');
if (errorMessages.length) throw new Error(errorMessages.join(' '));

const u = new URL(this.hubValue);
u.searchParams.append('topic', this.topicValue);
if (this.hasTopicValue) {
u.searchParams.append('topic', this.topicValue);
} else {
this.topicsValue.forEach((topic) => {
u.searchParams.append('topic', topic);
});
}

this.url = u.toString();
}
Expand Down
29 changes: 20 additions & 9 deletions src/Turbo/src/Bridge/Mercure/TurboStreamListenRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,23 +44,34 @@ public function __construct(

public function renderTurboStreamListen(Environment $env, $topic): string
{
if (\is_object($topic)) {
$class = $topic::class;
$topics = [];

if (!$id = $this->idAccessor->getEntityId($topic)) {
throw new \LogicException(\sprintf('Cannot listen to entity of class "%s" as the PropertyAccess component is not installed. Try running "composer require symfony/property-access".', $class));
foreach ((array) $topic as $topicVal) {
if (\is_object($topicVal)) {
$class = $topicVal::class;

if (!$id = $this->idAccessor->getEntityId($topicVal)) {
throw new \LogicException(\sprintf('Cannot listen to entity of class "%s" as the PropertyAccess component is not installed. Try running "composer require symfony/property-access".', $class));
}

$topics[] = \sprintf(Broadcaster::TOPIC_PATTERN, rawurlencode($class), rawurlencode(implode('-', $id)));
} elseif (!preg_match('/[^a-zA-Z0-9_\x7f-\xff\\\\]/', $topic) && class_exists($topicVal)) {
// Generate a URI template to subscribe to updates for all objects of this class
$topics[] = \sprintf(Broadcaster::TOPIC_PATTERN, rawurlencode($topicVal), '{id}');
}
}

$topic = \sprintf(Broadcaster::TOPIC_PATTERN, rawurlencode($class), rawurlencode(implode('-', $id)));
} elseif (!preg_match('/[^a-zA-Z0-9_\x7f-\xff\\\\]/', $topic) && class_exists($topic)) {
// Generate a URI template to subscribe to updates for all objects of this class
$topic = \sprintf(Broadcaster::TOPIC_PATTERN, rawurlencode($topic), '{id}');
$controllerAttributes = ['hub' => $this->hub->getPublicUrl()];
if (1 < \count($topics)) {
$controllerAttributes['topics'] = $topics;
} else {
$controllerAttributes['topic'] = current($topics);
}

$stimulusAttributes = $this->stimulusHelper->createStimulusAttributes();
$stimulusAttributes->addController(
'symfony/ux-turbo/mercure-turbo-stream',
['topic' => $topic, 'hub' => $this->hub->getPublicUrl()]
$controllerAttributes,
);

return (string) $stimulusAttributes;
Expand Down
2 changes: 1 addition & 1 deletion src/Turbo/src/Twig/TurboStreamListenRendererInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
interface TurboStreamListenRendererInterface
{
/**
* @param string|object $topic
* @param string|object|array<string> $topic
*/
public function renderTurboStreamListen(Environment $env, $topic): string;
}
2 changes: 1 addition & 1 deletion src/Turbo/src/Twig/TwigExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public function getFunctions(): array
}

/**
* @param object|string $topic
* @param object|string|array<string> $topic
*/
public function turboStreamListen(Environment $env, $topic, ?string $transport = null): string
{
Expand Down

0 comments on commit 8067164

Please sign in to comment.