ワンクリックで
developing-new-attachables
// Turn Eloquent models into rich text content attachments (e.g., user mentions, embedded resources) using the AttachableContract and Attachable trait.
// Turn Eloquent models into rich text content attachments (e.g., user mentions, embedded resources) using the AttachableContract and Attachable trait.
| name | developing-new-attachables |
| description | Turn Eloquent models into rich text content attachments (e.g., user mentions, embedded resources) using the AttachableContract and Attachable trait. |
Use this skill when turning an Eloquent model into a content attachment for rich text editors (e.g., user mentions, embedded resources), or when creating custom non-model attachables (e.g., OpenGraph embeds).
The model must implement AttachableContract and use the Attachable trait:
@verbatim
use Tonysm\RichTextLaravel\Attachables\AttachableContract;
use Tonysm\RichTextLaravel\Attachables\Attachable;
class User extends Model implements AttachableContract
{
use Attachable;
// ...
}
@endverbatim
richTextRender() MethodThis is the only required method. It returns the HTML used when displaying the attachment outside the editor:
@verbatim
public function richTextRender(array $options = []): string
{
return view('mentions.partials.user', [
'user' => $this,
])->render();
}
@endverbatim
The $options array may contain an in_gallery boolean when the attachment is rendered inside a gallery.
Create a Blade view that renders the attachment's HTML. This is the content end users will see:
@verbatim
{{-- resources/views/mentions/partials/user.blade.php --}}
<span class="mention">
{{ $user->name }}
</span>
@endverbatim
For plain text export (toPlainText()), implement:
@verbatim
public function richTextAsPlainText(?string $caption = null): string
{
return $this->name;
}
@endverbatim
For Markdown export (toMarkdown()), implement:
@verbatim
public function richTextAsMarkdown(?string $caption = null): string
{
return $caption ?: $this->name;
}
@endverbatim
If these methods are not implemented and no caption is stored, the attachment won't appear in the respective output.
The Attachable trait provides sensible defaults for these methods, but you can override them:
richTextContentType(): string — defaults to 'application/octet-stream'richTextFilename(): ?string — defaults to nullrichTextFilesize(): ?int — defaults to nullrichTextPreviewable(): bool — defaults to falserichTextMetadata(?string $key = null): mixed — defaults to nullFor better organization, extract the attachable behavior into its own trait:
@verbatim
// app/Models/User/Mentionee.php
namespace App\Models\User;
use Tonysm\RichTextLaravel\Attachables\Attachable;
trait Mentionee
{
use Attachable;
public function richTextRender(array $options = []): string
{
return view('mentions.partials.user', ['user' => $this])->render();
}
public function richTextAsPlainText(?string $caption = null): string
{
return e($this->name);
}
public function richTextAsMarkdown(?string $caption = null): string
{
return $caption ?: e($this->name);
}
}
@endverbatim
Then use it on the model:
@verbatim
class User extends Model implements AttachableContract
{
use User\Mentionee;
}
@endverbatim
For attachments that don't need a database record (e.g., OpenGraph embeds), implement AttachableContract directly on a plain class and register a custom resolver:
@verbatim
use Tonysm\RichTextLaravel\RichTextLaravel;
// In AppServiceProvider::boot()
RichTextLaravel::withCustomAttachables(function (DOMElement $node) {
if ($attachable = OpengraphEmbed::fromNode($node)) {
return $attachable;
}
});
@endverbatim
The class must implement toRichTextAttributes(), equalsToAttachable(), and richTextRender(). Use a content-type attribute on the node to identify your custom attachment type:
@verbatim
class OpengraphEmbed implements AttachableContract
{
const CONTENT_TYPE = 'application/vnd.rich-text-laravel.opengraph-embed';
public static function fromNode(DOMElement $node): ?self
{
if ($node->getAttribute('content-type') === static::CONTENT_TYPE) {
return new self(/* attributes from node */);
}
return null;
}
public function richTextRender(array $options = []): string
{
return view('attachables.opengraph-embed', ['attachable' => $this])->render();
}
public function toRichTextAttributes(array $attributes): array
{
return [
'content_type' => static::CONTENT_TYPE,
'previewable' => true,
// ... your custom attributes
];
}
public function equalsToAttachable(AttachableContract $attachable): bool
{
return $this->richTextRender() === $attachable->richTextRender();
}
}
@endverbatim
The Attachable trait automatically generates a Signed Global ID (SGID) for the model via richTextSgid(). This SGID is stored in the <rich-text-attachment sgid="..."> tag in the canonical HTML. When content is rendered, the AttachableFactory resolves the SGID back to the original model. SGIDs are signed using your APP_KEY and never expire for rich text attachments.
On the frontend, create attachments in the editor when a user selects an attachable. The key data needed from your API endpoint is:
sgid — the model's Signed Global IDcontent — the HTML to display inside the editorFor Trix, create a new Trix.Attachment({ sgid, content, contentType: '...' }) and insert it. For Lexxy or custom editors, the mechanism differs but the concept is the same: embed an attachment node with the sgid attribute.
After saving, extract all attachables of a specific type:
@verbatim
use Tonysm\RichTextLaravel\Attachment;
$post->body->attachments()
->filter(fn (Attachment $attachment) => $attachment->attachable instanceof User)
->map(fn (Attachment $attachment) => $attachment->attachable)
->unique();
@endverbatim