Anders

Clean syntax for rendering pages with specific template?

Recommended Posts

Consider this example, where I want to print a list of teasers (linked title + summary) for news articles:

$content = "";

foreach ($pages->find("template=article") as $article) {
    $content .= wireRenderFile('teaser', array("item" => $article));
}

It works, but I find the code quite ugly. I would much prefer to be able to switch the call to wireRenderFile to something more like this:

$article->renderWith("teaster");

Or even better, replace the whole thing with something like this:

$pages->find("template=article")->renderAllWith("teaser");

Are there  any functions like that? I searched the API, but couldn't find anything. Or is there any other way to make my code look better? Or is my quest completely misquided here?

I just started out with PW today, so I have no clue what I am doing yet. I already love it though!

Share this post


Link to post
Share on other sites

Welcome to the PW forums @Anders!

32 minutes ago, Anders said:

Consider this example, where I want to print a list of teasers (linked title + summary) for news articles:

There are many different ways you could do this and at the end of the day it comes down to personal preference.

Myself, if this was a one-off listing of teasers that wasn't to be used on other templates I would tend to build the markup directly in the template file and not render any other files. And if it was a listing that was to be used in several template files I would tend to render a file that looped over multiple pages and I would pass those pages to the rendered file. I prefer to output markup directly and make use of the Markup Regions feature, but to keep with your case of populating a $content variable it would be something like this:

$content .= $files->render('teasers', ['items' => $pages->find("template=article")]);

BTW, the wireRenderFile() function is sort of obsolete as far as I can see because it is just an alias for $files->render().

But if you wanted to render a page with something similar to your example...

37 minutes ago, Anders said:

$article->renderWith("teaster");

...then you could look at the (largely undocumented) $page->render() method. The best resource that I know of for this method is Ryan's post here:

Also you can read the comments for PageRender::renderPage() in /wire/modules/PageRender.module (which is what $page->render() calls behind the scenes).

So you could do something like:

foreach ($pages->find("template=article") as $article) {
    $content .= $article->render('teaser.php');
}

 

  • Like 2

Share this post


Link to post
Share on other sites

Your last code block (with $article->render) seems to render not only the teaser, but the full page of the article after that!

But it doesn't matter, because I think I like your $file->render suggestion the best anyway. I'll go with that. Thanks a lot for the help!

Share this post


Link to post
Share on other sites

On second thought.. Turns out module development is so simple due to the elegant API, so I went ahead and wrote myself a module:

<?php namespace ProcessWire;

class RenderWith extends WireData implements Module {

    public static function getModuleInfo() {
        return [
        'title' => 'RenderWith',
        'summary' => 'API extention for rendering pages with different templates.',
        'version' => 1,
        'autoload' => true
        ];
    }

    public function ready() {
        $this->addHook('Page::renderWith', $this, 'renderWith');
        $this->addHook('PageArray::renderWith', $this, 'renderManyWith');
    }

    public function renderWith($event) {
        $page = $event->object;
        $template = $event->arguments(0);
        $event->return = wireRenderFile($template, ["item" => $page]);
    }

    public function renderManyWith($event) {
        $pages = $event->object;
        $template = $event->arguments(0);
        $content = "";
        foreach ($pages as $page) {
            $content .= wireRenderFile($template, ["item" => $page]);
        }
        $event->return = $content;
    }

}

No idea if I am breaking any design principles or anything here, so viewer discretion is adviced.

  • Like 3

Share this post


Link to post
Share on other sites
26 minutes ago, Anders said:

Your last code block (with $article->render) seems to render not only the teaser, but the full page of the article after that!

Perhaps you have auto-prepended and/or auto-appended template files in your /site/config.php, in which case you would want to use the $options array to override those when rendering the page, e.g.

foreach ($pages->find("template=article") as $article) {
    $content .= $article->render('teaser.php', ['appendFile' => null]);
}

 

  • Like 3

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.