fvsch / kirby-twig

Twig templating support for Kirby CMS 2. For Kirby 3, use https://github.com/amteich/kirby-twig
MIT License
70 stars 8 forks source link

Write controller for included twig-module and not for page/template #11

Closed Jones-S closed 7 years ago

Jones-S commented 8 years ago

I have a setup where I create twig pages which mostly consist of included twig modules. When I now want to write a controller file, I have to name this similar as the page name. I would prefer to name it after the module name, so that I can I just embed the module on whatever page and it loads the content automatically, without me having to add a controller for each page which includes the module (with the same code!).

So is there a way I can tell a module where to get it's controller from? Or how would you approach this challenge? (Maybe this is even a kirby-related question, I don't really know...)

Thanks for your help

Cheers

fvsch commented 8 years ago

I’m labelling this as a question for now, because I’m not sure what you’re referring to or if it can be solved through code in this extension.

What do you mean by a Twig module. Is it a content block that may appear on several pages, that has a HTML output (a <div> or <section> or <form> with more elements in it), and that you include in your pages as <?php snippet('subscribe-form') ?> (with Kirby PHP snippets) or {% include 'modules/subscribe-form.twig' %} maybe?

In that case, if this block relies on data from the CMS, I don’t think Kirby provides a way to create a Controller for a block or snippet. Controllers are for pages only and there’s only one controller per page at most. So you have two options:

  1. Put the logic (retrieving content from the CMS, some conditions maybe) in the snippet (PHP) or included template (Twig).
  2. Put the logic in a controller, maybe the site/controllers/site.php fallback controller (note that this controller only runs for page types which don’t have their own controller).

What solution is more convenient and sensible depends on what you’re trying to do. If you’d like to describe your use case, maybe I can point you to one or two decent solutions.

Jones-S commented 8 years ago

Sorry for the vagueness... Exactly, a twig module is a content block that may appear on multiple pages. And this is the include pattern: {% include 'modules/subscribe-form.twig' %}

We did it by writing a site.php as you are supposing too. But we did it on the root level, two directories up (from controllers). There we include a data_theme.php which holds a DataTheme Class. That class provides methods for retrieving specific data. (Let's say it can retrieve all visible pages). Now every page type will get it's controller file and we use the Class's methods to get what we want.

E.g. practically every page will use the loadMenuPages() method to get all pages and to display them in the menu (menu-module), whereas a very specific page which loads an image gallery, will use the getGalleryImages() method and no other page will.

That's how we solved it.

Thanks for your answer. Very much appreciated! Maybe a question: What do you think makes more sense; should we use the site.php in the root directory or should we switch and use your proposed method and loading the data_theme.php in the site.php in the controllers directory? We did not know that there was a fallback controller. We thought there was default.php as a fallback, but it is only implemented in the develop branch and not released yet. (and as we are installing kirby with the kirby-cli we can only use the kirby from the master branch).

Well thanks again

cheers

fvsch commented 8 years ago

Okay, so what you could do:

For instance on a few projects I’ve done this kind of thing:

/**
 * Whether a page is considered visible/public or not
 * (since we're not using Kirby's $page->visible()).
 * @param Page $page
 * @return boolean
 */
$kirby->set('page::method', 'isPublished', function(Page $page) {
    // Use the Kirby $page object to look at the page's fields and
    // return true or false
});

/**
 * Complete an asset's URL with base directory and timestamp
 * @param Site $site
 * @param string $url
 * @return string
 */
$kirby->set('site::method', 'asset', function(Site $site, $url='') {
    // …
});

Which allows me to use those methods easily in Twig templates:

<link rel="stylesheet" href="{{ site.asset('css/main.css') }}">
{% if page.isPublished %}
  Show something
{% endif %}

Again, not something you have to do, but by adding custom methods to the Site and Page classes you get the $site or $page object for free, without having to pass it to your function or class method.

Also if you haven’t read it already, the Twig templating in Kirby guide might help.

To sum up, using these tools I don’t think you’ll have to use controllers, or a fallback controller, for the specific needs you talked about in this issue.

Of course you can still do it. For instance if you want all pages to have access to the result of a method, and you have 3 controllers:

site/controllers/
    category.php
    page.php
    site.php (fallback)

then in each controller you could return the result of your method:

<?php
return function($site, $pages, $page) {
    $data = [];
    // …
    $data['something'] = MyStaticClass::myMethod();
    $otherThing = new MyOtherClass();
    $data['otherthing'] = $otherThing->someMethod();
    return $data;
};

But if you have many controllers you would have to use the same code in each controller, or use an include that returns an array and merge it. Which is not necessarily a bad thing.

To sum up, Kirby is pretty flexible; it’s mostly basic PHP, with a few anchoring points such as Page methods and controllers, that you can use or skip. So there are always several ways to do something “right”.