Good day to all.
As the question title says, I do have HTML template code stored in s3 with Laravel tags on it, lets say:
<html>
<head>
<title>{{ $landing->title }}</title>
</head>
<body>
@foreach($landing->products as $product)
<p>{{ $product->title }}</p>
@endforeach
</body>
</html>
Then I would like to render this as post-processed html with the Laravel tags replaced as if it were a normal blade template.
In my controller I do have this:
print_r( view('render', compact('template', 'landing'))->render() );
(I don't want to show in it the browser, just get the html code)
And in the render.blade.php
I have:
{!! html_entity_decode($template->html()) !!}
But this will show me the code with the Laravel tags without proper replacement.
Any lights on this? Any help will be appreciated.
I see two possible solutions to this problem.
You can make a helper function that will compile blade for you, given a string. (source)
helpers.php (Or wherever you want to locate the function)
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;
}
Then in your controller, you would pre-process the s3 blade for consumption in your render.blade.php file like:
return view('render', [
'template' => compile_blade($template, $landing),
'landing' => $landing,
));
I don't think this is the optimal solution since you end up creating files anyways.
Firstly you need to create a folder in your project like ./storage/local/blade
. Then you need to add a namespace for views in that folder like so:
AppServiceProvider.php
public function boot()
{
...
view()->addNamespace('s3', storage_path('/local/views');
...
}
Now to handle retrieving the markup from s3 (in your controller, or elsewhere) you would do something like:
// 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);
Now, if your render.blade.php is being used solely to render the markup on s3, you should just use the new namespaced view instead. You can use this in your controller like:
return view('s3::markup', compact('landing'));
It becomes a bit more tricky if you want to use the s3 markup in one of your other blade files. But can be done by extending blade as in this post.
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 = '$1<?php ';
$php .= 'if($__env->exists(\''.$namespace.'.\'.$2)){';
$php .= 'echo $__env->make(\''.$namespace.'.\'.$2)->render();';
$php .= '}';
$php .= 'else {';
$php .= 'echo $__env->make($2)->render();';
$php .= '}';
$php .= '?>';
return preg_replace($pattern, $php, $view);
});
Now you would be able to @include a namespaced view in your blade files like:
@includeNamespaced('s3/markup')
The other reason I prefer solution 2, is that you can get some "caching" effect, if you look to see if the file already exists in local/views, before downloading from s3. Then you can create a scheduled job that deletes files in storage/local/views older than some time limit.