如何在存储在 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 标签但没有正确替换的代码。
这里有灯吗?任何帮助将不胜感激。
我看到这个问题有两种可能的解决方案。
- 自己编译 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,
));
我认为这不是最佳解决方案,因为无论如何您最终都会创建文件。
- 为来自 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 中早于某个时间限制的文件。
大家好。
正如问题标题所说,我确实有 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 标签但没有正确替换的代码。
这里有灯吗?任何帮助将不胜感激。
我看到这个问题有两种可能的解决方案。
- 自己编译 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,
));
我认为这不是最佳解决方案,因为无论如何您最终都会创建文件。
- 为来自 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 中早于某个时间限制的文件。