Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Layouts, sections and partials

The pages of a site share a frame: header, menu, footer. To avoid repeating it in every view, the frame is described once in a layout. A page declares which layout it uses and fills its sections — named places for content.

The layout

Create the file app/Views/layouts/main.php by hand:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title><?= esc($title ?? 'Blog') ?></title>
</head>
<body>
    <main>
        <?= $this->renderSection('content') ?>
    </main>
</body>
</html>

The renderSection('content') method marks the place where a page inserts its content. The section name can be anything.

A page based on the layout

Now the post list view contains only its own part:

<!-- app/Views/posts/index.php -->
<?= $this->extend('layouts/main') ?>
 
<?= $this->section('content') ?>
    <h1>Posts</h1>
    <p>The list of posts will be here.</p>
<?= $this->endSection() ?>

extend() names the layout, and everything between section('content') and endSection() lands in place of renderSection('content'). The controller does not change: it still calls view('posts/index'). Open /posts and look at the page source — the heading is inside the main tag.

Several sections

A layout may contain several places. Add the line <?= $this->renderSection('scripts') ?> before the closing body tag, and a page can fill a second section:

<!-- app/Views/posts/index.php -->
<?= $this->extend('layouts/main') ?>
 
<?= $this->section('content') ?>
    <h1>Posts</h1>
<?= $this->endSection() ?>
 
<?= $this->section('scripts') ?>
    <script src="<?= base_url('js/posts.js') ?>"></script>
<?= $this->endSection() ?>

A section the page did not fill is rendered empty by the layout.

Partials

A repeating fragment — a menu, a footer — is moved to a separate file. Create app/Views/partials/nav.php:

<!-- app/Views/partials/nav.php -->
<nav>
    <a href="/">Home</a>
    <a href="/posts">Posts</a>
</nav>

In the layout the include() method inserts it — add the line right after the opening body tag:

<body>
    <?= $this->include('partials/nav') ?>

The menu appears on every page that uses the layout.

A partial with its own data

To pass separate data to a fragment, for example one post inside a loop, call the view() function:

<div class="cards">
    <?php foreach ($posts as $post): ?>
        <?= view('posts/_card', ['post' => $post]) ?>
    <?php endforeach ?>
</div>

The file app/Views/posts/_card.php receives the $post variable. A leading underscore in the name is the accepted way to tell partials from whole pages.

Common mistake

In a view that extends a layout only the content inside sections is output. Markup written after extend() outside section() and endSection() never reaches the page. If a part of the page has disappeared, check that it sits inside a section and that the section name matches the name in renderSection().

Thanks for reading!

Donate to the site — we work hard for you.