Jump to content

Storing templates in separate directories


Cal
 Share

Recommended Posts

is this possible? To keep things organised I'd like to have a separate directory for each set of related templates. They would all still be in the main templates directory, just in various subdirectories.

 

Surely I'm not restricted to having every template file in the same directory?

Link to comment
Share on other sites

Do you have so many templates that you need them in separated sub dirs ? Its all about organizing the site folder tree with your page tree ! Through time I also restructured things in the site folder but I keep my templates in the root of the templates folder. Instead I keep html format for different web site pages on the front in separate folders (page views) and any navigation (topnav, subnav) in separated folders. I also create separated associated css files for them in the styles folder. This speeded up my editing and maintenance time.

  • Like 1
Link to comment
Share on other sites

I am a total offender of using a ridiculous amount of templates, but there are many ways around this. Most of the time you can standardize your templates so that every different type of page doesn't need to use a different template, and just includes the functions that are applicable for that part of your site. Over the years there have been several discussions on this subject, and what I have seen everything come down to is just a matter of implementation. If you find that you are getting too many templates then possibly look into how you can make your code a bit more reusable and aware of its context. Here is a good starter, but I recommend googling our forum and focus on posts in relation to page structure.
 

 

  • Like 2
Link to comment
Share on other sites

I set all my templates up initially as "no-file" templates, using the Create a new template without a file... option, then go to the Files tab and enter "main" as the Alternate Template Filename. This points all my "logical" templates to a single "physical" template at /site/templates/main.php. From there, you can include the actual template any number of ways, the simplest being something like this:

echo wireRenderFile('./views/'.$page->template->name.'/'.$page->template->name.'.inc');

The main reason I do it this way is that I also use AllInOneMinify for asset management, and I have it set up to automatically include an associated javascript and/or LESS file for each template if present, so for the "home" template, instead of having home.php loose in the templates directory, I have:

/site/templates/views/home/

...which contains:

home.inc
home.js
home.less


 

 

  • Like 4
Link to comment
Share on other sites

1 hour ago, Jason Huck said:

I set all my templates up initially as "no-file" templates, using the Create a new template without a file... option, then go to the Files tab and enter "main" as the Alternate Template Filename. This points all my "logical" templates to a single "physical" template at /site/templates/main.php. From there, you can include the actual template any number of ways, the simplest being something like this:


echo wireRenderFile('./views/'.$page->template->name.'/'.$page->template->name.'.inc');

 

I like the sound of this!  Gonna give it a try, thanks for sharing.

Link to comment
Share on other sites

5 hours ago, Jason Huck said:

The main reason I do it this way is that I also use AllInOneMinify for asset management, and I have it set up to automatically include an associated javascript and/or LESS file for each template if present

Could you please elaborate a bit more on how to achieve this with AIOM? @Jason Huck

Link to comment
Share on other sites

On 9/24/2016 at 11:15 AM, Jason Huck said:

echo wireRenderFile('./views/'.$page->template->name.'/'.$page->template->name.'.inc');

I tried this method but found my defaults set in '_init.php' would be undefined when the page loaded. LIke this:

// _init.php - prepended to current page template, i.e. main.php
$content = $page->body;
$summary = $page->summary;

// main.php
<?php
    //include($page->template->name.'.inc'); //works fine
    echo wireRenderFile($page->template->name .'.inc'); // 'Notice: Undefined variable: 'summary'
?>

// basic-page.inc (template in admin alternate name = "main.php")
<?php if ($summary) {
    echo $summary;
}
    echo $content;

Still not 100% sure why this happens.

Link to comment
Share on other sites

the wireRenderFile() function won't have access to your custom variables, you would need to do this:

$viewBag = array(
  'content' => $content,
  'summary => '$summary,
);

then

echo wireRenderFile($page->template->name .'.inc', $viewBag);

 

  • Like 2
Link to comment
Share on other sites

56 minutes ago, Macrura said:

the wireRenderFile() function won't have access to your custom variables, you would need to do this:


$viewBag = array(
  'content' => $content,
  'summary => '$summary,
);

then


echo wireRenderFile($page->template->name .'.inc', $viewBag);

 

Thanks for the info.

Link to comment
Share on other sites

On 9/24/2016 at 11:31 AM, 3fingers said:

Could you please elaborate a bit more on how to achieve this with AIOM? @Jason Huck

It's pretty simple. AIOM accepts an array of files to include, and fails gracefully when one is missing. I keep the arrays in $page for portability. I load third party libraries and site wide stuff first, then layer on the view-specific assets if available, something like this:

<?php
    $page->styles = array(
        // third party libraries first...
        'lib/normalize-css/normalize.css',
        // ...etc...
        // site-wide/global assets next. this file imports many separate stub files...
        'styles/main.less',
        // now add the view file. you can check for its existence first,
        // but a missing file won't prevent the rest from working
        'views/'.$page->template->name.'/'.$page->template->name.'.less'
    );

    // do the same thing for scripts
    // $page->scripts = array(...
?>

...then in your template:

<link rel="stylesheet" href="<?= AIOM::CSS($page->styles); ?>">

...and:

<script src="<?= AIOM::JS($page->scripts); ?>"></script>

 

  • Like 1
Link to comment
Share on other sites

14 hours ago, Macrura said:

the wireRenderFile() function won't have access to your custom variables, you would need to do this:


$viewBag = array(
  'content' => $content,
  'summary => '$summary,
);

then


echo wireRenderFile($page->template->name .'.inc', $viewBag);

 

Apologies for quoting this again but there's something that's been bugging me. What's the point of using this method? I see structures like this when looking at MVC patterns for processwire:

/templates/
-main.php (master template)
-home.php
-basic-page.php
--/views/
---home-view.php
---basic-page-view.php

But why use the wireRenderFile() function at all? What's the advantage of having a '$viewBag' array in '_init.php' and then having to pass this around to various templates. From what I read, the idea is that the main logic would go in home.php and the HTML and basic logic would be more for home-view.php. However, what I've found that I'm struggling to see how to separate these things out. For example I have the following which outputs a basic page rendering the title/body/summary and a section underneath rendering the child pages title/summary:

// _func.php
function showChildPagesSummary() {
    $str = '';

    foreach(wire('page')->children as $item) {

        if($item->summary) {
            $str .= "<div class=\"section\">";
            $str .= "<h3><a href=\"$item->url\">$item->title</a></h3>";
            $str .= $item->summary;
            $str .= "</div>";
        }
    }
    return $str;
}

// basic-page.inc
<?php if ($summary) {
    echo $summary;
}

    echo $content;

    if (showChildPagesSummary()) {
        echo "<h2>In this section</h2>";
        echo showChildPagesSummary();
    }

//main.php
        <!-- main content -->
        <div id="content">
            <h1><?php echo $title; ?></h1>
            <?php include($page->template->name.'.inc'); ?>
        </div>

Why not just use include() like above which has access to the custom variables in the first place (in '_init.php')?

So I read over https://medium.com/@clsource/the-wire-render-pattern-806bf6d6097a#.3rpw6z6sq a few times and it looks to be abstracting away from functionality that's baked in already. I guess I just can't see a real world example where using views makes more sense (and wireRenderFile() along with custom variables passed as an array) rather than plain old include().

Any ideas would be appreciated. Thanks.

Link to comment
Share on other sites

34 minutes ago, SamC said:

Why not just use include() like above which has access to the custom variables in the first place (in '_init.php')?

I tend to agree, and use include() over wireRenderFile() myself. I suppose one reason some prefer wireRenderFile() is the scope of variables is limited so you don't run the risk of overwriting a variable of the same name in your template file.

As for using a view file separate to the the template file, it depends how strictly necessary you consider "separation of concerns" to be. Definitely good if you are part of a team and want to hand the view file on to a front-end person who shouldn't be dealing with business logic. Or if you have a lot of logic that you don't like cluttering up the view. As a solo designer-developer, I like to have my business logic in my template so I can see what is going into variables, but I guess a lot of people would frown on that.

  • Like 1
Link to comment
Share on other sites

FWIW, I don't do it to enforce MVC at all, which IMO is often overkill for typical web projects. I also don't have the kind of duplication you've illustrated for each template, with e.g. both home.php AND home.inc files. I route everything through a single main.php file. That file is just a series of includes for various global functions and configuration, and at the bottom I use output buffering to include the page-specific view and wrap it with a global base template. I prefer doing it that way so that all of the global markup is in a single file rather than split across separate "head.inc" and "foot.inc" includes (which then have to live in every template file). I also like having view-specific js and css assets together in the same folder as the view. That makes more sense to me than organizing assets by file type.

I could just as easily have used wireRenderFile() instead of output buffering. I provided that as the example simply because it's available in PW and this is a PW forum. I also tend to store additional properties in $page rather than creating global variables (e.g., $page->foo = 'bar' instead of $foo = 'bar'), which makes it a lot easier to use wireRenderFile(). As long as you pass in $page, you know you've got everything you need, and you can add more properties later without worrying about updating your "view bag."

  • Like 4
Link to comment
Share on other sites

3 hours ago, Jason Huck said:

I also tend to store additional properties in $page rather than creating global variables (e.g., $page->foo = 'bar' instead of $foo = 'bar'), which makes it a lot easier to use wireRenderFile(). As long as you pass in $page, you know you've got everything you need, and you can add more properties later without worrying about updating your "view bag."

I especially like this idea!

Link to comment
Share on other sites

Thanks for all the replies, it's really helpful to a beginner.

8 hours ago, Robin S said:

I tend to agree, and use include() over wireRenderFile() myself. I suppose one reason some prefer wireRenderFile() is the scope of variables is limited so you don't run the risk of overwriting a variable of the same name in your template file.

As for using a view file separate to the the template file, it depends how strictly necessary you consider "separation of concerns" to be. Definitely good if you are part of a team and want to hand the view file on to a front-end person who shouldn't be dealing with business logic. Or if you have a lot of logic that you don't like cluttering up the view. As a solo designer-developer, I like to have my business logic in my template so I can see what is going into variables, but I guess a lot of people would frown on that.

I think each to their own. If there's no team involved, I don't think it matters. Personally, this is what I am enjoying most about PW. Coming mainly from Drupal 7, the ability to just add fields, create a template, and use an API to manually spit the data out onto a webpage anywhere I want, with the markup I want is rather liberating. I don't have separate 'header.inc' and 'footer.inc' files, they never usually change so I keep the whole lot in main.php, just change the middle content section.

6 hours ago, Jason Huck said:

I also tend to store additional properties in $page rather than creating global variables (e.g., $page->foo = 'bar' instead of $foo = 'bar'), which makes it a lot easier to use wireRenderFile(). As long as you pass in $page, you know you've got everything you need, and you can add more properties later without worrying about updating your "view bag."

This sounds quite interesting to me also but I'm not sure I understand what you mean by pass in $page to wireRenderFile e.g., $page->foo = 'bar'? Say I have this:

// _init.php
$home = $pages->get('/');
$title = $page->get('headline|title');
$content = $page->body;
$summary = $page->summary;

$viewBag = array(
    'home' => $home,
    'title' => $title,
    'content' => $content,
    'summary' => $summary
);

$page->foo = 'bar' // where would this fit in?


// main.php
<?php
    echo wireRenderFile($page->template->name .'.inc', $viewBag);
?>

 

Link to comment
Share on other sites

@SamC in your example, all of your variables are things that you can access directly from $page, so you could, if you wanted, just pass in $page instead of all of those individual variables. If you have anything extra you want to add, you can just add it. Just be careful not to accidentally override the built-in properties & methods.
 

$page->any_key => 'any value';
$page->another_key => 'another value';
echo wireRenderFile($page->template->name.'.inc', array('page' => $page)); // everything in $page will now be available within this template

 

  • Like 4
Link to comment
Share on other sites

11 minutes ago, Jason Huck said:

@SamC in your example, all of your variables are things that you can access directly from $page, so you could, if you wanted, just pass in $page instead of all of those individual variables. If you have anything extra you want to add, you can just add it. Just be careful not to accidentally override the built-in properties & methods.
 


$page->any_key => 'any value';
$page->another_key => 'another value';
echo wireRenderFile($page->template->name.'.inc', array('page' => $page)); // everything in $page will now be available within this template

 

Thanks. This might come in handy.

Link to comment
Share on other sites

  • 1 month later...
On 26.9.2016 at 9:18 PM, Jason Huck said:

@SamC in your example, all of your variables are things that you can access directly from $page, so you could, if you wanted, just pass in $page instead of all of those individual variables. If you have anything extra you want to add, you can just add it. Just be careful not to accidentally override the built-in properties & methods.
 


$page->any_key => 'any value';
$page->another_key => 'another value';
echo wireRenderFile($page->template->name.'.inc', array('page' => $page)); // everything in $page will now be available within this template

 

Great idea. Just a thought: Couldn't it be overkill to pass the whole $page object to the function (in case it has a lot of properties&methods, and the file to render needs just a few of them)?

Link to comment
Share on other sites

1 hour ago, ottogal said:

Great idea. Just a thought: Couldn't it be overkill to pass the whole $page object to the function (in case it has a lot of properties&methods, and the file to render needs just a few of them)?

Everything is in memory already anyways. You could even use $this->page in the included file even if you don't explicitly give it the object as $page. So there's not really overhead.

  • Like 1
Link to comment
Share on other sites

Since the Page object is already in the memory, I can't see any performance related issues with it, but I'm not a PHP resources optimizer wizard, so someone might chime in to shed more light on it.

Edit: @LostKobrakai was faster than me :) 

Edited by szabesz
Link to comment
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
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...