Jump to content

Use Latte Templates via PageClasses


cwsoft
 Share

Recommended Posts

This tutorial is about making the Latte template engine available in ProcessWire template files via the use of ProcessWires Custom Page Classes. Based on some inspiring posts from @bernhard, I wanted to use custom page classes and Latte templates in my own projects. I know there are modules like RockFrontend and others available to integrate template engines like Latte or Twig . However I wanted to start with a small, flexible and reusable code base as playground for my next projects without module dependencies as starting point.

Please note: The template part of this tutorial assumes you are using the Site profile Default profile (standard/intermediate edition), which can be downloaded from the ProcessWire Github repository. Here a technique called delayed output strategy is used. I also posted an alternative Latte implementation some posts below, using a normal class file for implementing Latte via your template _init.php file.

Step 1: Enable ProcessWire Custom Page Classes
PageClasses were added with ProcessWire 3.0.152. To use page classes open site/config.php and enable the feature if needed via $config->usePageClasses = true;. After that create the folder site/classes in your site installation and add the PHP file DefaultPage.php inside with the following content for now.

<?php namespace ProcessWire;
class DefaultPage extends Page {}

The DefaultPage class is used whenever a ProcessWire template like site/templates/your-template.php has no page class associated with it. In other words, if no site/classes/YourTemplatePage.php exists, the default page class site/classes/DefaultPage.php is used as fallback.

Step 2: Install PHP Dependency Manager Composer
As we are using the Latte Composer package, we first need to install Composer on our computer if not yet available. Visit the Composer webiste and install Composer on your system. On Windows just grab the Composer-Setup.exe file and execute it. Once Composer is installed, open a new terminal and type the command composer. You should see some Composer output in your console. If so, proceed with Step 3. If an error occurs, please read and follow the Docs on the Composer website to get Composer installed on your system before proceeding with this tutorial.

Step 3: Install Latte Composer Package
In the next step, we install the Latte template engine files as Composer package in our site folder. Open a terminal and switch into your local site/classes folder created in Step 1. From inside the site/classes folder type the following command into your terminal composer require latte/latte.

Please note: If your installation has a composer.json file in the root, you will get asked if you want to use the composer.json in the root. As this tutorial assumes to install the composer files inside our site/classes folder, you MUST answer no. Otherwise the Latte files will be installed inside /root/vendor not /root/site/classes/vendor as expected in this tutorial.

Once done, your site classes folder should contain a vendor folder with the Latte composer files, the two Composer files composer.json and composer.lock and our DefaultPage.php file created in Step 1.

site/
	classes/
		vendor/
			bin/
			composer/
			latte/
			autoload.php
		composer.json
		composer.lock
		DefaultPage.php (implements Latte logic inherited by other PageClasses)
		EventsPage.php (implements logic for /templates/events.php)
	
	// Note this part is setup and explained later in this tutorial
	templates/
		views/
			events.latte
		your-template.php (associated with fallback classes/DefaultPage.php)
		events.php (associated with classes/EventsPage.php)

Please note: The files located under site/templates are explained later in this tutorial. I just thought it would be a good idea to show the relevant file structure at one spot.

Step 4: Make Latte available from PageClasses
Now we need to make Latte available in our custom PageClasses. Open site/classes/DefaultPage.php and change the code as follows:

<?php

namespace ProcessWire;

/**
 * The DefaultPage class implements methods to render Latte templates as string. This page class serves as
 * fallback for PW templates inside site/templates not implementing their own page class inside site/classes.
 */
class DefaultPage extends Page {
    protected static \Latte\Engine|null $latte = null;
    private array $latteConfig = [];

    // Constructor.
    public function __construct(Template $tpl = null) {
        parent::__construct($tpl);
        $this->setupLatteEngine();
        $this->setLatteConfig();
    }

    // Set or update Latte configuration.
    public function setLatteConfig(array $config = []): void {
        if (!isset(self::$latte)) return;

        // Remove keys from user config not available in Latte defaults, except for 'pwAPI'.
        $config = array_intersect_key($config, $this->getLatteDefaultConfig());
        // Merge user config with Latte defaults.
        $this->latteConfig = array_merge($this->getLatteDefaultConfig(), $config);

        // Apply updated configuration to actual Latte instance.
        self::$latte->setAutoRefresh($this->latteConfig['autoRefresh']);
        self::$latte->setTempDirectory($this->latteConfig['cacheFolder']);
    }

    // Render given Latte template as string, injecting ProcessWire API and $data.
    public function renderTemplate(string $template, array $data = []): string {
        if (!isset(self::$latte) || !$this->latteConfig) return '';

        // Ensure Latte template exists within specified template folder.
        $latteTemplateFolder = realpath($this->latteConfig['latteTemplateFolder']);
        $templatePath = realpath($latteTemplateFolder . '/' . $template);
        if ($templatePath === false || stripos($templatePath, $latteTemplateFolder) !== 0) {
            throw new \Exception("Latte template '$template' not found or outside 'latteTemplateFolder'.");
        }

        // Render Latte template and inject ProcessWire API and $data.
        return self::$latte->renderToString($templatePath, array_merge($data, $this->latteConfig['pwAPI']));
    }

    // Returns associative array with Latte default configuration.
    protected function getLatteDefaultConfig(): array {
        return [
            'autoRefresh' => true,
            'cacheFolder' => $this->wire('config')->paths->cache . 'Latte/',
            'latteTemplateFolder' => $this->wire('config')->paths->templates,
            'pwAPI' => [
                'page' => $this,
                'pages' => $this->wire('pages'),
                'config' => $this->wire('config'),
            ],
        ];
    }

    // Initiate Latte template engine.
    private function setupLatteEngine(): void {
        if (isset(self::$latte)) return;

        require_once "vendor/autoload.php";
        self::$latte = new \Latte\Engine;
    }
}

The DefaultPage class assumes the Latte template files to be stored inside path site/templates/. By default we can access the PW-API $page, $pages and $config inside our Latte templates. You can add more ProcessWire APIs like $sanitizer or template specific data via the setLatteConfig() method or via the optional associative array $data of the renderTemplate() method.

Assume we have another PageClass in site/classes/EventsPage.php, which implements the complex method hello(). To make the Latte render methods available in the EventsPage class, we need to extend from our DefaultPage class instead of the ProcessWire Page class. This way we can create as many page classes with logic for specific templates as needed, while keeping code structured and organized.

<?php namespace ProcessWire;

class EventsPage extends DefaultPage {
    public function hello() {
        return "Hello World!";
    }
}

Step 5: Add Latte template (acting as View)
Next we add a Latte template file to site/templates/views/events.latte with the following content.

<div>
    <h2>{$page->title|capitalize}</h2>
    <p>PageId: {$page->id}</p>
    
    <p n:if="$config->useFunctionsAPI">
        Using Functions API and Latte is fun.
    </p>
  
	{* This will be translated based on the default PW language or users language preference *}
	<p>{$lang['Please select a date']}</p>

    <ul n:if="$page">
        <li n:foreach="$page->getFields() as $field">
            $page->{$field}: {$page->$field}
        </li>
    </ul>
</div>

Step 6: Render Latte template from ProcessWire templates (acting as Controller)
The code below shows how custom methods and the Latte render method can be invoked within normal ProcessWire templates like site/templates/events.php. The first code line appends the output from the method hello() implemented in the associated PageClass site/classes/EventsPage.php to our template $output variable.

Please note: In this example the ProcessWire template file uses a technique called Delayed Output Strategy. You can read more about this strategy in the linked ProcessWire blog post from Ryan.

<?php namespace ProcessWire;

/**
 * Template: "events.php"
 * Using Processwire delayed output strategy
 * https://processwire.com/docs/tutorials/how-to-structure-your-template-files/
 */

// Append output from method hello() implemented in `site/classes/EventPage.php`.
$content .= $page->hello();

// Add needed translations here. You can access them via $lang inside your Latte template.
// The translation for 'Please select a date' can be added via the ProcessWire backend.
// For details see https://processwire.com/docs/multi-language-support/code-i18n/.
$data = ['lang' => [
	'Please select a date' => __('Please select a date!'),
	],
];

// Append output of rendered Latte template located in `site/templates/views/events.latte`.
$content .= $page->renderTemplate('views/events.latte', $data);

At the bottom of our template file site/templates/events.php we call the render method implemented in site/classes/DefaultPage.php to render the specified Latte template in site/templates/views/events.latte. The ProcessWire API $page, $pages, $config is available in all Latte templates by default. As the EventsPage class extends the DefaultPage class, it inherents it's render methods This way we need to implement the Latte template code once, but can use it in all our custom PageClasses and default ProcessWire template files located in /site/templates.

Note: If you are using the output strategy Markup regions, the Events template file would look as follows:

<?php

namespace ProcessWire;

/**
 * Template: "events.php"
 * Using Processwire delayed output strategy
 * https://processwire.com/docs/tutorials/how-to-structure-your-template-files/
 */

// Append output from method hello() implemented in `site/classes/EventPage.php`.
// Add needed translations here. You can access them via $lang inside your Latte template.
// The translation for 'Please select a date' can be added via the ProcessWire backend.
// For details see https://processwire.com/docs/multi-language-support/code-i18n/.
$data = [
    'lang' => [
        'Please select a date' => __('Please select a date!'),
    ],
];
?>

<div id="content">
    <?= $page->hello(); ?>
    <?= $page->renderTemplate('views/events.latte', $data); ?>
</div>

Have fun.
cwsoft

Please note: Edited the initial post to include the code suggestions from member @d'Hinnisdaël some posts below. Thanks for your suggestions.

  • Like 6
Link to comment
Share on other sites

  • cwsoft changed the title to Use Latte Templates via PageClasses

Great to see a tutorial about Latte 🙂 And always great to get some promotion of using custom page classes. 👍💪

One thing to add might be that when using RockFrontend you can use __('Some text') translations directly in your Latte files! That's not possible with your approach. Also the next version of RockFrontend will have automatic layout loading for latte, which is really cool and comes from an input of @dotnetic who had this need and sponsored that addition 😍 

And not to forget live reloading 🚀 But of course Latte is great even without RockFrontend 🙂 

  • Like 2
Link to comment
Share on other sites

Hi Bernhard,

thanks for your feedback. Fully agree, custom page classes and Latte templates are really awesome features and help to keep custom projects structured and clean. 

Regarding your suggestion about ProcessWire translate functions. So far all my custom projects are German only sites, so I had no need to implement this feature yet. However a great suggestion as usual, which may find it‘s way into a follow up writing.

Wished I knew about the potential of page classes an Latte already in my first project. However in future projects I will make use of page classes and Latte right from the beginning. And if I find some spare time, maybe I will switch to RockFrontend mid to long term. 

Regards cwsoft

  • Like 2
Link to comment
Share on other sites

Though about automatic layout loading too, but decided not to implement it yet (Python ZEN says Explizit is better than Implicit). However I may add a method to include custom CSS or Javascript code/files into head/body to the DefaultPage class. If I do, it will most likely be similar to what I implemented in my modules EmailToEncryptedMailto and NoCookieWithoutConsent, just with some more options/parameters. For sure my approach will only add basic stuff and not try to reimplement RockFrontend. As mentioned I may switch to RockFrontend anyway mid to long term.

Cheers cwsoft

Link to comment
Share on other sites

Hi,

updated my initial post with an example of how to use ProcessWire default translation methods in Latte templates. My solution of course is not as elegant as Bernhards solution in RockFrontend, where you can use translation functions inside Latte templates, but it works for my needs.

Cheers cwsoft

Link to comment
Share on other sites

Great to see people adopting Latte 🥯

One potential issue with your example is that you're creating a new Latte instance for every single Page class instance that is created. And page class instances are created whenever they're pulled from the database. Meaning, when you're looping over a set of 100 pages, you're creating 100 Page instances and 100 Latte instances with it. That's rather costly for performance.

The advantage when using existing modules like TemplateEngineFactory or RockFrontend is that they take care to only instantiate a single instance of whatever renderer they use for generating template views.

  • Like 2
Link to comment
Share on other sites

On 11/17/2023 at 9:06 PM, d'Hinnisdaël said:

One potential issue with your example is that you're creating a new Latte instance for every single Page class instance that is created. And page class instances are created whenever they're pulled from the database. Meaning, when you're looping over a set of 100 pages, you're creating 100 Page instances and 100 Latte instances with it. That's rather costly for performance.

Thanks for your feedback. I am aware of this issue. As my normal PW installations deal with 5-10 template files, I didn't run into performance or memory issues so far. However I am working on some code improvements right now, which will address your concerns.

  • Like 1
Link to comment
Share on other sites

@d'HinnisdaëlI reworked my DefaultPage class and made the latte object a protected static member of the DefaultPage class to prevent multiple Latte instances as you suggested together with some other small improvements. Have added the updated DefaultPage class code to my initial post in this thread to keep things clean.

Cheers cwsoft

  • Like 2
Link to comment
Share on other sites

Alternative solution using your template _init.php file to setup the Latte engine.
Another option setting up the Latte engine is to add the required code to your _init.php template file (if file exists depends on your output strategy) and add the Latte instance to the global wire object for convenience.

File: /site/classes/ProcessWireLatteEngine.php
Note: Based on the DefaultPage class code shown in the first post of this thread, I renamed the class to ProcessWireLatteEngine, removed the extends Page part from it's class definition and changed all occurences of $this->wire->xy by wire()->xy. The rest of the code is unchanged compared to the DefaultPage class.

<?php

namespace ProcessWire;

/**
 * Class LatteEngine initializes the template engine Latte and implements a method to render Latte templates.
 * Default PW-API ($page, $pages, $config) can be used in Latte templates without additional configuration.
 */
class ProcessWireLatteEngine {
    protected static \Latte\Engine|null $latte = null;
    private array $latteConfig = [];

    // Constructor.
    public function __construct(array $config = []) {
        $this->setupLatteEngine();
        $this->setLatteConfig();
    }

    // Set or update Latte configuration.
    public function setLatteConfig(array $config = []): void {
        if (!isset(self::$latte)) return;

        // Remove unknown keys from user config and merge user config with default config.
        $config = array_intersect_key($config, $this->getLatteDefaultConfig());
        $this->latteConfig = array_merge($this->getLatteDefaultConfig(), $config);

        // Apply updated configuration to actual Latte instance.
        self::$latte->setAutoRefresh($this->latteConfig['autoRefresh']);
        self::$latte->setTempDirectory($this->latteConfig['cacheFolder']);
    }

    // Render given Latte template as string, injecting ProcessWire API and $data.
    public function renderTemplate(string $template, array $data = []): string {
        if (!isset(self::$latte) || !$this->latteConfig) return '';

        // Ensure Latte template exists within specified template folder.
        $latteTemplateFolder = realpath($this->latteConfig['latteTemplateFolder']);
        $templatePath = realpath($latteTemplateFolder . '/' . $template);
        if ($templatePath === false || stripos($templatePath, $latteTemplateFolder) !== 0) {
            throw new \Exception("Latte template '$template' not found or outside 'latteTemplateFolder'.");
        }

        // Render Latte template and inject ProcessWire API and $data.
        return self::$latte->renderToString($templatePath, array_merge($data, $this->latteConfig['pwAPI']));
    }

    // Returns associative array with Latte default configuration.
    protected function getLatteDefaultConfig(): array {
        return [
            'autoRefresh' => true,
            'cacheFolder' => wire('config')->paths->cache . 'Latte/',
            'latteTemplateFolder' => wire('config')->paths->templates,
            'pwAPI' => [
                'page' => wire('page'),
                'pages' => wire('pages'),
                'config' => wire('config'),
            ],
        ];
    }

    // Initiate Latte template engine.
    private function setupLatteEngine(): void {
        if (isset(self::$latte)) return;

        require_once "vendor/autoload.php";
        self::$latte = new \Latte\Engine;
    }
}

File: /site/templates/_init.php (to setup Latte engine)

<?php

namespace ProcessWire;

// Optional initialization file, called before rendering any template file.
// This is defined by $config->prependTemplateFile in /site/config.php.
// Use this to define shared variables, functions, classes, includes, etc. 

// Default config applied if no user config was specified via the constructor.
// $latteConfig = [
//     'autoRefresh' => true,
//     'cacheFolder' => wire('config')->paths->cache . 'Latte/',
//     'latteTemplateFolder' => wire('config')->paths->templates,
//     'pwAPI' => [
//         'page' => wire('page'),
//         'pages' => wire('pages'),
//         'config' => wire('config'),
//     ],
// ];

// Setup Latte template engine and store instance in global wire object.
// Access Latte render method via wire('latte')->renderTemplate($template, $data).
require_once wire('config')->paths->site . 'classes/ProcessWireLatteEngine.php';
wire()->set('latte', new ProcessWireLatteEngine());

File: /site/templates/events.php (example for output strategy Markup Regions)

<?php

namespace ProcessWire;

// Template file for “events.php” template.
// ------------------------------------------------------
// The #content div in this file will replace the #content div in _main.php
// when the Markup Regions feature is enabled, as it is by default. 
// You can also append to (or prepend to) the #content div, and much more. 
// See the Markup Regions documentation:
// https://processwire.com/docs/front-end/output/markup-regions/

?>

<div id="content">
	Events page content
	<?= wire('latte')->renderTemplate('views/events.latte'); ?>
</div>

 

Link to comment
Share on other sites

Hi @cwsoft

thanks for this tutorial. I absolutely support what you write in the beginning, to have LATTE support without dependencies to any modules.

While RockFrontend is in regards to LATTE supporting and doing everything I need, and it works perfect for that, the drawback is, that it comes with too many things, I don´t need and don´t want to have in my project. 

So I tried out your proposals.

Option 1 ends up with this error: "Uncaught Error: Class "Latte\Engine" not found in site/classes/DefaultPage.php:61"
But Option 2 works without issues. 

I also have a question to you that you might be able to answer.

standard.php

<?php namespace ProcessWire;
echo $website_name; 
echo wire('latte')->renderTemplate('views/standard.latte');

$website_name is defined in my _init.php which is automatically called before standard.php. And within standard.php the value for $website_name is properly available end rendered when the template is called. In the 3rd line I now call the latte template, which looks like this:

latte.php

{$website_name} 

now I get the following warning "PHP Warning: Undefined variable $website_name in .../Latte/templates-views-standard.latte"

What do I need to do to make $website_name also available within the latte template?

thanks

Link to comment
Share on other sites

@maetmarThe first error indicates that Latte wasn‘t loaded. Please check the path you‘ve installed Latte. I assume you have the files in /site/templates/classes/vendor/… If those files are at the right spot, you may need absolute pathes in require statements. Maybe you can replace the following line (at the end of DefaultPage.php of my first example). 

require_once "vendor/autoload.php";

by this line

require_once wire('config')->site . 'classes/vendor/autoload.php';

Please also check if you have the /Latte/Engine slash at the beginning of the new statement. It may be, that my code interacts with other code in your site like other template engines or composer autoloaders etc. 

Regarding your second issue. By default I only added $page, $pages and $config to Latte templates. All other variables or objects you want to use in my Latte templates approach, needs to be manually injected like this

wire('latte')->renderTemplate('views/standard.latte', [
  'website_name' => $website_name,
]);

Now you could use {$website_name} inside your views/standard.latte file. However you could use {$page->title} to display the title of the actual page in standard.latte without requiring to add $page manually, as I added this object by default.

P.S. I tested my code on Apache webserver with PHP 8.2 local and in the web.

P.P.S.: I‘ve refactored a medium site project recently from Delayed Output with default PHP Templates to Latte Templates (slightly modified 2nd setup of my tutorial) in combination with PageClasses. This allowed me to use a simple but powerful MVC approach, which made the entire code much more structured and improved readability a lot. I added the namespace cwsoft to my LatteTemplateEngine class, so it won‘t interfere with other template classes out there in the wild and did some code cleanup. I can provide the updated code if needed. I will stick with this approach for my next projects and see how far I can go with this approach. For now I am very happy with what was possible with this simple approach. 

Link to comment
Share on other sites

12 hours ago, maetmar said:

However, with Bernards RockFrontend this works out of the box

Bernhards RockFrontend is at a complete different level than my two Latte implementations in this thread. If RockFrontend works for you, you may be better of staying with Bernhards module. It‘s by far more advanced than my code example and offers tons of other handy features like hot reload, translation functions from Latte templates and much more.

  • Like 1
Link to comment
Share on other sites

16 hours ago, cwsoft said:

P.P.S.: I‘ve refactored a medium site project recently from Delayed Output with default PHP Templates to Latte Templates (slightly modified 2nd setup of my tutorial) in combination with PageClasses. This allowed me to use a simple but powerful MVC approach, which made the entire code much more structured and improved readability a lot. I added the namespace cwsoft to my LatteTemplateEngine class, so it won‘t interfere with other template classes out there in the wild and did some code cleanup. I can provide the updated code if needed. I will stick with this approach for my next projects and see how far I can go with this approach. For now I am very happy with what was possible with this simple approach. 

thanks for your reply. I will try that out.

And yes, I would be interested in the code you mentioned. Always good to see other approaches to learn something.

Link to comment
Share on other sites

13 hours ago, cwsoft said:

Bernhards RockFrontend is at a complete different level than my two Latte implementations in this thread. If RockFrontend works for you, you may be better of staying with Bernhards module. It‘s by far more advanced than my code example and offers tons of other handy features like hot reload, translation functions from Latte templates and much more.

I used it for quite some time now. But recently I revisted an older project and wanted to upgrade all modules to the newest versions. When upgrading Rockfront from a very old to the newest version it broke my whole site and needed to restore it. The only thing I really used was LATTE, that made me think about a solution where I am not depending on another module. RockFrontend offers great things, but as said I personally dont need a module to take care of loading google fonts, managing font-sizes, etc. 

Link to comment
Share on other sites

2 hours ago, maetmar said:

RockFrontend offers great things, but as said I personally dont need a module to take care of loading google fonts, managing font-sizes, etc. 

If you don't need that, don't use it 🙂 I understand your arguments and I'm not going to try to convince you which way is better as both have pro's and con's. Just wanted to mention that idea of RockFrontend is to provide the tools you need and stay quiet in the background if you don't need them.

If that's not the case for whatever reason I'd be happy to get a detailed report of the issue so I can fix it and make RockFrontend work in the way it's intended to work. If you do that or not is up to you. Just didn't want to leave that "RockFrontend broke my whole site" uncommented, as I'm putting a lot of work into my modules and I also try to make them as backwards compatible as possible.

  • Like 1
Link to comment
Share on other sites

18 hours ago, cwsoft said:

Regarding your second issue. By default I only added $page, $pages and $config to Latte templates. All other variables or objects you want to use in my Latte templates approach, needs to be manually injected like this

wire('latte')->renderTemplate('views/standard.latte', [
  'website_name' => $website_name,
]);

You could also just pass the $wire object and from there you can access anything in your templates like $wire->pages->find(...) or $wire->foo->bar...

  • Like 1
Link to comment
Share on other sites

Yepp, good point about wire object. About 98% of my needs are covered by $page, $pages and $config. As I put my page/template related code/logic into custom page classes, it can be used via $page->method() inside my Latte templates too without extra injection. So my normal PW template files act as controller, my Latte templates as view and Page/Custom Page classes as model (at least sort of). 

  • Like 2
Link to comment
Share on other sites

Yeah, I'm using the same setup and it's absolutely great. Simple, clean, flexible, powerful. You can also access the $wire instance from every wire derived class, so you could also do $page->wire->foo->bar, eg $page->wire->pages->find(...). Many options 🙂 

  • Like 2
  • Thanks 1
Link to comment
Share on other sites

V interesting article! Thanks all. I have also implemented latte as the main templating engine in a site. I didn't use any of the big module solutions because I think my needs are simple and fewer dependencies leads to easier long term maintenance. I have implemented latte as an api var and used page classes for all the page type specific work. It's been good as I've leveraged inheritance in both page classes (I'm also using pageTableNext) and also latte (`{layout...}`), reducing duplicate code.

One observation I had was that I like to use latte to make html templates more **readable**, and do all the heavy lifting **logic** in pure php in page classes.

(I've also observed that I'm that committed to markdown that I use it instead of fiddly, non-semantic buttons I'd have to scroll back to get to! Oops)

  • Like 1
Link to comment
Share on other sites

On 11/24/2023 at 2:07 PM, bernhard said:

If you don't need that, don't use it 🙂 

that is exactly what I try to do, find a solution to use LATTE without any of the modules that offer support for LATTE. That is what this thread is about and I didn´t post this in the RockFrontend thread 🙂

From the existing modules that I tried, RockFrontend offers in my opinion the best LATTE support and is the easiest to use. But if I find a solution w/o a module needed, for me that would even be better. So I try to find that out now.

fyi, I used an older backup from the site where I got the issues, and I tried to reproduce the error, but I can not manage to reproduce it anymore. So I take back the statement that it was RockFrontend causing the issues, it might have been also something else.

  • Like 1
Link to comment
Share on other sites

@maetmarFrom an earlier post it seemed you got my 2nd code example where I include a simple Latte class via _init.php working. That‘s the approach I used for the refactoring of two medium sized sites. Worked great. One site used delayed output the other layout regions before with plain PW PHP templates. Converted the main template to Latte and also used a simple MVC approach with Latte as the view part in those two sites. Very flexible setup and easy to transfer to existing sites or start off right from the beginning for new projects too. I will go with this setup for my next projects until I feel the need for more complex features like PW translations in Latte files available in RockFrontend for example.

Link to comment
Share on other sites

@cwsoft, yes got Option 2 working. Also including my custom variables. Regarding Option 1: I tried your proposals to but didn´t get it to work. LATTE is properly installed, otherwise Option 2 would also not work I guess. But anyway. I am happy with Option 2 for my purposes. Thanks

  • Like 1
Link to comment
Share on other sites

On 11/28/2023 at 8:00 AM, maetmar said:

@cwsoft, yes got Option 2 working. Also including my custom variables. Regarding Option 1: I tried your proposals to but didn´t get it to work. LATTE is properly installed, otherwise Option 2 would also not work I guess. But anyway. I am happy with Option 2 for my purposes. Thanks

Ok. I finally followed my initial tutorial step by step based on a clean ProcessWire 3.0.229 with basic blank profile (output strategy Markup Regions) and PHP 8.2.4. Following the steps and copying the code one by one showed no warnings or errors at all. Of course i needed to adapt the events.php template for markup regions to see the output.

However I found two possible traps one can step into. First trap is related to the installation process of the Latte composer package. If a default ProcessWire composer.json exists in the root of your installation, composer asks to use this composer.json file. If you answer Yes (default), this would install the Latte files in /root/vendor instead of /root/site/classes/vendor as I assume in my tutorial. This could explain the file not found error you mentioned. In order to install the Latte composer files inside /root/site/classes, one needs to answer NO to the composer question "Should I use the composer.json in the root?". Added a note in my initial post to avoid this trap.

Second trap could be your template output strategy. My tutorial assumed you are using delayed output strategy, not markup regions or others. If you use markup regions, you won't see any Latte output in the events.php at all. So I decided to add an example for markup regions for the events.php template too.

In your specific case, the error may be related to the fact that you already installed other Latte template implementations like RockFrontend, LatteTemplateEngine module or others, which may affect each other (e.g. namespaces, autoloaders). So I guess if you would start with a blank ProcessWire installation, my initial solution should work out of the box for you as well 😀.

  • Like 2
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...