Zum Hauptinhalt springen

Was ist der TextBuilder?

Der TextBuilder ist eine Klasse, mit der Sie die vom Bluesky-AT-Protocol definierten facets (Rich-Text-Annotations) über Method-Chaining zusammenstellen. Der Body eines Bluesky-Posts ist reiner Text. Um Mentions, Links und Hashtags anzuzeigen, muss neben dem Text ein facets-Array übermittelt werden, das jeweils Position (Byte-Offset) und Typ angibt. Der TextBuilder übernimmt diese Offset-Berechnung und den Array-Aufbau automatisch.

Einfache Textausgabe

TextBuilder::make()

Erzeugen Sie mit TextBuilder::make() eine Instanz mit optionalem Initialtext. Der Initialtext ist optional.
use Revolution\Bluesky\RichText\TextBuilder;

$builder = TextBuilder::make(text: 'Hello Bluesky');

text()

Mit text() hängen Sie Text am Ende an.
$builder = TextBuilder::make()
    ->text('Hello ')
    ->text('Bluesky');

// $builder->text === 'Hello Bluesky'

newLine()

Fügt einen Zeilenumbruch hinzu. Über count können Sie die Zeilenanzahl angeben (Default: 1).
$builder = TextBuilder::make('Zeile 1')
    ->newLine()
    ->text('Zeile 2')
    ->newLine(count: 2)
    ->text('Zeile 4');

toPost()

Wandelt die TextBuilder-Instanz in einen Post-Record um. Kann direkt an Bluesky::post() übergeben werden.
use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\RichText\TextBuilder;

$post = TextBuilder::make('Einfacher Post')->toPost();

$response = Bluesky::withToken()->post($post);

Post::build()

Alternativ übergeben Sie eine Closure an Post::build(). Der Rückgabewert ist ein Post.
use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\Record\Post;
use Revolution\Bluesky\RichText\TextBuilder;

$post = Post::build(function (TextBuilder $builder) {
    $builder->text('Hello Bluesky');
});

$response = Bluesky::withToken()->post($post);

Mention (@mention) hinzufügen

Fügen Sie mit mention() eine Mention-Facette hinzu.
$builder->mention(text: '@alice.bsky.social');

Automatische DID-Auflösung

Wenn Sie did weglassen, wird die DID automatisch aus dem Handle aufgelöst (Bluesky::resolveHandle() wird aufgerufen).
// DID automatisch auflösen (führt einen API-Aufruf aus)
$builder->mention('@alice.bsky.social');

DID explizit angeben

Wenn die DID bekannt ist, können Sie den API-Aufruf vermeiden, indem Sie sie explizit übergeben.
// DID direkt angeben (empfohlen: kein API-Aufruf)
$builder->mention(text: '@alice.bsky.social', did: 'did:plc:xxxxxxxxxxxxxxxxxxxx');
Wenn Sie in der Produktion häufig Mentions verwenden, können Sie die DIDs cachen, um API-Aufrufe zu reduzieren.
Fügen Sie mit link() eine Link-Facette hinzu.
// Die URL selbst als Anzeigetext nutzen
$builder->link('https://laravel.com');

// Anzeigetext und URL getrennt angeben
$builder->link(text: 'Offizielle Laravel-Website', uri: 'https://laravel.com');
Wird uri weggelassen, wird text als URI verwendet.

Hashtags hinzufügen

Fügen Sie mit tag() eine Hashtag-Facette hinzu.
// Text, der mit # beginnt, extrahiert das Tag automatisch
$builder->tag('#Laravel');

// Tag-String explizit angeben (String ohne #)
$builder->tag(text: '#Laravel', tag: 'Laravel');

Zusammengesetzte Texte

Kombinieren Sie mehrere Facetten für reichhaltige Post-Texte.
use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\RichText\TextBuilder;

$post = TextBuilder::make('Ein neuer Artikel wurde veröffentlicht!')
    ->newLine(count: 2)
    ->mention('@alice.bsky.social', did: 'did:plc:xxxx')
    ->text(' sollte ihn unbedingt lesen.')
    ->newLine()
    ->link(text: 'Artikel lesen', uri: 'https://example.com/article/1')
    ->newLine()
    ->tag('#Laravel')
    ->text(' ')
    ->tag('#PHP')
    ->toPost();

$response = Bluesky::withToken()->post($post);

Schreibweise mit Post::build()

use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\Record\Post;
use Revolution\Bluesky\RichText\TextBuilder;

$post = Post::build(function (TextBuilder $builder) {
    $builder->text('Ein neuer Artikel wurde veröffentlicht!')
            ->newLine(count: 2)
            ->mention('@alice.bsky.social', did: 'did:plc:xxxx')
            ->text(' sollte ihn unbedingt lesen.')
            ->newLine()
            ->link(text: 'Artikel lesen', uri: 'https://example.com/article/1')
            ->newLine()
            ->tag('#Laravel')
            ->text(' ')
            ->tag('#PHP');
});

$response = Bluesky::withToken()->post($post);

Integration mit Posts

Kombination mit Bluesky::post()

use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\RichText\TextBuilder;

$post = TextBuilder::make('test')
    ->newLine()
    ->link('https://bsky.app/')
    ->toPost();

$response = Bluesky::withToken()->post($post);

Kombination mit dem Notification-Kanal

In der Methode toBluesky() von BlueskyChannel können Sie Post::build() verwenden.
use Illuminate\Notifications\Notification;
use Revolution\Bluesky\Notifications\BlueskyChannel;
use Revolution\Bluesky\Record\Post;
use Revolution\Bluesky\RichText\TextBuilder;

class DeployedNotification extends Notification
{
    public function __construct(
        private string $url,
    ) {}

    public function via(object $notifiable): array
    {
        return [BlueskyChannel::class];
    }

    public function toBluesky(object $notifiable): Post
    {
        return Post::build(function (TextBuilder $builder) {
            $builder->text('Deployment abgeschlossen')
                    ->newLine()
                    ->link(text: 'Überprüfen', uri: $this->url)
                    ->newLine()
                    ->tag('#Laravel');
        });
    }
}

Facets automatisch erkennen

Es ist auch möglich, @mention, URLs und #hashtag im Text automatisch zu erkennen und facets zu setzen.
use Revolution\Bluesky\RichText\TextBuilder;

$builder = TextBuilder::make('@alice.bsky.social test https://example.com #alice')
    ->detectFacets();

// Nach detectFacets() können Sie weitere facets ergänzen
$builder->newLine()->tag('#bob');

$post = $builder->toPost();
detectFacets() arbeitet regex-basiert. Wenn Sie Links garantiert setzen möchten, ist es sicherer, link(), mention() und tag() explizit zu verwenden.

Eigene Facets hinzufügen

Mit der Methode facet() können Sie ein beliebiges Facet-Array direkt hinzufügen.
use Revolution\Bluesky\RichText\TextBuilder;

$builder = TextBuilder::make();

$builder->facet([
    'index' => [
        'byteStart' => 0,
        'byteEnd' => 5,
    ],
    'features' => [
        [
            '$type' => 'app.bsky.richtext.facet#link',
            'uri' => 'https://example.com',
        ],
    ],
]);

Zeichenbegrenzung und Hinweise

Byte-Offsets und Graphemes

Der Facet-Index des AT Protocols wird in UTF-8-Byte-Offsets angegeben. TextBuilder berechnet die Byte-Anzahl intern über strlen(). Multi-Byte-Zeichen wie japanische Zeichen und Emojis verbrauchen pro Zeichen mehrere Bytes. Die Offsets ergeben sich daher aus der Byte-Anzahl, nicht aus der Zeichenanzahl.
// 'Hello' hat in UTF-8 5 Bytes (1 Zeichen = 1 Byte)
// '日本語' hat in UTF-8 9 Bytes (1 Zeichen = 3 Bytes)
$builder = TextBuilder::make('日本語');
// strlen($builder->text) === 9

Zeichenlimit für Posts

Bluesky-Posts sind auf maximal 300 Zeichen in Graphemes (angezeigte Zeichenanzahl) begrenzt. Da nicht in Bytes, sondern in Graphemes gerechnet wird, können Sie auch in Japanisch bis zu 300 Zeichen schreiben.
use Illuminate\Support\Str;

$text = 'Dies ist der Post-Text.';

// Zeichenanzahl in Graphemes ermitteln
$length = Str::length($text); // äquivalent zu mb_strlen()
TextBuilder selbst prüft die Zeichenanzahl nicht. Wenn Sie einen Post mit mehr als 300 Graphemes senden, gibt die AT-Protocol-API einen Fehler zurück.

Methodenübersicht

MethodeBeschreibung
TextBuilder::make(string $text = '')Instanz erzeugen
text(string $text)Text am Ende hinzufügen
newLine(int $count = 1)Zeilenumbruch hinzufügen
mention(string $text, ?string $did = null)Mention-Facette hinzufügen
link(string $text, ?string $uri = null)Link-Facette hinzufügen
tag(string $text, ?string $tag = null)Hashtag-Facette hinzufügen
detectFacets()Facets automatisch aus dem Text erkennen
facet(array $facet)Eigene Facette hinzufügen
resetFacets()Alle Facets zurücksetzen
toPost()In einen Post-Record umwandeln
toArray()In das Array ['text' => ..., 'facets' => ...] umwandeln
Zuletzt geändert am 13. Juli 2026