How to Serve Trix Editor Files Publicly in Laravel Nova

Have you ever struggled with making images uploaded through the Trix editor in Laravel Nova publicly viewable? By default, your S3 bucket is private to ensure data security. However, if you need to publicly render these images, you can use Laravels temporary URLs. In this article, we will walk you through the steps to achieve this, ensuring your images are accessible while maintaining security.

Step 1: Configure S3
First, ensure your S3 configuration is set up correctly. Set up your S3 bucket and grab your credentials, then use those details for the following environment variables in your .env file:
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=
Step 2: Configure Trix Editor
Next, configure the Trix field to start accepting files. Add the following code snippet to your Nova Resource inside the fields method:
Trix::make('Content')
    ->withFiles('s3', 'blog-images')
    ->sortable()
    ->rules('required'),
The withFiles method specifies the S3 disk and the directory (blog-images in this case) where the files will be stored.

Step 3: Generate Temporary URLs
Once you upload an image through the Trix editor, it wont display immediately due to the bucket being private. We need to use temporary URLs to render the images. You can achieve this by creating a getter on the model to replace the URLs in the content with temporary URLs.

Assuming the column is called content, create a getContentAttribute($value) method to handle this logic:
public function getContentAttribute($value): ?string
{
    if (!$value) {
        return null;
    }

    $dom = new \DOMDocument();
    $dom->loadHTML($value, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
    $hrefs = $dom->getElementsByTagName('a');
    $images = $dom->getElementsByTagName('img');

    foreach ($hrefs as $href) {
        $value = $href->getAttribute('href');

        if (Str::startsWith($value, 'https://your-s3-bucket-url')) {
            $formattedValue = Str::after($value, 'https://your-s3-bucket-url');
            $href->setAttribute('href', Storage::disk('s3')->temporaryUrl($formattedValue, now()->addMinutes(5)));
        }
    }

    foreach ($images as $img) {
        $src = Str::after($img->getAttribute('src'), 'https://your-s3-bucket-url');
        $img->setAttribute('src', Storage::disk('s3')->temporaryUrl($src, now()->addMinutes(5)));
    }

    return $dom->saveHTML();
}

Step 4: Understanding the Code
Lets break down the method step by step:

 1. Early Return: If $value doesnt exist, return null.
if (!$value) {
    return null;
}
 2. Parsing HTML: Use PHPs DOMDocument class to parse the HTML from the Trix editor. Extract all <a> and <img> elements.
$dom = new \DOMDocument();
$dom->loadHTML($value);
$hrefs = $dom->getElementsByTagName('a');
$images = $dom->getElementsByTagName('img');
 3. Processing <a> Elements: Loop through all <a> elements, find those linking to your S3 bucket, and replace their href attribute with a temporary URL.
foreach ($hrefs as $href) {
    $value = $href->getAttribute('href');

    if (Str::startsWith($value, 'https://your-s3-bucket-url')) {
        $formattedValue = Str::after($value, 'https://your-s3-bucket-url');
        $href->setAttribute('href', Storage::disk('s3')->temporaryUrl($formattedValue, now()->addMinutes(5)));
    }
}
4. Processing <img> Elements: Loop through all <img> elements and replace their src attribute with a temporary URL.
foreach ($images as $img) {
    $src = Str::after($img->getAttribute('src'), 'https://your-s3-bucket-url');
    $img->setAttribute('src', Storage::disk('s3')->temporaryUrl($src, now()->addMinutes(5)));
}
 5. Return Processed HTML: Return the modified HTML.
return $dom->saveHTML();
Because we used a getter, now anytime you get the content it will automatically format the urls to temporary urls allowing the images to be displayed. Hopefully this helps you out.