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

前端 未结 6 1572
滥情空心
滥情空心 2020-11-27 07:32

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

{{ $name }}\';
echo          


        
相关标签:
6条回答
  • 2020-11-27 07:33

    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;
        }
    
    }
    
    0 讨论(0)
  • 2020-11-27 07:37

    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;
    }
    
    0 讨论(0)
  • 2020-11-27 07:48

    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.

    0 讨论(0)
  • 2020-11-27 07:55

    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()
    
    0 讨论(0)
  • 2020-11-27 07:56

    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://stackoverflow.com/a/31435824/4249775
    2. https://stackoverflow.com/a/33594452/4249775
    0 讨论(0)
  • 2020-11-27 07:57

    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!

    0 讨论(0)
提交回复
热议问题