RFC: Latte macros installation & TemplateFactory service

5 years ago

enumag
Member | 2128
+
0
-

I wanted to write a RFC for TemplateFactory but some changes in Latte seems necessary for the TemplateFactory to look good so this is kind of 2 in 1 RFC. Of course we can eventually use only one part of this RFC and solve the other using different way.

Part 1: Latte

1) Latte Engine as service

Currently Nette\Latte\Engine is not registered as service and creates its dependencies (Parser and Compiler) by itself instead of requiring them in constructor. The common opinion is that there should be a service EngineFactory which would be injected whereever latte is needed.

After thinking about it for a while, I don't see a why it couldn't be a service. How many differently configured instances of latte do you need in your application? One is enough most of the time. And if it isn't you could always pass down a different (non-autowired) instance in your config.neon, it's just the same as for any other service. A factory would be the same in this regard.

Obvious question: Ok, there maybe are no downsides with Engine as a service. But what are the benefits? See the macros installation below.

2) Macros installation

You will probably agree with me that these two things in Latte are wrong:

So let's try to get rid of these (while preserving BC if possible). Some BC break for Latte used without DIC is necessary though, see below.

First of all let's have Engine as a service. Then remove the creation of Parser and Compiler from its constructor and inject them via DI. That means Parser and Compiler will be services as well. Now how to add some macros to the compiler?

I am currently aware about two ways that could be used to provide macros by extension…:

  1. Interface like IMacroInstaller + tagged services (implementations of the interface)
  2. Interface for extensions + configuration section (so that one can add macros via config.neon)

…and two ways to actually pass the macros to Compiler:

  1. CompilerFactory class which would get an array of macro installers. The Compiler service is created by this factory.
  2. Foreach cycle with addSetup in extension.

I used the first option in both cases for my implementation but that can be changed easily.

Note: Future MacroSets could implement the IMacroInstaller by themselves, but is also possible to install the current MacroSets using a general MacroInstaller for BC.

3) What about Latte without Nette\DI?

Obviously removing the installation of default macros from constructor and requiring Parser and Compiler in constructor parameters are incompatible changes when there is no DIC to do all the work. The best way imho is to have a default factory for this (it could even be a static method) which would NOT be used by the LatteExtension for Nette\DI.

class LatteFactory
{
    public static function create()
    {
        $parser = new Parser();
        $compiler = new Compiler();
        $engine = new Engine($parser, $compiler);

        \Nette\Latte\Macros\CoreMacros::install($compiler);
        // maybe some other macros as well

        return $engine;
    }
}

Part 2: TemplateFactory

1) TemplateFactory as service

Now finally to the main topic, TemplateFactory. First of all, why should there be such factory?

  • Code reduction in UI\Control.
  • Usage of already configured templates even without a Control or Presenter (for emails, generating XML, etc.).
  • Configuration of template by extension (see ITemplateConfigurator below). Older implementations of TemplateFactory on GH don't support this.

Obvious question: What if I need differently configurated template somewhere in my app?

Obvious answer: Let's have ITemplateFactory interface, then you can implement your own (non-autowired) template factory and pass it whereever you need it in your config.neon or extension. It's just the same as with any other service, the only reason I mention it here is to say that the interface really is important.

As far as I know there are at least 5 other implementations of TemplateFactory, which is quite a number:

None of them implements anything like the following feature though:

2) ITemplateConfigurator interface

Again, let's start with the reasons for this:

  • Configuration of template by extension – adding helpers, filters and most importantly variables to be used by macros (most common use case is a macro which uses some service).
  • Easier separation of some Nette modules (Nette\Latte, Nette\Localization and Nette\Security).
  • Passing parameters from config.neon directly to template.
  • TemplateFactory is very simple and flexible.

Examples of such services: https://github.com/…onfigurators

And how to add them in extension: https://github.com/…xtension.php#…

3) TemplateFactory in Control and Presenter

I've implemented this trait which replaces the old behaviour.

Of course we didn't get rid of the context in Control, but that's a story for another RFC.

Implementation

For the time being I've implemented this behaviour as an extension.

https://github.com/…plateFactory

Compatibility

With this hacky commit all macros installed in the nette: section in config.neon and even macros installed in extension will work. No need for modifications of MacroSets, no need for modifications in extensions.

I don't think I've broken anything with the TemplateFactory either, I've pretty much just moved some code from Control::createTemplate() to new classes.

5 years ago

Filip Procházka
Moderator | 4693
+
0
-

Latte Engine as service

would you prepare pullrequest?

Macros installation

I see forest of classes where doesn't have to be any, we just need to fix the API for installation of macros to compiler

What about Latte without Nette\DI?

dislike

TemplateFactory as service

certainly not nette-way, it's overcomplicated, it should be one class with create method internally splitted to several protected methods that you can extend and override.

ITemplateConfigurator interface

dislike

5 years ago

David Grudl
Nette Core | 6797
+
0
-

I think we should turn around the discussion. First, we need to modify Latte that it can easily be used separately. I wrote RFC. After that we can create some higher abstractions for Application\UI.

5 years ago

enumag
Member | 2128
+
0
-

It seems I was way too concerned about compatibility. Your way does look better but will require some changes in applications.

5 years ago

stekycz
Member | 161
+
0
-

@enumag I like the idea but how do you want to solve link generating in template without control and presenter (in mails etc.)? I think there should be some UrlGenerator which should be a dependency for template which uses macros for link generating. I think this idea is not complete without this change.

5 years ago

vvoody
Member | 911
+
0
-

Actually there's no need control for generating links with “absolute” destination (started with “:” or “//:”). In LinkFactory/UrlGenerator could be setting control only optional. Or something like ControlLinkFactory extends LinkFactory?