We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

Rendering a volt view from a string

I'm building a system where the admin can edit email templates stored in the database. I would like to use volt as the templating engine for the emails but I can't figure out how to use view engine or the volt compiler to render a template stored in a string instead of a file. Is this possible? Basically I would like to do something like:

$this->view->purchase = PurchaseModel::findFirst($id);
$body = $this->view->renderString(
    "Thanks for your purchase, you can find the details here: {{ static_url(purchase.getUrl()) }}"
)
edited Oct '14

Maybe give this a try?

<?php

$this->view->purchase = PurchaseModel::findFirst($id);
$body =  "Thanks for your purchase, you can find the details here: " . $this->purchase->getUrl(); // or however your app gets the purchase url

supernovagurl: The point was that the template strings would be user editable and stored in the database. My example was a bit misleading, the actual code would be something like this:

$template = TemplateModel::findFirst($template_id);
$this->view->purchase = PurchaseModel::findFirst($id);
$body = $this->view->renderString($template->body);

// $template->body = "Thanks for your purchase, you can find the details here: {{ static_url(purchase.getUrl()) }}"

Oleg: I tried that, but it got me only the half way through compiling the volt markup to PHP resulting to this:

Thanks for your purchase, you can find the details here: <?php echo $this->url->getStatic($purchase->getUrl()); ?>

I still need something to render this to plain text with the proper context.

Hi, Maybe you should:

  • use beforeUpdate model to database to verify if user didn't paste unprivileged code, then afterUpdate to database, save the same template to disk, and use this template from disk
  • submit NFR to github, describe how you see it and how it could help other users (i think that renderString() is good idea) and wait for next release to use it if phalcon add this functionality, but remember that priority is phalcon 2.0 based on zephir. As i know new things will be implemented to 2.x versions, 2.0 will be the same as 1.3 functionality

I think that for now my idea is good solution becasue of security, and that you benefit from system caching your template from disk



8.1k
edited Oct '14

May this way help for you: file test.volt

<div>
    {% set key=true %}
    {% if key %}
        {{'inblock message'}}
    {% endif %}
</div>

Code

$compiler = new \Phalcon\Mvc\View\Engine\Volt\Compiler();

$compiler->compile('test.volt');
$plaintext = eval('; ?>'.file_get_contents($compiler->getCompiledTemplatePath()));
echo $plaintext;

Output

<div>
                inblock message    </div>

Or

$compiler = new \Phalcon\Mvc\View\Engine\Volt\Compiler();

$code = $compiler->compileString("Thanks for your purchase, 
you can find the details here: {% set key=true %}
    {% if key %}
        {{'test message'}}
    {% endif %}");

echo eval('; ?>'. $code);

Output

Thanks for your purchase, 
you can find the details here:             test message
// 1. Get template from database
$this->view->purchase = PurchaseModel::findFirst($id);

// 2. Create template as file
$fp = fopen('templates/purchase.volt', 'w');
fwrite($fp, $this->view->purchase->template);
fclose($fp);

// 3. Include template from file
{% include "templates/purchase.volt" %}


1.6k

You know that the standard volt engine can render a string?

$parsed  = (new Volt($this->view, $this->getDI()))->getCompiler()->compileString($string);
edited Jul '16

Is there a solution for this issue? I need the same thing done like @mika and stuck also halfway.

$content = Page::findFirst(array(
    "conditions" => array("slug" => "subscription_mail")
));

$compiler = new Volt\Compiler();
$string = $compiler->compileString($content->content_parsed); // Returns the string ready for PHP ... 
edited Jul '16

I actually ended up implementing this without Volt and just using str_replace(). I figured that it's easier (and safer) for the user to use simple tags like %PRODUCT% and %PRICE% instead of learning the volt syntax.

edited Feb '18

an example of a solution to this problem is:

    public function getTemplateBody(string $content, array $viewData): string
    {
        if ($content === '') {
            return $content;
        }

        ob_start();
        \extract($viewData, EXTR_OVERWRITE);
        eval(' ?>'.$this->volt->getCompiler()->compileString($content));

        return ob_get_clean();
    }

Where $content is your Volt template string from db and $viewData is an array of params you would normally pass to $view->setVars()