Building a website with bootstrap 4 - alternate template strategy

Posted 3rd Nov 2017

#bootstrap4 #completeguide #templatestrategy

Follow pages 2 & 3 of this guide to set up your fields, templates and pages then come back to this tutorial.

NOTE: Remember to set the 'alternate template filename' for each of these templates when you create them to '_main' as we are using the alternate template strategy here.

We're also able to use a slightly different folder structure because of this method. It's purely to show that you can organize your templates in different ways.

/site/templates/_main.php

<?php namespace ProcessWire; ?>

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">

  <title>
    <?php
      // print the current page field altTitle value
      // if blank or not present, print title field value
      echo $page->get("altTitle|title");
    ?>
  </title>

  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
  <link rel="stylesheet" href="<?php echo $config->urls->templates; ?>styles/main.css">
</head>

<body>

  <?php include("./includes/header" . ".php"); ?>

  <?php include("./views/{$page->template->name}" . ".php"); ?>

  <?php include("./includes/footer" . ".php"); ?>

  <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.11.0/umd/popper.min.js" integrity="sha384-b/U6ypiBEHpOf/4+1nzFpr53nxSS+GLCkfwBdFNTxtclqqenISfwAzpKaMNFNmj4" crossorigin="anonymous"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/js/bootstrap.min.js" integrity="sha384-h0AbiXch4ZDo7tp9hKZ4TsHbi047NrKGLO3SEJAg45jXxnGIfYzk4Si90RDIqNm1" crossorigin="anonymous"></script>

</body>

</html>

/site/templates/views/basic-page.php

<?php namespace ProcessWire; ?>

<div class="container">
  <div class="row py-5 justify-content-center align-items-center">
  <?php
    // if the page has a featured image
    if ($page->featuredImage):
    // https://processwire.com/api/fieldtypes/images/
    // set some default image options
    $options = array('quality' => 80, 'cropping' => 'center');
    // create a new image on the fly 800px wide
    $img = $page->featuredImage->width(800, $options);
    // get the url to the image
    $imgUrl = $img->url;
    // get the description field
    $imgDesc = $img->description;
  ?>

    <div class="col-md-6">
      <img src="<?php echo $imgUrl; ?>" alt="<?php echo $imgDesc; ?>" class="img-fluid" />
    </div>

    <?php endif; ?>
  
    <?php
      // change class name if image is present
      if ($page->featuredImage) {
        $colClassName = "col-md-6";
      }
      else {
        $colClassName = "col";
      }       
    ?>
    <div class="<?php echo $colClassName; ?>">
      <h2><?php echo $page->summary; ?></h2>
    </div>

  </div>
</div>

/site/templates/views/home.php

<?php namespace ProcessWire; ?>

<div class="container py-5">
  <h2>Services</h2>

  <div class="row">

    <?php
      // get the child pages of the current page
      $servicesPages = $pages->find("template=services-entry, limit=3");
      foreach ($servicesPages as $servicesPage):
    ?>
  
    <div class="col-md-4">
      <div class="card">

        <?php
        // if the page object has a featured image
        if ($servicesPage->featuredImage):
        // https://processwire.com/api/fieldtypes/images/
        // set some default image options
        $options = array('quality' => 80, 'cropping' => 'center');
        // create a new image on the fly 800px wide
        $img = $servicesPage->featuredImage->width(400, $options);
        // get the url to the image
        $imgUrl = $img->url;
        // get the description field
        $imgDesc = $img->description;
        ?>

        <a href="<?php echo $servicesPage->url; ?>">
          <img src="<?php echo $imgUrl; ?>" alt="<?php echo $imgDesc; ?>" class="img-fluid card-img-top" />
        </a>

        <?php endif; ?>

        <div class="card-body">
          <h4 class="card-title">
            <a href="<?php echo $servicesPage->url; ?>"><?php echo $servicesPage->title; ?></a>
          </h4>
          
          <p class="card-text">
          <?php
            if ($servicesPage->summary) {
              echo $servicesPage->summary;
            }
          ?>
          </p>
        </div>

      </div>
    </div>

    <?php endforeach; ?>

  </div>
</div>


<div class="container pb-5">
  <div class="row justify-content-center align-items-center">
  <?php
    // https://processwire.com/api/fieldtypes/images/
    // set some default image options
    $options = array('quality' => 80, 'cropping' => 'center');
    // create a new image on the fly 800px wide
    $img = $page->featuredImage->width(800, $options);
    // get the url to the image
    $imgUrl = $img->url;
    // get the description field
    $imgDesc = $img->description;
  ?>

    <div class="col-md-6">
      <img src="<?php echo $imgUrl; ?>" alt="<?php echo $imgDesc; ?>" class="img-fluid" />
    </div>
  
    <div class="col-md-6">
      <h2><?php echo $page->summary; ?></h2>
    </div>

  </div>
</div>

/site/templates/views/services-entry.php

<?php namespace ProcessWire; ?>

<div class="container">
  <div class="row py-5">
    <div class="col-md-3">
      <h2>Services</h2>
      <ul class="list-unstyled">
          
        <?php
          // get the child pages of parent page
          $childPages = $page->parent->children;
          // iterate over them to create a menu
          foreach ($childPages as $childPage):
        ?>
        
        <li><a href="<?php echo $childPage->url; ?>"><?php echo $childPage->get("altTitle|title"); ?></a></li>
  
        <?php endforeach; ?>

      </ul>
    </div>

    <div class="col-md-9">

    <?php
      // if the page has a featured image
      if ($page->featuredImage):
      // https://processwire.com/api/fieldtypes/images/
      // set some default image options
      $options = array('quality' => 80, 'cropping' => 'center');
      // create a new image on the fly 800px x 400px
      $img = $page->featuredImage->size(800, 400, $options);
      // get the url to the image
      $imgUrl = $img->url;
      // get the description field
      $imgDesc = $img->description;
    ?>

      <img src="<?php echo $imgUrl; ?>" alt="<?php echo $imgDesc; ?>" class="img-fluid mb-3" />

    <?php endif; ?>
      
    <?php
      // check if body field exists
      if ($page->body) {
        echo $page->body;
      }
    ?>

    </div>
  </div>
</div>

/site/templates/views/services-index.php

<?php namespace ProcessWire; ?>

<div class="container">
  <div class="row py-5">
  <?php
    // get the child pages of the current page
    $childPages = $page->children;
    foreach ($childPages as $childPage):
  ?>
  
  <div class="col-md-4">

    <div class="card">

    <?php
      // if the page object has a featured image
      if ($childPage->featuredImage):
        // https://processwire.com/api/fieldtypes/images/
        // set some default image options
        $options = array('quality' => 80, 'cropping' => 'center');
        // create a new image on the fly 400px wide
        $img = $childPage->featuredImage->width(400, $options);
        // get the url to the image
        $imgUrl = $img->url;
        // get the description field
        $imgDesc = $img->description;
    ?>
      
      <a href="<?php echo $childPage->url; ?>">
        <img src="<?php echo $imgUrl; ?>" alt="<?php echo $imgDesc; ?>" class="img-fluid card-img-top" />
      </a>

    <?php endif; ?>
      
      <div class="card-body">
        <h4 class="card-title">
          <a href="<?php echo $childPage->url; ?>"><?php echo $childPage->title; ?></a>
        </h4>
        <p class="card-text">
          <?php
            if ($childPage->summary) {
              echo $childPage->summary;
            }
          ?>
        </p>
      </div>

    </div>
  </div>

  <?php endforeach; ?>

  </div>
</div>

/site/templates/styles/main.css

header {
  text-align: center;
}

footer {
  background-color: #444;
  color: #fff;
  text-align: center;
  font-size: 0.8rem;
}

/site/templates/includes/header.php

<?php namespace ProcessWire; ?>

<nav class="navbar navbar-expand-lg navbar-light bg-light">
  <a class="navbar-brand" href="/">Big Meow Services</a>
  <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
    <span class="navbar-toggler-icon"></span>
  </button>
  <div class="collapse navbar-collapse" id="navbarNav">
    <ul class="navbar-nav">

    <?php
      // get the homepage object
      $homePage = $pages->get("/");
      // get PageArray of homepage object and child page objects
      $navItems = $homePage->and($homePage->children);
      // or if you don't want the home link
      // $navItems = $homePage->children;
      // iterate over the $navItems PageArray
      foreach ($navItems as $navItem):
    ?>

      <li class="nav-item">
        <a class="nav-link" href="<?php echo $navItem->url; ?>"><?php echo $navItem->title; ?></a>
      </li>

      <?php endforeach; ?>

    </ul>
  </div>
</nav>

<header>
  <div class="jumbotron jumbotron-fluid">
    <div class="container">

      <h1 class="display-3"><?php echo $page->title; ?></h1>
      <p class="lead">
      <?php
        // only print this if the summary field exists
        // on the current page
        if ($page->altTitle) {
          echo $page->altTitle;
        }
      ?>        
      </p>
      
    </div>
  </div>
</header>

/site/templates/includes/footer.php

<?php namespace ProcessWire; ?>

<footer class="py-5">
  <div class="container">
    <p class="mb-0">
      &copy; Copyright Big Meow Services - <?php echo $datetime->date($format = "Y"); ?>
    </p>
  </div>
</footer>

And that's it! Now the site is using the alternate template strategy.

The site looks exactly the same as the other strategy which you can see here.

But don't stop there! There's a couple more strategies including delayed output and the newest one since processwire v3.0.62 called markup regions.

Feedback & support

I hope you enjoyed this tutorial. You can support pwtuts by following on @pwtuts. You can also donate at paypal.me/swcarrey to help support the site which would be awesome!

Related tutorials / See all

Suggest a tutorial

Please note: I do not store any of this information, it's simply used to send me an email. Your email address is required so I can get clarification on your request if needed.