Jump to content
Adam Kiss

Question: Understanding templates–Advanced templating

Recommended Posts

Hello all advanced users (Ryan, Antti, ?) :)

Introduction:

I am curious about how the template files get included, called, how they are run and such. Last week or so, I tried to create little MVC (or rather, V(C+M) :) ) directory skeleton, where templates itself would be classes generating data for 'views', which would be located in one of the directories in '/site/templates/'.

However, when I set up Base class, set few of my templates to use this, added View object (which in theory loads file and dumps data in it), prepared some place for common functions, and prepared autorendering in snippets (creating renderXyz function would give the template object variable called $Xyz with generated object/array of objects).

However, upon doing all of this, system felt really slow-ish, having generation time 0,5s-1s for localhost and simple, 3 or 4 field pages, which felt really unnatural - and I suspect my noobish object model :)

So this brings me to my question:

I noticed that there are TemplateFile objects, with render() function, and such, whole ProcessWire is 100% OOPhp, is there any recommended way I can use ProcessWire as any MVC framework, having autoloading, so I don't have to use require(); with objects for instance?

If this doesn't make any sense for you, I'll try rephrase later, but I hope it does make sense. :)

Share this post


Link to post
Share on other sites

Adam, that's a pretty slow page render time you had there. I don't think that can be attributed to taking an MVC approach, so am guessing there is some other factor at play that caused the bottleneck.

If you want your current template file to be a controller pulling in other views, then use the TemplateFile class and place your view files in a directory like /site/templates/views/

$t = new TemplateFile('./views/list-news.php');
$t->set('items', $pages->find('template=news, featured=1, sort=-date, limit=3'));
$out = $t->render();

Your /views/list-news.php file would look like this:

<ul>
<?php foreach($items as $item): ?>
<li><a href="<?=$item->url?>"><?=$item->title?></a> <?=$item->date?></li>
<?php endforeach; ?>
</ul>

If you are using this approach, it is far more efficient and straightforward to keep the foreach() in the view rather than calling up a new view for each item. It also gives the view the opportunity to handle an empty condition (if you want it to), and it prevents separation of the container from the items (i.e. you aren't splitting a <ul> and an <li> in separate files).

Another approach (and the one I like to use) is to use a module as a view. I'll create an autoload module that adds a 'renderNews' (or another name as applicable) to the PageArray class. Then I get the output of that view like this:

$out = $pages->find('...')->renderNews(); 

It's almost as easy with a normal, non-autoload module:

$m = $modules->get("MarkupRenderNews");
$out = $m->render($pages->find("..."));

Of course, the same approach can be done just by including a set of functions as views and calling upon them as needed, though I prefer the structure of modules for this stuff. But the end result is just as simple:

$out = render_news($pages->find('...'));
  • Like 3

Share this post


Link to post
Share on other sites

I'm interested in these ideas too.

I like the CMS back-end of PW but I'm not so keen on the way it essentially drills down to a file that just spits out content.

I would prefer an architecture thay is more like a pyramid whereby the index file dispatches based on the URL, fetches the data and returns the content and header in an object back up to the starting point (the index.php again) where it is handled appropriately.

It seems like this might be possible if I can just have a rewrite rule that redirects anything non-cms to my own secondary index file where I can use the API to handle it in the way I would like e.g. much more like a modern MVC framework such as Symfony2. I'm using TWIG for templating but this is just the start.

Share this post


Link to post
Share on other sites
I like the CMS back-end of PW but I'm not so keen on the way it essentially drills down to a file that just spits out content.

I'm not sure this describes what it actually does, or I might be misunderstanding what you are trying to say. But what actually happens is that the template file assigned to the accessed page gets executed, as a PHP file. ProcessWire spits out no content. You have to specifically code your template file to spit out content. ProcessWire does not participate in this aspect, only you do.

That template file is handed a copy of $page, which represents the page that was requested by the URL. Whether you choose to do anything with that $page is up to you.

I would prefer an architecture thay is more like a pyramid whereby the index file dispatches based on the URL, fetches the data and returns the content and header in an object back up to the starting point (the index.php again) where it is handled appropriately.

This is an architecture that ProcessWire supports. Make all your pages use the same template file and this is what you will have. This doesn't mean all your pages have to use the same 'template' (unless you want them to), just the same 'template file'. To achieve that, edit each template, click 'Advanced' and set the 'Alternate Template Filename' to be the one you want to be the top of your pyramid (i.e. main.php). Another way to do it is to maintain separate template files that just include("./main.php")). Either way, all valid requests will be routed to your main.php with $page being the only thing different from request to request.

If you want requests routed to your template file that don't map to URLs in ProcessWire, then edit the template settings and turn on URL Segments in the URLs tab. Now all URLs below the page(s) using that template file will be handed any URLs below them that don't map to pages. You can then map $input->urlSegment(n) to whatever you want, whether a function/class method, another $pages->get/find call, another application, etc. This is a lot of fun to be had in here. :)

It seems like this might be possible if I can just have a rewrite rule that redirects anything non-cms to my own secondary index file where I can use the API to handle it in the way I would like e.g. much more like a modern MVC framework such as Symfony2.

You can do this, but your htaccess will have to identify the paths that get directed to your own script by looking for them with a rewrite rule. Or you'd have to put your script in a directory that is the same as the URL used to access it (or at least, the first url segment is the same). The htaccess file does not know which requests will result in 404s. It doesn't know what is "non-cms" unless the request is to a real file or directory. ProcessWire is what ultimately generates the 404.

  • Like 2

Share this post


Link to post
Share on other sites

I think I may have got the tone of that last post a bit wrong, I didn't mean to sound negative in any way.

It appears that "spitting out content" is how the tutorials presented the CMS as working, but as you say there is no necessity to do that.

I suppose what I'd really like to do (and what I've often wanted to do with CMS' I've used) is to essentially leave the back-end intact to manage the data in/out and build my own dispatcher because that way I can get as creative as I like and maintain a lot of control.

I think perhaps if I dig into the API a bit more deeply I may be able to essentially separate the whole thing in two. I can perhaps run the CMS as a subdomain and then have the principle domain for my own dispatcher, pull the page data etc myself and do with it as I please. Judging by the brief exposure I've had to the API, it appears to be very well-built and geared towards flexibility and modular design so I think it's a good candidate for this sort of thing.

Share this post


Link to post
Share on other sites
It appears that "spitting out content" is how the tutorials presented the CMS as working, but as you say there is no necessity to do that.

This is because we don't necessarily know the needs and programming level of the audience, so always try to present stuff in the most basic way possible. You already know your way around PHP, so some examples will seem simplistic. But my hope is that as people learn more, they uncover more layers and ways of doing things. For instance, the basic site profile uses a simple header/footer include system for the main markup, and each template outputs what's in between. This isn't the way I usually build sites. But I learned early on that the way I build sites doesn't connect with people until they know their way around the software. So the header/footer system is a way to relate back to the methods used by more entry-level systems like WordPress and Expression Engine. I'm also hopeful that we'll get more site profiles available for download that get into more advanced techniques that'll be better examples for the PHP coders in our audience. CMS architecture is one of my favorite topics, and I hope I didn't suggest you were being negative--you always ask good questions and I enjoy the discussion.

I think perhaps if I dig into the API a bit more deeply I may be able to essentially separate the whole thing in two. I can perhaps run the CMS as a subdomain and then have the principle domain for my own dispatcher, pull the page data etc myself and do with it as I please. Judging by the brief exposure I've had to the API, it appears to be very well-built and geared towards flexibility and modular design so I think it's a good candidate for this sort of thing.

What WillyC was referring to above is how you can bootstrap ProcessWire from another PHP application or command line script. If I understand what you are talking about correctly, this may be in line with what you want to do because you could create your own index.php and include() ProcessWire's index.php. From that point forward, you have a wire('api var') function that gives you access to the entire PW API, leaving you in control of everything else. Here's another really basic example: if you wanted to grab your /sitemap/ page and render it:

test.php:

<?php
include("/path/to/processwire/index.php");
$page = wire('pages')->get('/sitemap/');
echo $page->render();

Share this post


Link to post
Share on other sites

I'm having a play with bootstrapping and using alternate tools for request/response and templating.

I'm a huge fan of the Symfony2 framework and it's constituent components. If you haven't had a look I'd recommend it, the developers seem to have a very strong grasp on clean, modern, best-practise architecture. In terms of PHP frameworks and code quality I think it's the best I've ever seen, Fabien Potencier seems to be very forward-thinking.

My ultimate aim is to have an "admin.mysite.com" domain for the back-end and then write my own dispatcher that will live at "mysite.com". At this stage it seems entirely plausible, but I can't work out how to make the admin subdomain/site ONLY deal with CMS-realted page requests. In fact it'd be great to be able to filter out the "front" page requests and also get rid of any "/processwire" part of the URL for CMS requests so it looks cleaner.

All the above are non-essential though, it's more my own curiosity than anything!

Share this post


Link to post
Share on other sites
I'm a huge fan of the Symfony2 framework and it's constituent components.

I agree with all your points, it's a great framework. Admittedly it's rare that I use a traditional framework as most of the work I do better fits in line with what ProcessWire does. But if I found myself in a project that called for a more traditional framework, Symfony2 would be one of the first I'd consider.

My ultimate aim is to have an "admin.mysite.com" domain for the back-end and then write my own dispatcher that will live at "mysite.com". At this stage it seems entirely plausible, but I can't work out how to make the admin subdomain/site ONLY deal with CMS-realted page requests. In fact it'd be great to be able to filter out the "front" page requests and also get rid of any "/processwire" part of the URL for CMS requests so it looks cleaner.

I've not considered this need before, but you should be able to add a snippet to the top of your /site/templates/admin.php that prevents it from being accessed by your main site:

if($config->httpHost != 'admin.mysite.com') throw new Wire404Exception(); 

As for making the admin run at the root path, I don't immediately know of a way to do that while still having it power a site. Though you can certainly rename the admin page to something more transparent like "a" rather than "processwire".

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By DooM
      Hello,
      I'm working on something like "structure generator" for my own needs which is run after clean installation of PW to create basic structure, fields, install modules, remove unnecessities, etc.
      I'm trying to remove site/templates/scripts and site/templates/styles folders, but I'm out of ideas already. When I try PHP's unlink function, it says that the path doesn't exists, which is obvious, because it will try to delete those folders under FileCompiler folder. This is probably because PW compilation process? I'm initializing this mine "structure generator" in ready.php.
      Do any of you guys have any idea how to get rid of those folders? My styles and scripts folders are separated, so I don't need those two inside templates folder.
      Thanks for every advice. 🙂
    • By MateThemes
      Hello everyone!
      I am trying to add my repeater matrix fields to the search selector, but unfortunately nothing seems to work for me.
      I have following search code in my search.php:
      <?php namespace ProcessWire; // look for a GET variable named 'q' and sanitize it $q = input()->get('q'); // sanitize to text, which removes markup, newlines, too long, etc. $q = sanitizer()->text($q); // did $q have anything in it after sanitizing to text? if($q) { // Make the search query appear in the top-right search box. // Always entity encode any user input that also gets output echo '<input id="search-query" value="' . sanitizer()->entities($q) . '">'; // Sanitize for placement within a selector string. This is important for any // values that you plan to bundle in a selector string like we are doing here. // It quotes them when necessary, and removes characters that might cause issues. $q = sanitizer()->selectorValue($q); // Search the title and body fields for our query text. // Limit the results to 50 pages. The has_parent!=2 excludes irrelevant admin // pages from the search, for when an admin user performs a search. $selector = "title|body~=$q, limit=50, has_parent!=2"; // Find pages that match the selector $matches = pages()->find($selector); } else { $matches = array(); } // unset the variable that we no longer need, since it can contain user input unset($q); ?> <main pw-replace='main'> <?php include('./includes/_pageheadersearch.php'); ?> <div id='content-body' class='uk-section uk-section-large uk-section-large'> <div class='uk-container uk-container-small'> <?php // did we find any matches? if(count($matches)) { // yes we did, render them echo ukAlert(sprintf(_n('Found %d page', 'Found %d pages', $matches->count), $matches->count), "default", "check"); echo ukDescriptionListPages($matches); } else { // we didn't find any echo ukAlert(__('Sorry, no results were found'), "danger", "warning"); } ?> </div> </div> </main> I have tried to add my fields to the selector code (repeater_matrix.aboutsblock_repeaters.mytextfield) . But I didn't get any results.
      What I am doing wrong?
      Thanks for your help!
    • By Robin S
      This module corrects a few things that I find awkward about the "Add New Template" workflow in the PW admin. I opened a wishlist topic a while back because it would good to resolve some of these things in the core, but this module is a stopgap for now.
      Originally I was going to share these as a few standalone hooks, but decided to bundle them together in a configurable module instead.
      Add Template Enhancements
      A module for ProcessWire CMS/CMF. Adds some efficiency enhancements when adding or cloning templates via admin.

      Features
      Derive label from name when new template added: if you like to give each of your templates a label then this feature can save some time. The label can be added automatically when templates are added in admin, in admin/API, or not at all. There are options for underscore/hyphen replacement and capitalisation of the label. Edit template after add: when adding only a single template, the template is automatically opened for editing after it is added. Copy field contexts when cloning: this copies the field contexts (a.k.a. overrides such as column width, label and description) from the source template to the new template when using the "Duplicate/clone this template?" feature on the Advanced tab. Copy field contexts when duplicating fields: this copies the field contexts if you select the "Duplicate fields used by another template" option when adding a new template. Usage
      Install the Add Template Enhancements module.
      Configure the module settings according to what suits you.
       
      https://github.com/Toutouwai/AddTemplateEnhancements
      https://modules.processwire.com/modules/add-template-enhancements/
    • By joe_ma
      On a new pw 3.0.89 installation of mine I've got a problem with role permissions. The setup is like this:
      Templates: home, basic-page, text-only, folder
      All of these have no restrictions in the "family" section and all of them have the same access definitions (see edit-template.jpg below)
      There is a role "redaktor" that has permission to edit, add, delete, move and also clone pages (see permissions.jpg below).
      Now, when logged in as a user with this role, I can only choose from basic-page and folder. The text-only template is not available.


    • By EyeDentify
      I as have been a bit confused for some time about how the "Markup Regions" functionality in Processwire worked.
      But i have know read a bit more and think that i am getting to grips with it. And Markup Regions is going to be huge.

      To aid me in understanding Markup Regions better i started to read the Source code for the new "Regular" theme in conjunction with the Blog about the markup regions.
      It helped me a great deal to understand the basics and more fine details of it.

      A tip is to open both links and use the Source code of the "Regular" theme while reading the blog post.
       
      The Source code:
      https://github.com/processwire/processwire/blob/dev/site-regular/templates/_main.php
      The Blog post:
      https://processwire.com/blog/posts/processwire-3.0.62-and-more-on-markup-regions/
       
      I hope this could help others starting out with markup regions.
      Just take it slow and read it a couple a times and soon you will see the greatness of markup regions.

      /EyeDentify 
×
×
  • Create New...