Is there any way to compile a blade template from a string?
Asked Answered
L

8

37

How can I compile a blade template from a string rather than a view file, like the code below:

<?php
$string = '<h2>{{ $name }}</h2>';
echo Blade::compile($string, array('name' => 'John Doe')); 
?>

http://paste.laravel.com/ujL

Levine answered 3/6, 2013 at 6:51 Comment(3)
You've written some code that looks good - what's wrong with it?Variable
I have tried $string = '<h2>{{ $name }}</h2>'; echo Blade::compileString($string, array('name' => 'John Doe')); nothing returned.Levine
This is almost a duplicate of #21557481 Just pointing the link where you would find more solutionsHallam
L
23

I found the solution by extending BladeCompiler.

<?php namespace Laravel\Enhanced;

use Illuminate\View\Compilers\BladeCompiler as LaravelBladeCompiler;

class BladeCompiler extends LaravelBladeCompiler {

    /**
     * Compile blade template with passing arguments.
     *
     * @param string $value HTML-code including blade
     * @param array $args Array of values used in blade
     * @return string
     */
    public function compileWiths($value, array $args = array())
    {
        $generated = parent::compileString($value);

        ob_start() and extract($args, EXTR_SKIP);

        // We'll include the view contents for parsing within a catcher
        // so we can avoid any WSOD errors. If an exception occurs we
        // will throw it out to the exception handler.
        try
        {
            eval('?>'.$generated);
        }

        // If we caught an exception, we'll silently flush the output
        // buffer so that no partially rendered views get thrown out
        // to the client and confuse the user with junk.
        catch (\Exception $e)
        {
            ob_get_clean(); throw $e;
        }

        $content = ob_get_clean();

        return $content;
    }

}
Levine answered 7/6, 2013 at 10:48 Comment(2)
Thanks it helped a lot. Just to help someone like me I can add to your post that a way to instancing a custom compiler extension is to pass filesystem instance ad path to compiled views to the constructor: $myBlade = new MyBlade(App::make('files'), App::make('path').'/storage/views');Rosales
How did you get laravel to use this blade compiler?Uncounted
S
19

Small modification to the above script. You can use this function inside any class without extending the BladeCompiler class.

public function bladeCompile($value, array $args = array())
{
    $generated = \Blade::compileString($value);

    ob_start() and extract($args, EXTR_SKIP);

    // We'll include the view contents for parsing within a catcher
    // so we can avoid any WSOD errors. If an exception occurs we
    // will throw it out to the exception handler.
    try
    {
        eval('?>'.$generated);
    }

    // If we caught an exception, we'll silently flush the output
    // buffer so that no partially rendered views get thrown out
    // to the client and confuse the user with junk.
    catch (\Exception $e)
    {
        ob_get_clean(); throw $e;
    }

    $content = ob_get_clean();

    return $content;
}
Schiro answered 23/11, 2015 at 13:25 Comment(2)
This solution works quite good. However, I have noticed an issue with foreach loops. <ul> @foreach($items as $item) <li>{{ $item }}</li> @endforeach </ul> This code does not render and I get this error: Undefined variable: __env Is there a solution?Chophouse
$mail_template = MailTemplate::where('name',$name)->first(); $this->bladeCompile($mail_template->body,array('price' => 300));Tropical
S
19

For anyone still interested in this, they've added it to Laravel 9

use Illuminate\Support\Facades\Blade;
 
return Blade::render('Hello, {{ $name }}', ['name' => 'Julian Bashir']);

https://laravel.com/docs/9.x/blade#rendering-inline-blade-templates

Skylab answered 21/2, 2022 at 5:20 Comment(1)
If you are using Laravel 8.x you can copy the function from 9.x as is and it will work with Laravel 8.x, Tested with php 7.4 The function render() is in [BladeCompiler.php] (github.com/laravel/framework/blob/…)Cousteau
B
6

I just stumbled upon the same requirement! For me, i had to fetch a blade template stored in DB & render it to send email notifications.

I did this in laravel 5.8 by kind-of Extending \Illuminate\View\View. So, basically i created the below class & named him StringBlade (I couldn't find a better name atm :/)

<?php

namespace App\Central\Libraries\Blade;

use Illuminate\Filesystem\Filesystem;

class StringBlade implements StringBladeContract
{
    /**
     * @var Filesystem
    */
    protected $file;

    /**
     * @var \Illuminate\View\View|\Illuminate\Contracts\View\Factory
    */
    protected $viewer;

    /**
     * StringBlade constructor.
     *
     * @param Filesystem $file
     */
    public function __construct(Filesystem $file)
    {
        $this->file = $file;
        $this->viewer = view();
    }

    /**
     * Get Blade File path.
     *
     * @param $bladeString
     * @return bool|string
     */
    protected function getBlade($bladeString)
    {
        $bladePath = $this->generateBladePath();

        $content = \Blade::compileString($bladeString);

        return $this->file->put($bladePath, $content)
            ? $bladePath
            : false;
    }

    /**
     * Get the rendered HTML.
     *
     * @param $bladeString
     * @param array $data
     * @return bool|string
     */
    public function render($bladeString, $data = [])
    {
        // Put the php version of blade String to *.php temp file & returns the temp file path
        $bladePath = $this->getBlade($bladeString);

        if (!$bladePath) {
            return false;
        }

        // Render the php temp file & return the HTML content
        $content = $this->viewer->file($bladePath, $data)->render();

        // Delete the php temp file.
        $this->file->delete($bladePath);

        return $content;
    }

    /**
     * Generate a blade file path.
     *
     * @return string
     */
    protected function generateBladePath()
    {
        $cachePath = rtrim(config('cache.stores.file.path'), '/');
        $tempFileName = sha1('string-blade' . microtime());
        $directory = "{$cachePath}/string-blades";

        if (!is_dir($directory)) {
            mkdir($directory, 0777);
        }

        return "{$directory}/{$tempFileName}.php";
    }
}

As you can already see from the above, below are the steps followed:

  1. First converted the blade string to the php equivalent using \Blade::compileString($bladeString).
  2. Now we have to store it to a physical file. For this storage, the frameworks cache directory is used - storage/framework/cache/data/string-blades/
  3. Now we can ask \Illuminate\View\Factory native method 'file()' to compile & render this file.
  4. Delete the temp file immediately (In my case i didn't need to keep the php equivalent file, Probably same for you too)

And Finally i created a facade in a composer auto-loaded file for easy usage like below:

<?php

if (! function_exists('string_blade')) {

    /**
     * Get StringBlade Instance or returns the HTML after rendering the blade string with the given data.
     *
     * @param string $html
     * @param array $data
     * @return StringBladeContract|bool|string
     */
    function string_blade(string $html, $data = [])
    {
        return !empty($html)
            ? app(StringBladeContract::class)->render($html, $data)
            : app(StringBladeContract::class);
    }
}

Now i can call it from anywhere like below:

<?php

$html = string_blade('<span>My Name is {{ $name }}</span>', ['name' => 'Nikhil']);

// Outputs HTML
// <span>My Name is Nikhil</span>

Hope this helps someone or at-least maybe inspires someone to re-write in a better way.

Cheers!

Buoyancy answered 28/2, 2019 at 17:23 Comment(1)
Dope dude thanks! StringBlade is sick! its like String Cheese...... kinda.. haha edit: orrrrrr Sling Blade!?Accumulative
K
5

I'm not using blade this way but I thought that the compile method accepts only a view as argument.

Maybe you're looking for:

Blade::compileString()
Keto answered 3/6, 2013 at 12:9 Comment(1)
$string = '<h2>{{ $name }}</h2>'; echo Blade::compileString($string, array('name' => 'John Doe')); "name" is not assign to the template.Levine
H
4

It's a old question. But I found a package which makes the job easier.

Laravel Blade String Compiler renders the blade templates from the string value. Check the documentation on how to install the package.

Here is an example:

$template = '<h1>{{ $name }}</h1>';  // string blade template

return view (['template' => $template], ['name' => 'John Doe']);

Note: The package is now updated to support till Laravel 6.

Heine answered 19/9, 2016 at 20:38 Comment(3)
The answers from above will solve the issue without any package.Rehearing
Doesn't it throw undefined $name exception? before it reaches view function just wonderingCatlin
@sumeet I do not think as $name is not a variable, it is just part of stringInositol
I
4

I know its pretty old thread, but today also requirement is same.

Following is the way I solved this on my Laravel 5.7 (but this will work with any laravel version greater than version 5), I used the knowledge gained from this thread and few other threads to get this working (will leave links to all threads at the end, if this help up-vote those too)

I added this to my helper.php (I used this technique to add helper to my project, but you can use this function directly as well)

if (! function_exists('inline_view')) {
    /**
     * Get the evaluated view contents for the given blade string.
     *
     * @param  string  $view
     * @param  array   $data
     * @param  array   $mergeData
     * @return \Illuminate\View\View|\Illuminate\Contracts\View\Factory
     */
    function inline_view($view = null, $data = [], $mergeData = [])
    {
        /* Create a file with name as hash of the passed string */
        $filename = hash('sha1', $view);

        /* Putting it in storage/framework/views so that these files get cleared on `php artisan view:clear*/
        $file_location = storage_path('framework/views/');
        $filepath = storage_path('framework/views/'.$filename.'.blade.php');

        /* Create file only if it doesn't exist */
        if (!file_exists($filepath)) {
            file_put_contents($filepath, $view);
        }
        
        /* Add storage/framework/views as a location from where view files can be picked, used in make function below */
        view()->addLocation($file_location);

        /* call the usual view helper to render the blade file created above */
        return view($filename, $data, $mergeData);
    }
} 

Usage is exactly same as laravel's view() helper, only that now first parameter is the blade string

            $view_string = '@if(strlen($name_html)>6)
                                <strong>{{ $name_html }}</strong>
                            @else 
                                {{$name_html}} 
                            @endif';
            return inline_view($view_string)->with('name_html', $user->name);
            return inline_view($view_string, ['name_html' => $user->name]);

References:

  1. https://mcmap.net/q/426152/-render-blade-from-string-instead-of-file
  2. https://mcmap.net/q/426153/-render-view-from-path-laravel-5
Inositol answered 29/8, 2020 at 9:6 Comment(0)
H
0

Laravel 9 :

use Illuminate\Support\Facades\Blade;
 
return Blade::render('Your Blade Content {{ $parameter1}}', ['parameter1' => 'Name']);
Hards answered 5/9, 2022 at 12:46 Comment(1)
This is exactly the same as an already given answer.Teasley

© 2022 - 2024 — McMap. All rights reserved.