如何在存储在 s3 中的 html 内渲染 Laravel 标签

How to render Laravel tags inside html stored in s3

大家好。

正如问题标题所说,我确实有 HTML 模板代码存储在 s3 中,上面有 Laravel 标签,可以说:

<html>
<head>
    <title>{{ $landing->title }}</title>
</head>
<body>
@foreach($landing->products as $product)
    <p>{{ $product->title }}</p>
@endforeach
</body>
</html>

然后我想将其呈现为 post-processed html 并替换 Laravel 标签,就好像它是一个普通的 blade 模板一样。

在我的控制器中我有这个:

print_r( view('render', compact('template', 'landing'))->render() );

(我不想在浏览器中显示,只需获取 html 代码)

render.blade.php 我有:

{!! html_entity_decode($template->html()) !!}

但是这将向我显示带有 Laravel 标签但没有正确替换的代码。

这里有灯吗?任何帮助将不胜感激。

我看到这个问题有两种可能的解决方案。

  1. 自己编译 blade(不是最佳 IMO)

你可以创建一个辅助函数,它会为你编译 blade,给定一个字符串。 (source)

helpers.php(或者你想找到函数的任何地方)

function compile_blade($markup, $data = []) {
    $fs = new \Illuminate\Filesystem\Filesystem;
    $b = new \Illuminate\View\Compilers\BladeCompiler($fs, __DIR__);
    $src = $b->compileString($markup);

    $isPhp = false;
    if (substr( $src, 0, 5 ) === "<?php")
    {
        $isPhp = true;
        $src = substr($src, 5);
    }
    
    $tempFileName = tempnam("/tmp", "blade-compile");
    file_put_contents($tempFileName, $src);

    ob_start();

    extract($data);

    include $tempFileName;
    $out = ob_get_clean();
    if ($isPhp)
    {
        $out = '<?php'.$out;
    }
    
    return $out;
}

然后在您的控制器中,您将 pre-process s3 blade 在您的 render.blade.php 文件中使用,例如:

    return view('render', [
        'template' => compile_blade($template, $landing),
        'landing' => $landing,
    ));

我认为这不是最佳解决方案,因为无论如何您最终都会创建文件。

  1. 为来自 s3 的 blade/html 创建一个新的命名空间。

首先你需要像./storage/local/blade一样在你的项目中创建一个文件夹。然后您需要为该文件夹中的视图添加一个命名空间,如下所示:

AppServiceProvider.php

public function boot()
{
    ...

    view()->addNamespace('s3', storage_path('/local/views');
    ...
}

现在要处理从 s3 中检索标记(在您的控制器中或其他地方),您可以执行以下操作:

    // Lets say the file on s3 is markup.blade.php
    $contents = Storage::disk('s3')->get('path/to/markup.blade.php')
    Storage::disk('local')->put(storage_path('local/views/markup.blade.php'), $contents);

现在,如果您的 render.blade.php 仅用于在 s3 上呈现标记,您应该只使用新的命名空间视图。您可以在您的控制器中使用它,例如:

    return view('s3::markup', compact('landing'));

如果您想在其他 blade 文件之一中使用 s3 标记,这会变得有点棘手。但是可以像 post.

那样通过扩展 blade 来完成
Blade::extend(function($view, $compiler)
{
    $pattern = $compiler->createMatcher('includeNamespaced');

    $viewPath = realpath($compiler->getPath());
    $parts = explode(DIRECTORY_SEPARATOR, $viewPath);
    $viewsDirectoryIndex = array_search('views', $parts);
    $namespace = $parts[$viewsDirectoryIndex + 1];

    $php = '<?php ';
    $php .= 'if($__env->exists(\''.$namespace.'.\'.)){';
    $php .= 'echo $__env->make(\''.$namespace.'.\'.)->render();';
    $php .= '}';
    $php .= 'else {';
    $php .= 'echo $__env->make()->render();';
    $php .= '}';
    $php .= '?>';

    return preg_replace($pattern, $php, $view);
});

现在您可以在 blade 文件中 @include 命名空间视图,例如:

    @includeNamespaced('s3/markup')

我更喜欢解决方案 2 的另一个原因是,如果您在从 s3 下载之前查看文件是否已经存在于 local/views 中,您可以获得一些“缓存”效果。然后您可以创建一个计划作业,删除 storage/local/views 中早于某个时间限制的文件。