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

Optional macro parameters

I'm writing macros to simplify form generation, but I'm having trouble getting the optional macro parameters to work. I have this code that defines a macro and calls it without the last optional parameter:

{%- macro textfld(field, title, class="") %}
    {{ startinput(field, 'textfld') }}
        <label>{{ t(title) }}</label>
        {{ text_field(field, 'class': class) }}
    {{ endinput() }}
{%- endmacro %}

{{ textfld('firstName', '_your_first_name') }}

But I'm getting this error:

Phalcon\Mvc\View\Exception: Macro textfld was called without parameter: class 

In fact, a copy-pasted documentation example has the same issue:

{%- macro my_input(name, class="input-text") %}
    {% return text_field(name, 'class': class) %}
{%- endmacro %}

{# Call the macro #}
{{ '<p>' ~ my_input('name') ~ '</p>' }}

When looking at the compiled template code - it looks like there should be another else-if to assign the default value.

if (isset($__p[2])) { $class = $__p[2]; } else { if (isset($__p['class'])) { $class = $__p['class']; } else {  throw new \Phalcon\Mvc\View\Exception("Macro textfld was called without parameter: class");  } }


98.9k

It seems the optional parameter list is remaining to be implemented

Thanks for checking this out, I hope to see this added :) Meanwhile, I suppose I can try to add this to the compiled templates myself with some crazy regexing.

While we're on topic of the macros, what would you suggest to make the macros "globally" available? I have them in a separate file, however even if I include them in a view template via partial() it does not work, because the compiler complains about undefined function even before including the partial. The only way I could make this work is to include the macro template in a file at least 2 include layers above, for example:

layout.tpl -> includes macro.tpl
register.tpl -> extends layout.tpl
form.tpl -> included via partial in register.tpl, calls the macro function

If I want to use the macro in register.tpl file, it fails, but if it's included in the form.tpl, it works. Ideally, after including it in layout.tpl, the macros would work in the extended templates directly.



18.4k
Accepted
answer
edited Oct '14

Macro loading issue was resolved by extending Volt and loading the macro file directly from the extended class on initialization.

For example:

public function getCompiler()
{
    if (empty($this->_compiler))
    {
        parent::getCompiler()
        $this->partial("macro/form.tpl");
    }

    return parent::getCompiler();
}


77

Macro loading issue was resolved by extending Volt and loading the macro file directly from the extended class on initialization.

For example:

public function getCompiler()
{
  if (empty($this->_compiler))
  {
      parent::getCompiler()
      $this->partial("macro/form.tpl");
  }

  return parent::getCompiler();
}

I've been struggling to get this to work and this solution somewhat made it. However, when the Volt engine caches the partial, if I try to use the macro in a different action - and as a consequence, differente template - I get an exception in the form of "Undefined function [macro name]...". Cleaning the cache only solves the problem for the first loaded template, then I get the same behaviour in the ones loaded next. I've seen elsewhere people using the "compileAlways" directive set to <true> but, again, it's not a definitive solution.

Has anyone been able to work around this? Or am I the only one bumping into this?



526
edited Aug '15

Thanks for your insight!

Could you show an example of how you would extend the Volt engine and what the related code in the services.php should be?

When I create a VoltM class that extends \Phalcon\Mvc\View\Engine\Volt with your code, and use the following in services.php, I get a 'maximum recursion depth exceeded' error.

$di->setShared('view', function () use ($config) {

    $view = new View();

    $view->setViewsDir($config->application->viewsDir);

    $view->registerEngines(array(
        '.volt' => function ($view, $di) use ($config) {

            //$volt = new VoltEngine($view, $di);
            $volt = new VoltM($view, $di);

            $volt->setOptions(array(
                'compiledPath' => $config->application->cacheDir,
                'compiledSeparator' => '_',
                'compileAlways' => true
            ));

            $compiler = $volt->getCompiler();
            ...

            return $volt;
        },
        '.phtml' => 'Phalcon\Mvc\View\Engine\Php'
    ));

    return $view;
});

When I remove the $this->partial part, everything works fine, so it seems the partial function is again calling the compiler..?

Macro loading issue was resolved by extending Volt and loading the macro file directly from the extended class on initialization.

For example:

public function getCompiler()
{
  if (empty($this->_compiler))
  {
      parent::getCompiler()
      $this->partial("macro/form.tpl");
  }

  return parent::getCompiler();
}