I have a project where I use email and pdf templates and what I did was to have the rendering all take place within components.
Firstly, my folder structure contains (and I will only put here what is relevant) a cache, components and views directory. Let's look at the email setup rather than the PDF as this is more relevant to your situation.
/app
/cache
/email
/components
/views
/email
/elements
Of course there is public, controllers etc but let's not think about them for this.
I'm using Swift mailer for mine but I hope you will be able to use this all the same. In /app/components/Swift.php I have a __construct that calls for this->init_template_engine();
/**
* Create a volt templating engine for generating html
*/
private function init_template_engine() {
$this->_template = new \Phalcon\Mvc\View\Simple();
$di = new \Phalcon\DI\FactoryDefault();
$this->_template->setDI($di);
$this->_template->registerEngines([
'.volt' => function($view, $di) {
$volt = new \Phalcon\Mvc\View\Engine\Volt($view, $di);
$volt->setOptions([
'compiledPath' => APP_DIR."cache".DS."email".DS, // render cache in /app/cache/email/
'compiledSeparator' => '_'
]);
return $volt;
// or use ".phtml" => 'Phalcon\Mvc\View\Engine\Php' if you want,
// both will accept PHP code if ya don't fancy it being a 100% volt.
},
]);
// tell it where your templates are
$this->_template->setViewsDir(APP_DIR.'views'.DS.'email'.DS);
return $this->_template;
}
The constants above (like APP_DIR) are something I have already made in my bootstrap and all they do is store full paths to directories.
Once the $_template variable has a template engine set up I can then use it to render my templates.
/**
* Returns HTML via Phalcon's volt engine.
* @param string $template_name
* @param array $data
*/
private function render_template($template_name = null, $data = null) {
// Check we have some data.
if (empty($data)) {
return false; // or set some default data maybe?
}
// Use the template name given to render the file in views/email
if(is_object($this->_template) && !empty($template_name)) {
return $this->_template->render($template_name, ['data' => $data]);
}
return false;
}
A sample volt email template may look like this:
{{ partial('elements/email_head') }}
<h2>Your Order has been dispatched</h2>
<p>Dear {{ data.name }}</p>
<p>Your order with ACME has now been dispatched and should be with you within a few days.</p>
<p>Do not hesitate to contact us should you have any questions when your waste of money arrives.</p>
<p>Thank you for choosing ACME Inc.</p>
{{ partial('elements/email_foot') }}
All I have to do then is grab the html and use swiftmailer's setBody method and I'm done:
->setBody($this->render_template($template, $data), 'text/html');
You don't need to place separate view engines like this in components, it could become memory hungry like that, but it does show the whole process. Hope that makes sense :)