diff --git a/extensions/mentions/src/Formatter/FormatGroupMentions.php b/extensions/mentions/src/Formatter/FormatGroupMentions.php index 0c8881a837..e9398f58e1 100644 --- a/extensions/mentions/src/Formatter/FormatGroupMentions.php +++ b/extensions/mentions/src/Formatter/FormatGroupMentions.php @@ -9,9 +9,9 @@ namespace Flarum\Mentions\Formatter; +use Flarum\Database\AbstractModel; use Flarum\Group\Group; use Flarum\Locale\TranslatorInterface; -use Flarum\Post\Post; use s9e\TextFormatter\Renderer; use s9e\TextFormatter\Utils; @@ -25,8 +25,8 @@ public function __construct( public function __invoke(Renderer $renderer, mixed $context, string $xml): string { return Utils::replaceAttributes($xml, 'GROUPMENTION', function ($attributes) use ($context) { - $group = (($context && isset($context->getRelations()['mentionsGroups'])) || $context instanceof Post) - ? $context->mentionsGroups->find($attributes['id']) + $group = ($context instanceof AbstractModel && $context->isRelation('mentionsGroups')) + ? $context->mentionsGroups->find($attributes['id']) // @phpstan-ignore-line : Group::find($attributes['id']); if ($group) { diff --git a/extensions/mentions/src/Formatter/FormatPostMentions.php b/extensions/mentions/src/Formatter/FormatPostMentions.php index 35759f55d2..f020f7f5ff 100644 --- a/extensions/mentions/src/Formatter/FormatPostMentions.php +++ b/extensions/mentions/src/Formatter/FormatPostMentions.php @@ -9,11 +9,11 @@ namespace Flarum\Mentions\Formatter; +use Flarum\Database\AbstractModel; use Flarum\Discussion\Discussion; use Flarum\Http\SlugManager; use Flarum\Locale\TranslatorInterface; use Flarum\Post\Post; -use Psr\Http\Message\ServerRequestInterface as Request; use s9e\TextFormatter\Renderer; use s9e\TextFormatter\Utils; @@ -27,18 +27,12 @@ public function __construct( /** * Configure rendering for post mentions. - * - * @param \s9e\TextFormatter\Renderer $renderer - * @param mixed $context - * @param string $xml - * @param \Psr\Http\Message\ServerRequestInterface|null $request - * @return string $xml to be rendered */ - public function __invoke(Renderer $renderer, $context, $xml, Request $request = null) + public function __invoke(Renderer $renderer, mixed $context, string $xml): string { return Utils::replaceAttributes($xml, 'POSTMENTION', function ($attributes) use ($context) { - $post = (($context && isset($context->getRelations()['mentionsPosts'])) || $context instanceof Post) - ? $context->mentionsPosts->find($attributes['id']) + $post = ($context instanceof AbstractModel && $context->isRelation('mentionsPosts')) + ? $context->mentionsPosts->find($attributes['id']) // @phpstan-ignore-line : Post::find($attributes['id']); if ($post && $post->user) { diff --git a/extensions/mentions/src/Formatter/FormatTagMentions.php b/extensions/mentions/src/Formatter/FormatTagMentions.php index b867969ca9..e3c15af69f 100644 --- a/extensions/mentions/src/Formatter/FormatTagMentions.php +++ b/extensions/mentions/src/Formatter/FormatTagMentions.php @@ -9,7 +9,7 @@ namespace Flarum\Mentions\Formatter; -use Flarum\Post\Post; +use Flarum\Database\AbstractModel; use Flarum\Tags\Tag; use Psr\Http\Message\ServerRequestInterface as Request; use s9e\TextFormatter\Renderer; @@ -17,12 +17,12 @@ class FormatTagMentions { - public function __invoke(Renderer $renderer, mixed $context, ?string $xml, Request $request = null): string + public function __invoke(Renderer $renderer, mixed $context, string $xml, Request $request = null): string { return Utils::replaceAttributes($xml, 'TAGMENTION', function ($attributes) use ($context) { /** @var Tag|null $tag */ - $tag = (($context && isset($context->getRelations()['mentionsTags'])) || $context instanceof Post) - ? $context->mentionsTags->find($attributes['id']) + $tag = ($context instanceof AbstractModel && $context->isRelation('mentionsTags')) + ? $context->mentionsTags->find($attributes['id']) // @phpstan-ignore-line : Tag::query()->find($attributes['id']); if ($tag) { diff --git a/extensions/mentions/src/Formatter/FormatUserMentions.php b/extensions/mentions/src/Formatter/FormatUserMentions.php index feefd58b44..74d002940f 100644 --- a/extensions/mentions/src/Formatter/FormatUserMentions.php +++ b/extensions/mentions/src/Formatter/FormatUserMentions.php @@ -9,9 +9,9 @@ namespace Flarum\Mentions\Formatter; +use Flarum\Database\AbstractModel; use Flarum\Http\SlugManager; use Flarum\Locale\TranslatorInterface; -use Flarum\Post\Post; use Flarum\User\User; use s9e\TextFormatter\Renderer; use s9e\TextFormatter\Utils; @@ -27,8 +27,8 @@ public function __construct( public function __invoke(Renderer $renderer, mixed $context, string $xml): string { return Utils::replaceAttributes($xml, 'USERMENTION', function ($attributes) use ($context) { - $user = (($context && isset($context->getRelations()['mentionsUsers'])) || $context instanceof Post) - ? $context->mentionsUsers->find($attributes['id']) + $user = ($context instanceof AbstractModel && $context->isRelation('mentionsUsers')) + ? $context->mentionsUsers->find($attributes['id']) // @phpstan-ignore-line : User::find($attributes['id']); $attributes['deleted'] = false; diff --git a/extensions/mentions/src/Formatter/UnparsePostMentions.php b/extensions/mentions/src/Formatter/UnparsePostMentions.php index 2b4b8a062e..c0e138846d 100644 --- a/extensions/mentions/src/Formatter/UnparsePostMentions.php +++ b/extensions/mentions/src/Formatter/UnparsePostMentions.php @@ -9,6 +9,7 @@ namespace Flarum\Mentions\Formatter; +use Flarum\Database\AbstractModel; use Flarum\Locale\TranslatorInterface; use Flarum\Post\Post; use s9e\TextFormatter\Utils; @@ -33,8 +34,8 @@ public function __invoke(mixed $context, string $xml): string protected function updatePostMentionTags(mixed $context, string $xml): string { return Utils::replaceAttributes($xml, 'POSTMENTION', function ($attributes) use ($context) { - $post = (($context && isset($context->getRelations()['mentionsPosts'])) || $context instanceof Post) - ? $context->mentionsPosts->find($attributes['id']) + $post = ($context instanceof AbstractModel && $context->isRelation('mentionsPosts')) + ? $context->mentionsPosts->find($attributes['id']) // @phpstan-ignore-line : Post::find($attributes['id']); if ($post && $post->user) { diff --git a/extensions/mentions/src/Formatter/UnparseTagMentions.php b/extensions/mentions/src/Formatter/UnparseTagMentions.php index 0164c16ae3..06eb0f3735 100644 --- a/extensions/mentions/src/Formatter/UnparseTagMentions.php +++ b/extensions/mentions/src/Formatter/UnparseTagMentions.php @@ -9,7 +9,7 @@ namespace Flarum\Mentions\Formatter; -use Flarum\Post\Post; +use Flarum\Database\AbstractModel; use Flarum\Tags\Tag; use s9e\TextFormatter\Utils; @@ -29,8 +29,8 @@ protected function updateTagMentionTags(mixed $context, string $xml): string { return Utils::replaceAttributes($xml, 'TAGMENTION', function (array $attributes) use ($context) { /** @var Tag|null $tag */ - $tag = (($context && isset($context->getRelations()['mentionsTags'])) || $context instanceof Post) - ? $context->mentionsTags->find($attributes['id']) + $tag = ($context instanceof AbstractModel && $context->isRelation('mentionsTags')) + ? $context->mentionsTags->find($attributes['id']) // @phpstan-ignore-line : Tag::query()->find($attributes['id']); if ($tag) { diff --git a/extensions/mentions/src/Formatter/UnparseUserMentions.php b/extensions/mentions/src/Formatter/UnparseUserMentions.php index 63c9af0184..a278166f9a 100644 --- a/extensions/mentions/src/Formatter/UnparseUserMentions.php +++ b/extensions/mentions/src/Formatter/UnparseUserMentions.php @@ -9,8 +9,8 @@ namespace Flarum\Mentions\Formatter; +use Flarum\Database\AbstractModel; use Flarum\Locale\TranslatorInterface; -use Flarum\Post\Post; use Flarum\User\User; use s9e\TextFormatter\Utils; @@ -34,8 +34,8 @@ public function __invoke(mixed $context, string $xml): string protected function updateUserMentionTags(mixed $context, string $xml): string { return Utils::replaceAttributes($xml, 'USERMENTION', function ($attributes) use ($context) { - $user = (($context && isset($context->getRelations()['mentionsUsers'])) || $context instanceof Post) - ? $context->mentionsUsers->find($attributes['id']) + $user = ($context instanceof AbstractModel && $context->isRelation('mentionsUsers')) + ? $context->mentionsUsers->find($attributes['id']) // @phpstan-ignore-line : User::find($attributes['id']); $attributes['displayname'] = $user?->display_name ?? $this->translator->trans('core.lib.username.deleted_text'); diff --git a/extensions/messages/extend.php b/extensions/messages/extend.php index a828831697..bac98a3ae3 100644 --- a/extensions/messages/extend.php +++ b/extensions/messages/extend.php @@ -83,5 +83,7 @@ ->type(Notification\MessageReceivedBlueprint::class, ['email']), (new Extend\Event()) - ->listen(DialogMessage\Event\Created::class, Listener\SendNotificationWhenMessageSent::class), + ->listen(DialogMessage\Event\Created::class, Listener\SendNotificationWhenMessageSent::class) + ->listen(DialogMessage\Event\Created::class, Listener\UpdateMentionsMetadataWhenVisible::class) + ->listen(DialogMessage\Event\Updated::class, Listener\UpdateMentionsMetadataWhenVisible::class), ]; diff --git a/extensions/messages/js/src/forum/components/MessageComposer.tsx b/extensions/messages/js/src/forum/components/MessageComposer.tsx index 2c41aec97e..b0520ae1d0 100644 --- a/extensions/messages/js/src/forum/components/MessageComposer.tsx +++ b/extensions/messages/js/src/forum/components/MessageComposer.tsx @@ -134,7 +134,6 @@ export default class MessageComposer { this.composer.hide(); - // @todo: app.dialogs.refresh(); // @ts-ignore m.route.set(app.route('dialog', { id: message.data.relationships!.dialog.data.id })); this.attrs.onsubmit?.(message); diff --git a/extensions/messages/js/src/forum/components/MessageStream.tsx b/extensions/messages/js/src/forum/components/MessageStream.tsx index 00539e34ba..912dfb2313 100644 --- a/extensions/messages/js/src/forum/components/MessageStream.tsx +++ b/extensions/messages/js/src/forum/components/MessageStream.tsx @@ -117,9 +117,8 @@ export default class MessageStream import('./MessageComposer'), { user: app.session.user, replyingTo: this.attrs.dialog, - onsubmit: (message: DialogMessage) => { - this.attrs.state.push(message); - setTimeout(() => this.scrollToBottom(), 50); + onsubmit: () => { + this.attrs.state.refresh().then(() => setTimeout(() => this.scrollToBottom(), 50)); }, }) .then(() => app.composer.show()); diff --git a/extensions/messages/migrations/2024_09_01_000000_create_dialogs_table.php b/extensions/messages/migrations/2024_09_01_000000_create_dialogs_table.php index 1e99209ff5..54b50750f8 100644 --- a/extensions/messages/migrations/2024_09_01_000000_create_dialogs_table.php +++ b/extensions/messages/migrations/2024_09_01_000000_create_dialogs_table.php @@ -13,9 +13,9 @@ return Migration::createTable( 'dialogs', function (Blueprint $table) { - $table->bigIncrements('id'); - $table->unsignedBigInteger('first_message_id')->nullable(); - $table->unsignedBigInteger('last_message_id')->nullable(); + $table->increments('id'); + $table->unsignedInteger('first_message_id')->nullable(); + $table->unsignedInteger('last_message_id')->nullable(); $table->dateTime('last_message_at')->nullable(); $table->unsignedInteger('last_message_user_id')->nullable(); $table->foreign('last_message_user_id')->references('id')->on('users')->nullOnDelete(); diff --git a/extensions/messages/migrations/2024_09_01_000001_create_dialog_messages_table.php b/extensions/messages/migrations/2024_09_01_000001_create_dialog_messages_table.php index 20657e5ade..85c945f1d6 100644 --- a/extensions/messages/migrations/2024_09_01_000001_create_dialog_messages_table.php +++ b/extensions/messages/migrations/2024_09_01_000001_create_dialog_messages_table.php @@ -13,8 +13,9 @@ return Migration::createTable( 'dialog_messages', function (Blueprint $table) { - $table->bigIncrements('id'); - $table->foreignId('dialog_id')->constrained()->cascadeOnDelete(); + $table->increments('id'); + $table->unsignedInteger('dialog_id'); + $table->foreign('dialog_id')->references('id')->on('dialogs')->cascadeOnDelete(); $table->unsignedInteger('user_id')->nullable(); $table->foreign('user_id')->references('id')->on('users')->cascadeOnDelete(); $table->text('content'); diff --git a/extensions/messages/migrations/2024_09_01_000002_create_dialog_user_table.php b/extensions/messages/migrations/2024_09_01_000002_create_dialog_user_table.php index 6ffe56e5a6..1c00b1be30 100644 --- a/extensions/messages/migrations/2024_09_01_000002_create_dialog_user_table.php +++ b/extensions/messages/migrations/2024_09_01_000002_create_dialog_user_table.php @@ -14,10 +14,11 @@ 'dialog_user', function (Blueprint $table) { $table->id(); - $table->foreignId('dialog_id')->constrained()->cascadeOnDelete(); + $table->unsignedInteger('dialog_id'); + $table->foreign('dialog_id')->references('id')->on('dialogs')->cascadeOnDelete(); $table->unsignedInteger('user_id'); $table->dateTime('joined_at'); - $table->unsignedBigInteger('last_read_message_id')->default(0); + $table->unsignedInteger('last_read_message_id')->default(0); $table->dateTime('last_read_at')->nullable(); $table->foreign('user_id')->references('id')->on('users')->cascadeOnDelete(); } diff --git a/extensions/messages/migrations/2024_10_19_000000_create_dialog_message_mentions_user_table.php b/extensions/messages/migrations/2024_10_19_000000_create_dialog_message_mentions_user_table.php new file mode 100644 index 0000000000..e31bd05f39 --- /dev/null +++ b/extensions/messages/migrations/2024_10_19_000000_create_dialog_message_mentions_user_table.php @@ -0,0 +1,24 @@ +unsignedInteger('dialog_message_id'); + $table->unsignedInteger('mentions_user_id'); + $table->dateTime('created_at')->nullable()->useCurrent(); + + $table->primary(['dialog_message_id', 'mentions_user_id']); + $table->foreign('dialog_message_id')->references('id')->on('dialog_messages')->cascadeOnDelete(); + $table->foreign('mentions_user_id')->references('id')->on('users')->cascadeOnDelete(); + } +); diff --git a/extensions/messages/migrations/2024_10_19_000001_create_dialog_message_mentions_post_table.php b/extensions/messages/migrations/2024_10_19_000001_create_dialog_message_mentions_post_table.php new file mode 100644 index 0000000000..ae441750c0 --- /dev/null +++ b/extensions/messages/migrations/2024_10_19_000001_create_dialog_message_mentions_post_table.php @@ -0,0 +1,24 @@ +unsignedInteger('dialog_message_id'); + $table->unsignedInteger('mentions_post_id'); + $table->dateTime('created_at')->nullable()->useCurrent(); + + $table->primary(['dialog_message_id', 'mentions_post_id']); + $table->foreign('dialog_message_id')->references('id')->on('dialog_messages')->cascadeOnDelete(); + $table->foreign('mentions_post_id')->references('id')->on('posts')->cascadeOnDelete(); + } +); diff --git a/extensions/messages/migrations/2024_10_19_000002_create_dialog_message_mentions_group_table.php b/extensions/messages/migrations/2024_10_19_000002_create_dialog_message_mentions_group_table.php new file mode 100644 index 0000000000..bb52d1a18f --- /dev/null +++ b/extensions/messages/migrations/2024_10_19_000002_create_dialog_message_mentions_group_table.php @@ -0,0 +1,24 @@ +unsignedInteger('dialog_message_id'); + $table->unsignedInteger('mentions_group_id'); + $table->dateTime('created_at')->nullable()->useCurrent(); + + $table->primary(['dialog_message_id', 'mentions_group_id']); + $table->foreign('dialog_message_id')->references('id')->on('dialog_messages')->cascadeOnDelete(); + $table->foreign('mentions_group_id')->references('id')->on('groups')->cascadeOnDelete(); + } +); diff --git a/extensions/messages/migrations/2024_10_19_000002_create_dialog_message_mentions_tag_table.php b/extensions/messages/migrations/2024_10_19_000002_create_dialog_message_mentions_tag_table.php new file mode 100644 index 0000000000..d81c19be04 --- /dev/null +++ b/extensions/messages/migrations/2024_10_19_000002_create_dialog_message_mentions_tag_table.php @@ -0,0 +1,24 @@ +unsignedInteger('dialog_message_id'); + $table->unsignedInteger('mentions_tag_id'); + $table->dateTime('created_at')->nullable()->useCurrent(); + + $table->primary(['dialog_message_id', 'mentions_tag_id']); + $table->foreign('dialog_message_id')->references('id')->on('dialog_messages')->cascadeOnDelete(); + $table->foreign('mentions_tag_id')->references('id')->on('tags')->cascadeOnDelete(); + } +); diff --git a/extensions/messages/src/Api/Resource/DialogMessageResource.php b/extensions/messages/src/Api/Resource/DialogMessageResource.php index a54cf12987..06a91522fb 100644 --- a/extensions/messages/src/Api/Resource/DialogMessageResource.php +++ b/extensions/messages/src/Api/Resource/DialogMessageResource.php @@ -16,6 +16,7 @@ use Flarum\Api\Schema; use Flarum\Api\Sort\SortColumn; use Flarum\Bus\Dispatcher; +use Flarum\Extension\ExtensionManager; use Flarum\Foundation\ErrorHandling\LogReporter; use Flarum\Foundation\ValidationException; use Flarum\Locale\Translator; @@ -36,6 +37,7 @@ public function __construct( protected Translator $translator, protected LogReporter $log, protected Dispatcher $bus, + protected ExtensionManager $extensions, ) { } @@ -77,6 +79,20 @@ public function endpoints(): array }), Endpoint\Index::make() ->authenticated() + ->defaultInclude([ + 'user', + 'mentionsUsers', + 'mentionsPosts', + 'mentionsGroups', + 'mentionsTags', + ]) + ->eagerLoad(function () { + if ($this->extensions->isEnabled('flarum-mentions')) { + return ['mentionsUsers', 'mentionsPosts', 'mentionsGroups', 'mentionsTags']; + } + + return []; + }) ->paginate(), ]; } @@ -126,6 +142,18 @@ public function fields(): array ->includable() ->writableOnCreate() ->requiredOnCreateWithout(['attributes.users']), + Schema\Relationship\ToMany::make('mentionsUsers') + ->type('users') + ->includable(), + Schema\Relationship\ToMany::make('mentionsPosts') + ->type('posts') + ->includable(), + Schema\Relationship\ToMany::make('mentionsGroups') + ->type('groups') + ->includable(), + Schema\Relationship\ToMany::make('mentionsTags') + ->type('tags') + ->includable(), ]; } diff --git a/extensions/messages/src/DialogMessage.php b/extensions/messages/src/DialogMessage.php index 94277772a7..411b4620b4 100644 --- a/extensions/messages/src/DialogMessage.php +++ b/extensions/messages/src/DialogMessage.php @@ -10,12 +10,17 @@ namespace Flarum\Messages; use Flarum\Database\AbstractModel; +use Flarum\Database\Eloquent\Collection; use Flarum\Database\ScopeVisibilityTrait; use Flarum\Formatter\Formattable; use Flarum\Formatter\HasFormattedContent; use Flarum\Foundation\EventGeneratorTrait; +use Flarum\Group\Group; +use Flarum\Post\Post; +use Flarum\Tags\Tag; use Flarum\User\User; use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\BelongsToMany; /** * @property int $id @@ -26,6 +31,10 @@ * @property \Carbon\Carbon $updated_at * @property-read Dialog $dialog * @property-read User|null $user + * @property-read Collection $mentionsUsers + * @property-read Collection $mentionsPosts + * @property-read Collection $mentionsGroups + * @property-read Collection $mentionsTags */ class DialogMessage extends AbstractModel implements Formattable { @@ -48,4 +57,24 @@ public function user(): BelongsTo { return $this->belongsTo(User::class); } + + public function mentionsUsers(): BelongsToMany + { + return $this->belongsToMany(User::class, 'dialog_message_mentions_user', 'dialog_message_id', 'mentions_user_id'); + } + + public function mentionsPosts(): BelongsToMany + { + return $this->belongsToMany(Post::class, 'dialog_message_mentions_post', 'dialog_message_id', 'mentions_post_id'); + } + + public function mentionsGroups(): BelongsToMany + { + return $this->belongsToMany(Group::class, 'dialog_message_mentions_group', 'dialog_message_id', 'mentions_group_id'); + } + + public function mentionsTags(): BelongsToMany + { + return $this->belongsToMany(Tag::class, 'dialog_message_mentions_tag', 'dialog_message_id', 'mentions_tag_id'); + } } diff --git a/extensions/messages/src/DialogMessage/Event/Creating.php b/extensions/messages/src/DialogMessage/Event/Creating.php index 0e62fc6301..9fe1ec997b 100644 --- a/extensions/messages/src/DialogMessage/Event/Creating.php +++ b/extensions/messages/src/DialogMessage/Event/Creating.php @@ -14,8 +14,8 @@ class Creating { public function __construct( - protected DialogMessage $message, - protected array $data + public DialogMessage $message, + public array $data ) { } } diff --git a/extensions/messages/src/DialogMessage/Event/Updated.php b/extensions/messages/src/DialogMessage/Event/Updated.php index a35d0a54d9..09a5534eb6 100644 --- a/extensions/messages/src/DialogMessage/Event/Updated.php +++ b/extensions/messages/src/DialogMessage/Event/Updated.php @@ -14,7 +14,7 @@ class Updated { public function __construct( - protected DialogMessage $message + public DialogMessage $message ) { } } diff --git a/extensions/messages/src/DialogMessage/Event/Updating.php b/extensions/messages/src/DialogMessage/Event/Updating.php index 4104ad5f9e..1348ada603 100644 --- a/extensions/messages/src/DialogMessage/Event/Updating.php +++ b/extensions/messages/src/DialogMessage/Event/Updating.php @@ -14,8 +14,8 @@ class Updating { public function __construct( - protected DialogMessage $message, - protected array $data + public DialogMessage $message, + public array $data ) { } } diff --git a/extensions/messages/src/Listener/UpdateMentionsMetadataWhenVisible.php b/extensions/messages/src/Listener/UpdateMentionsMetadataWhenVisible.php new file mode 100755 index 0000000000..fa9ab2362f --- /dev/null +++ b/extensions/messages/src/Listener/UpdateMentionsMetadataWhenVisible.php @@ -0,0 +1,77 @@ +message instanceof DialogMessage) { + return; + } + + $content = $event->message->parsed_content; + + $this->syncUserMentions( + $event->message, + Utils::getAttributeValues($content, 'USERMENTION', 'id') + ); + + $this->syncPostMentions( + $event->message, + Utils::getAttributeValues($content, 'POSTMENTION', 'id') + ); + + $this->syncGroupMentions( + $event->message, + Utils::getAttributeValues($content, 'GROUPMENTION', 'id') + ); + + if ($this->extensions->isEnabled('flarum-tags')) { + $this->syncTagMentions( + $event->message, + Utils::getAttributeValues($content, 'TAGMENTION', 'id') + ); + } + } + + protected function syncUserMentions(DialogMessage $message, array $mentioned): void + { + $message->mentionsUsers()->sync($mentioned); + $message->unsetRelation('mentionsUsers'); + } + + protected function syncPostMentions(DialogMessage $message, array $mentioned): void + { + $message->mentionsPosts()->sync($mentioned); + $message->unsetRelation('mentionsPosts'); + } + + protected function syncGroupMentions(DialogMessage $message, array $mentioned): void + { + $message->mentionsGroups()->sync($mentioned); + $message->unsetRelation('mentionsGroups'); + } + + protected function syncTagMentions(DialogMessage $message, array $mentioned): void + { + $message->mentionsTags()->sync($mentioned); + $message->unsetRelation('mentionsTags'); + } +}