Having used Fat-Free Framework 2.x for a little while now, I’m still generally quite happy with it… aside from one thing.
The built-in templating engine’s syntax doesn’t offer many advantages over raw PHP, it’s more verbose in certain common cases, and, as Fabien Potencier wrote when he took over maintainership for Twig, template languages have evolved a lot since PHP began its life as one in 1995. …so I decided to use Twig.
Why Twig?
It does help that I also work in Django sometimes but it’s not as big a factor as you might think. The main reason is that Twig is both the fastest, lightest templating engine I know (benchmark) and also one of the most featureful. Both good reasons to use FatFree and Twig together.
Here are a few of the reasons I switched:
- Fully extensible
- Auto-escaping for cleaner, safer code and less stressful coding.
- Let’s me choose my preferred mix of template inheritance and template includes.
- Supports macros for tidying up repeated code snippets like form fields.
- Has clever constructs like for/else to clean up common cases like “List results or display a ‘no results’ message”
- Syntax is easier for me to read and write in data-heavy templates with many nullable fields.
- Generally more amenable to the kinds of factoring-out of duplicated boilerplate that I want to do.
Setup Instructions
Unsurprisingly, I couldn’t get integration with F3::get()
as concisely as in F3 templates, but I did pretty well, all things considered.
The simplest way I’ve found to set this up is to add something along these lines to the top of your index.php:
require_once __DIR__ . '/lib/Twig/Autoloader.php'; Twig_Autoloader::register(); $twig_loader = new Twig_Loader_Filesystem(__DIR__ . '/templates'); $twig = new Twig_Environment($twig_loader, array( 'cache' => __DIR__ . '/twig_cache', 'auto_reload' => true, )); $twig->addFilter('f3', new Twig_Filter_Function('F3::get')); $twig->addGlobal('is_ajax', Web::isajax());
This will set up the following:
- Template Location
- Templates will be stored in a folder named
templates
so they don’t conflict with any templates you continue to use with the default F3 templates. (Assuming you’re still using the defaultui
folder you probably kept from the sample code) - Template Caching
- Twig templates will be cached in a folder named
twig_cache
to avoid conflicts and the cache will be automatically regenerated when the templates change - F3::get()
- The
f3
filter gives you'foo'|f3
as a slightly inelegant but not overly verbose equivalent to@foo
in templates. Things like'result'|f3.title
will work as desired. - Also, if you’re only using F3::get to expose things to the templates, you can use Twig globals, which let you use
{{ foo }}
instead of{{ @foo }}
. The syntax for doing so is as follows:$twig->addGlobal('foo', $foo);
- is_ajax
- This global allows you to do the switching between full templating and minimal responses for AJAX requests entirely in the templates using this snippet of code (source):
{% extends is_ajax ? "base_ajax.html" : "base.html" %}
- Rendering
-
global $twig; echo $twig->render('page.html', array('bar' => $bar));
Important: Compatibility Fix
The one caveat is that there’s a little incompatibility between the current versions of Twig and Axon. When using {% if my_hydrated_axon.my_null_valued_field %}
, you’ll get
Undefined method Axon->my_null_valued_field()
The problem is:
- Twig’s dot operator uses
isset()
to determine whether a property exists, then fails over to assuming you wanted a method call with no arguments. Axon.__isset()
usesisset()
on its internal array of fields.isset()
on an array returns false if the field exists but has a value ofNULL
.- Even with
strict_variables
set tofalse
, current Twig has a bug that makes it error out rather than producing an empty value when accessing axon properties.
You have two options. First, you can use my_hydrated_axon.cast.my_null_valued_field
everywhere (Axon.cast()
returns an ordinary associative array, which doesn’t have this problem) or you can make a small patch to FatFree’s lib/db.php
and change this…
function __isset($field) { return isset($this->fields[$field]) || isset($this->adhoc[$field]); }
…to this…
function __isset($field) { return (is_array($this->fields) && array_key_exists($field, $this->fields)) || (is_array($this->adhoc) && array_key_exists($field, $this->adhoc)); }
The only caveat with this approach is that, in your PHP code, if (!$axon->property)
won’t work properly.
Using Twig with Fat-Free Framework: Why and How by Stephan Sokolow is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
Hey, i did not get how to do the render part, could you explain a little more?
Thanks in advance.
BTW, I’m starting to use fat-free right now and i’m loving it (:
What, specifically, are you having trouble understanding?
I’d be happy to go into more detail, but I’m so used to working with this sort of thing that I’m not sure where someone might have trouble.
G’Day fellow coder Stephan
Just found your blog while glooking for
F3 diety framework examples
And follow’d to your great Gender-Bending Fiction Index
Nowadays, today, on which framework is
your Index build on ?
Looks like it’s made with Yii,
(exact. same icons & lists styles)
or build on F3?
If so, do you have any links to
libraries to handle clever lists
to help me kick-start with F3
Thanks in adv ance for your help Stephan!
Eman @ Switzerland
It’s still based on F3. The icons are from the Silk icon theme as linked in the site footer but I’m not sure which lists you’re talking about.
The sidebar is just an adjusted jQuery UI Tabs widget and the “Newest Entries” and search result lists were things I cobbled together myself using CSS.
I’d be happy to help you, but you’ll have to clarify what you mean by “handle clever lists”.
Hi Stephan and Thanks for replying
Actually, there are a bunch of great-looking PHP frameworks here around.
Project is to develop a similar Index as yours
But among all these easy-fast-great looking framework,
it’s a pitty examples are so sparse…
Reinventing the wheel,
again & again …
Any links to F3 backoffice samples are
GREATLY welcomed!!!
Thanks in advance for your precious time
Eman @ Switzerland
There’s still a lot of stuff in the GBIndex codebase that predates my switch to F3 and those parts can be pretty messy. (and I haven’t yet got an admin UI that works with the current SQL schema version)
I’m planning to open-source the site, but not until I can get that stuff out of the way.
However, if you’ve got a BitBucket account or an e-mail address which can take a 5MiB attachment, I don’t mind sending you the F3-based codebase in its current state.
Hi Stephan,
https://bitbucket.org/account/user/emanwebdev/
Guess the gmail provided can handle 5Mb+ of attachment
Looking forward discovering real dev built on top of F3
That’s great
Genial, Thanks a Lot for that!
Have Great Days
Eman
Have you managed to figure out how to get this working with named routes (https://github.com/bcosca/fatfree#named-routes) yet? – Rob
Unfortunately, my life has been very busy in my final year of university and I’ve had to put the F3-based project on hold. However, worse case scenario, you should just be able to write a simple Twig filter which wraps the F3 code and then call it something like this:
{{ 'foo'|route }}
I know that this is an old post, but I was just trying to use the info for adding Twig to F3 and it no longer works. Between the newer version of F3 and Twig on 2.0 now, this is not working. Is there any way to update it to work?
Unfortunately, I haven’t worked with F3 or Twig in a long time, so I can’t give any specific advice.
That said, I’d be very surprised if it wasn’t simple for someone to make them work together, just as a side-effect of how components are put together.
Can you share more details on what “not working” looks like for you? I might be able to at least give a bit of advice then.