bernhard Posted July 19, 2022 Share Posted July 19, 2022 I don't know when I last found a tool for my daily work that had such a huge impact as Latte. The last one was ProcessWire I guess (which had an even greater impact of course, but still). Latte uses PHP syntax so you don't have to learn a new language! It plays extremely well with the PW API and it makes my markup files so much cleaner and easier to read and maintain I can't tell you. From time to time I stumble over situations where I'm once more impressed by latte, so this thread is intended to collect small latte snippets to show how one can use it. If you want use Latte for your ProcessWire projects you can either install it via composer or you wait some more days until I release RockFrontend - a frontend module that can render latte files and comes with several other nice frontend helpers ? ----- Breadcrumbs the Latte+PW way: <ul> <li n:foreach="$page->parents()->add($page) as $item"> <a href="{$item->url}" n:tag-if="$item->id != $page->id"> {$item->title} </a> </li> </ul> The cool thing here is that the last item (the page we are currently viewing) is NOT linked. The <a> tag is only rendered for all other items. Still the label of the link is rendered, because we are using n:tag-if and not n:if ----- Output an image, but only if it exists: <img n:if="{$page->yourimage}" src="{$page->yourimage->maxSize(400,300)->url}" alt="..."> Note that the ->maxSize() call will NOT throw an error even if you didn't upload an image! This is one of my favourite features of latte: Write the markup once and then add simple instructions directly within that tag. No more if/else/foreach chaos ? 17 2 Link to comment Share on other sites More sharing options...
Pixrael Posted July 19, 2022 Share Posted July 19, 2022 That template engine is great, in my beginnings with Processwire I was testing with this excellent module developed by @tpr it's very complete, with many additions and filters to extend it. But I finally ended up using Markup Regions when it came out around the same time. I wasn't that ready yet for that "advanced" PHP. I'll keep an eye on your module, maybe I'll try again with Latte ? 1 Link to comment Share on other sites More sharing options...
wbmnfktr Posted July 19, 2022 Share Posted July 19, 2022 I personally enjoy Twig as it's similar to Nunjucks / Liquid which is my goto choice in 11ty. Yet it somehow doesn't feel right for me in ProcessWire for whatever reason and therefore Twig never made it into any project so far. Maybe some day. 3 hours ago, bernhard said: when I last found a tool for my daily work that had such a huge impact Had this recently in some kind with TailwindCSS and AlpineJS. Both made work way faster for me. Not only in prototypes and playgrounds. 1 Link to comment Share on other sites More sharing options...
pwired Posted July 20, 2022 Share Posted July 20, 2022 Quote I don't know when I last found a tool for my daily work that had such a huge impact as Latte ... On the latte website I am reading some interesting things: - You don't have to look in the documentation. You don't have to learn another language. You just write like in PHP. - Latte is a next generation templating system – it understands HTML. Where other systems see only a bunch of characters, Latte sees HTML elements. - Latte is based on PHP, whereas Twig is based on Python. A designer in Latte doesn't have to constantly switch between two different conventions. I am using placeholders in my html using php and I must say it works really good and fast like this: <div class="header_navigation block"> <div class="header_language block"><?php include $header_language; ?></div> <div class="header_navbuttons block"><?php include $header_navbuttons; ?></div> </div> Here is my question: Could you say from your own experience if using latte shortcodes make more productive and is the overhead worth it ? Link to comment Share on other sites More sharing options...
fliwire Posted July 20, 2022 Share Posted July 20, 2022 install it via composer and than how init latte? tried breadcrumbs code not working? <ul> <li n:foreach="$page->parents()->add($page) as $item"> <a href="{$item->url}" n:tag-if="$item->id != $page->id"> {$item->title} </a> </li> </ul> Link to comment Share on other sites More sharing options...
zoeck Posted July 20, 2022 Share Posted July 20, 2022 I have also been using Latte for a few weeks. Thanks to Bernhard ? It's just so awesome to use latte. But you also learn new tricks every day. I'm looking forward to the public version of RockFrontend (by the way a brilliant module ? thanks again Bernhard!) 2 Link to comment Share on other sites More sharing options...
bernhard Posted July 20, 2022 Author Share Posted July 20, 2022 4 hours ago, pwired said: <div class="header_navigation block"> <div class="header_language block"><?php include $header_language; ?></div> <div class="header_navbuttons block"><?php include $header_navbuttons; ?></div> </div> Using RockFrontend your code would be like this: <div class="header_navigation block"> <div class="header_language block"><?= $rf->render("partials/header_language") ?></div> <div class="header_navbuttons block"><?= $rf->render("partials/header_navbuttons") ?></div> </div> Which is the short version of this: <div class="header_navigation block"> <div class="header_language block"><?= $rockfrontend->render("/site/templates/partials/header_language.latte") ?></div> <div class="header_navbuttons block"><?= $rockfrontend->render("/site/templates/partials/header_navbuttons.latte") ?></div> </div> But you can also use latte's include: https://latte.nette.org/en/tags#toc-include 4 hours ago, pwired said: Could you say from your own experience if using latte shortcodes make more productive and is the overhead worth it ? What more could I say than I love it? I'll share more examples here in the thread when they pop up in my work. And I'll release RockFrontend very soon (just need to do the showcase video...) 3 hours ago, fliwire said: install it via composer and than how init latte? tried breadcrumbs code not working? Just wait for RockFrontend ? Or see here: https://latte.nette.org/en/develop 1 Link to comment Share on other sites More sharing options...
bernhard Posted July 20, 2022 Author Share Posted July 20, 2022 Another nice one from today: Imagine you are on a blog overview page and you want to provide a link for your clients to directly add a new blog post (if they are allowed to): <a n:if="$page->addable()" href="{$pages->get(2)->url}page/add/?parent_id={$page}" > Add new blog-item </a> 2 Link to comment Share on other sites More sharing options...
pwired Posted July 21, 2022 Share Posted July 21, 2022 To make all the html layout better readable will make the workflow more productive I assume latte will allow me to rewrite php includes like this: <div class="header_navigation block"> <div class="header_language block">{language}</div> <div class="header_navbuttons block">{navbuttons}</div> </div> Link to comment Share on other sites More sharing options...
bernhard Posted July 21, 2022 Author Share Posted July 21, 2022 18 hours ago, bernhard said: But you can also use latte's include: https://latte.nette.org/en/tags#toc-include I've updated my post yesterday, seems you didn't get that detail ? Link to comment Share on other sites More sharing options...
teppo Posted July 21, 2022 Share Posted July 21, 2022 Cross-posting in case any Wireframe user wants to give Latte a try: there's a renderer for it now. Somewhat experimental, since I have a rather miniscule understanding of Latte myself ? Slightly annoying marketing shenanigans aside (I didn't know how to do a single thing in Latte without reading the docs, "Latte is the only system with an effective defense, thanks to context-sensitive escaping" is a silly thing to claim, etc.) Latte does look quite nice. I dig the n:attributes in particular, they remind me a lot of the AngularJS (v1) syntax. Apart from that, Latte seems largely the same as other engines/languages I've used (e.g. Twig, Blade, and Dust). Admittedly I've just scratched the surface, so there's likely a lot more there ? I'm not sure yet whether it's a good thing or not that Latte syntax is so close to plain PHP, just with auto-escaping enabled and <?php ?> replaced with curly brackets. Among other things a) the PHP syntax isn't necessarily the easiest to grasp or nicest to look at, especially for non-developers (though this is admittedly highly opinionated), and b) there's a slight fear in the back of my head that this actually makes it extra tempting for developers to put unnecessarily complex code within their views. So far among the templating languages I've used Blade has been my personal favourite. It's easy to grasp, familiar to PHP users but also for everyone who's ever used another templating language (like Twig), has some very handy shortcuts available (particularly when it comes to loops), the syntax looks nice and clean (in my opinion), and components in particular are just brilliant in terms of both implementation and use. But enough advertising ? 5 Link to comment Share on other sites More sharing options...
bernhard Posted July 21, 2022 Author Share Posted July 21, 2022 Need a language switcher? <div class="..."> <a n:foreach="$languages as $lang" href="{$page->localUrl($lang)}" class="..." > {$lang->title} </a> </div> 2 Link to comment Share on other sites More sharing options...
pwired Posted July 21, 2022 Share Posted July 21, 2022 <div class="..."> <a n:foreach="$languages as $lang" href="{$page->localUrl($lang)}" class="..." > {$lang->title} </a> </div> Useful example and good jump start for me to try latte thanks for posting 1 Link to comment Share on other sites More sharing options...
Pixrael Posted July 21, 2022 Share Posted July 21, 2022 @bernhard check this custom filters for PW, maybe you wan to add this to your module: https://github.com/rolandtoth/TemplateLatteReplace/wiki/Additional-filters Link to comment Share on other sites More sharing options...
bernhard Posted July 21, 2022 Author Share Posted July 21, 2022 1 hour ago, Pixrael said: @bernhard check this custom filters for PW, maybe you wan to add this to your module: https://github.com/rolandtoth/TemplateLatteReplace/wiki/Additional-filters Thx @Pixrael I've seen those filters already. IMHO many of them are overkill: // why should I use this? {$page->title|bd} // if I can simply use this: {bd($page->title)} I don't want to add too much stuff that might be more confusing than helping. For example I also see no reason for this: // why this? <div {$page->featured_image->size(1180,320)|bgimage|noescape}></div> // and not simply that? <div style="background-image: url('{$page->featured_image->size(1180,320)->url}')"></div> The latter is much easier to control. Though there are some examples that could be a great timesaver, like this one: https://github.com/rolandtoth/TemplateLatteReplace/wiki/Additional-filters#srcset Do you have any specific wishes or experience with the other latte module? Link to comment Share on other sites More sharing options...
Pixrael Posted July 21, 2022 Share Posted July 21, 2022 8 minutes ago, bernhard said: Do you have any specific wishes or experience with the other latte module? Not really, but what I wanted is to help you to have material, to review, because it's difficult to have an idea of future needs ahead of time. You could discard the ones that don't make sense of course Link to comment Share on other sites More sharing options...
bernhard Posted July 21, 2022 Author Share Posted July 21, 2022 6 minutes ago, Pixrael said: Not really, but what I wanted is to help you to have material, to review, because it's difficult to have an idea of future needs ahead of time. You could discard the ones that don't make sense of course Yeah I was just hoping you have some experience with it and have done the review before so I don't have to do it ?? Proper image handling is something on my list and it's definitely something that I'd want to have in RockFrontend. The goals is to have a module that makes all the tedious frontend tasks easier and more efficient. srcset would be a perfect example and image handling is on my list for quite some time now, but I don't even have experience with webp at the moment so I might not be the best person to implement such a feature ? Let's see what you guys think of RockFrontend once it is published and then maybe someone wants to contribute... Thx for the suggestions ? 1 Link to comment Share on other sites More sharing options...
Greg Lumley Posted July 22, 2022 Share Posted July 22, 2022 Gotta be honest, I'm really excited about this! Cant' wait for RockFrontend. I just want to jump in! Are there any "Hello Worlds" or should I just be patient and wait for the module. ? Have a great weekend. 1 Link to comment Share on other sites More sharing options...
bernhard Posted July 27, 2022 Author Share Posted July 27, 2022 Hey @Greg Lumley I wanted to finish my video about RockFrontend last week or weekend, but it turned out to be a lot more time consuming that I thought ? Yesterday I even realised that some parts of RockFrontend could be improved even more, so I decided to do some important updates before finalising the video. I love those new additions ? To get live reloading for example is now really just installing the module and adding "$config->livereload = 1" to your PW site ? No injecting of scripts whatsoever... Back to topic: Today I had to build an accordion content-element: <ul> <li n:foreach="$block->items() as $item"> <a href="#">{$item->title}</a> <div>{$item->content()|noescape}</div> </li> </ul> IMHO it can't get any cleaner ? 3 Link to comment Share on other sites More sharing options...
Greg Lumley Posted July 27, 2022 Share Posted July 27, 2022 @bernhard This is going to be so powerful. I've done a few videos (not for dev) over the years, it sure takes a lot more time than people think it does. Good luck with that! Really looking to RockFrontend! Thank you for your work. 1 1 Link to comment Share on other sites More sharing options...
d'Hinnisdaël Posted July 29, 2022 Share Posted July 29, 2022 Latte is great indeed! I've been using it for all projects since discovering it. Everything is so much more concise and elegant, especially once your start building your own filters and macros. Quick note that you can also use Latte with Wanze's TemplateEngineFactory. I've found that the easiest way to get started; one composer install and you're ready to go ? 5 Link to comment Share on other sites More sharing options...
bernhard Posted July 29, 2022 Author Share Posted July 29, 2022 1 hour ago, d'Hinnisdaël said: especially once your start building your own filters and macros. Want to share some examples with us? ? 2 Link to comment Share on other sites More sharing options...
d'Hinnisdaël Posted August 1, 2022 Share Posted August 1, 2022 On 7/29/2022 at 7:02 PM, bernhard said: Want to share some examples with us? ? Sure! Here's a few that might be of use to ProcessWire sites: <?php use Latte\Engine; /** * Latte filter provider * */ final class Filters { /** * Install available filters. */ public static function install(Engine $latte) { foreach ((new static())->provide() as $name => $callback) { $latte->addFilter($name, $callback); } } /** * @return array<string, callable> */ public function provide(): array { return [ // Sanitize values using ProcessWire's sanitizer API variable 'sanitize' => function ($value, $sanitizer, $options = null) { if (!$options) { return sanitizer()->$sanitizer($value); } else { $args = func_get_args(); unset($args[1]); // remove $sanitizer arg return call_user_func_array([sanitizer(), $sanitizer], array_values($args)); } }, // Render FormBuilder form, but allow prepending and appending fields 'form' => function ($form, $options = []) { if (!$form) { return ''; } $output = $form->render(); if ($options['append'] ?? false) { $output = str_ireplace('</form>', $options['append'], $output); } if ($options['prepend'] ?? false) { if (stripos($output, '<form') !== false) { $output = preg_replace('#(<form[^>]*>)#i', '\\1' . $options['prepend'], $output); } else { $output = $output . $options['prepend']; } } return $output; }, // URL slug / page name 'slug' => function ($str, $length = 128) { $str = sanitizer()->unentities($str); $str = sanitizer()->pageNameTranslate($str, $length); return $str; }, // Truncate string 'truncate' => function ($str, $length = 200) { return sanitizer()->truncate($str, $length, ['visible' => true]); }, // Render Markdown 'markdown' => function ($str) { $str = "{$str}"; modules()->get("TextformatterMarkdownExtra")->formatValue(new Page(), new Field(), $str); return $str; }, // Unwrap ProcessWire value objects 'value' => function ($object) { if (is_object($object)) { if ($object instanceof SelectableOptionArray) { return $object->implode(' ', 'value'); } else { return $object->value ?? ''; } } elseif (is_array($object)) { return implode(' ', $object); } else { return (string) $object; } }, // Join a string with a custom separator at the end 'join' => function ($list, $separator = ', ', $lastSeparator = ' & ') { if (count($list) > 1) { $last = array_pop($list); return implode($separator, $list) . $lastSeparator . $last; } else { return implode($separator, $list); } }, // Prettify URL by removing protocol and www 'prettyUrl' => function ($url, $options) { if (null === $url) { return false; } $url = trim($url); if ($options['www'] ?? true) { $url = str_replace('www.', '', $url); } if ($options['http'] ?? true) { $url = str_replace(array('https://', 'http://'), '', $url); } if ($options['slash'] ?? true) { $url = rtrim($url, '/'); } return $url; }, ]; } } 4 Link to comment Share on other sites More sharing options...
d'Hinnisdaël Posted August 1, 2022 Share Posted August 1, 2022 And a few macros as well: <?php use Latte\Compiler; use Latte\MacroNode; use Latte\Macros\MacroSet; use Latte\PhpWriter; /** * Latte macro provider * */ final class Macros extends MacroSet { /** * Install available macros. */ public static function install(Compiler $compiler): void { $me = new static($compiler); $me->addMacro('ifispage', [$me, 'macroIsPage'], '}'); $me->addMacro('icon', [$me, 'macroSvgIcon']); $me->addMacro('trim', '', [$me, 'macroTrimEnd'], null); $me->addMacro('minify', [$me, 'macroMinifyHtml'], [$me, 'macroMinifyHtmlEnd'], null, self::ALLOWED_IN_HEAD); } /** * {ifispage $page} */ public function macroIsPage(MacroNode $node, PhpWriter $writer) { return $writer->write(' $isObj = %node.word && is_object(%node.word); $isCustomPage = $isObj && is_a(%node.word, "ProcessWire\DefaultPage"); $isCorePage = $isObj && get_class(%node.word) === "ProcessWire\Page"; $isPage = ($isCustomPage || $isCorePage) && %node.word->id; if ($isPage) { '); } /** * {icon 'search'} */ public function macroSvgIcon(MacroNode $node, PhpWriter $writer) { return $writer->write(' $icon = %node.word; $ratio = "xMinYMid"; $ratioAttr = $ratio ? "preserveAspectRatio=\"{$ratio}\"" : ""; $html = "<svg class=\'icon icon-{$icon}\' {$ratioAttr} aria-hidden=\'true\'><use xlink:href=\'#icon-{$icon}\' /></svg>"; echo $html; '); } /** * n:trim */ public function macroTrimEnd(MacroNode $node, PhpWriter $writer) { $node->validate(false); $node->openingCode = "<?php ob_start(); ?>"; $node->closingCode = '<?php $__output = ob_get_clean(); ?>'; if ($node->prefix === MacroNode::PREFIX_INNER) { // Simple case: n:inner-trim -> just trim content $node->closingCode .= '<?php $__output = trim($__output); ?>'; } else { // Trickier case: n:trim // remove whitespace *inside* outer tags // while preserving whitespace *outside* of outer tags // Before: " <h1> Title </h1> " // After: " <h1>Title</h1> " $node->closingCode = $node->closingCode . // Remove whitespace after opening tag '<?php $__output = preg_replace(\'~^(\s*<[^>]+>)\s+~s\', "\$1", $__output); ?>' . // Remove whitespace before closing tag '<?php $__output = preg_replace(\'~\s+(</[^>]+>\s*)$~s\', "\$1", $__output); ?>'; } $node->closingCode .= '<?php echo $__output; ?>'; } /** * {minify} */ public function macroMinifyHtml(MacroNode $node, PhpWriter $writer) { return $writer->write(' ob_start(function ($s, $phase) { // 0: replace newlines //$html = preg_replace("/\r\n|\r|\n/", "", $s); // 1: remove whitespace from between tags that are not on the same line. $html=preg_replace(\'~>\s*\n\s*<~\', \'><\', $s); // 2: replace all repeated whitespace with a single space. static $strip = true; $html=LR\Filters::spacelessHtml($html, $phase, $strip); return $html; }, 4096); '); } /** * {/minify} */ public function macroMinifyHtmlEnd(MacroNode $node, PhpWriter $writer) { return $writer->write(' ob_end_flush(); '); } } 2 Link to comment Share on other sites More sharing options...
dotnetic Posted August 2, 2022 Share Posted August 2, 2022 On 7/21/2022 at 5:39 PM, bernhard said: // if I can simply use this: {bd($page->title)} Or just use {dump $page->title} On 7/27/2022 at 2:21 PM, bernhard said: <ul> <li n:foreach="$block->items() as $item"> <a href="#">{$item->title}</a> <div>{$item->content()|noescape}</div> </li> </ul> I think you can drop the parentheses after items or content. Also you might want to use a n:if clause on the ul, so the markup is discarded if there are no items. <ul n:if="$block->items"> <li n:foreach="$block->items as $item"> <a href="#">{$item->title}</a> <div>{$item->content|noescape}</div> </li> </ul> I am also using Latte in an actual project and love it so far. For the integration into ProcessWire I am using TemplateEngineFactory. It also has a Latte renderer (but with Latte 2 atm, actual version is 3). 1 Link to comment Share on other sites More sharing options...
Recommended Posts