Home Page

Let’s create the home page displaying your recent posts.

Before we start, you should know at least some basics about Model-View-Presenter design pattern (similar to MVC):

  • Model – data manipulation layer. It is completely separated from the rest of the application. It only communicates with presenters.
  • View – a front-end definition layer. It renders requested data to the user using templates.
  • Presenter (or Controller) – a connection layer. Presenter connects Model and View. Handles requests, asks Model for data and then passes them to the current View.

In case of a very simple application like our blog, the Model layer will actually consist only of queries to the database itself – we don't need any extra PHP code for it. We only need to create Presenter and View layers. In Nette, each Presenter has its own Views, so we will continue with both simultaneously.

Creating the Database with Adminer

To store the data, we will use the MySQL database, because it is the most common choice among web developers. But if you don’t like it, feel free to use a database of your choice.

Let’s prepare the database which will store our blog posts. We can start very simply – just with a single table for posts.

To create the database we can download Adminer, or you can use another tool for database management.

Let’s open Adminer and create a new database called quickstart.

Create a new table named posts and add these columns:

  • id int, click on autoincrement (AI)
  • title varchar, length 255
  • content text
  • created_at timestamp

It should look like this:

CREATE TABLE `posts` (
    `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
    `title` varchar(255) NOT NULL,
    `content` text NOT NULL,
    `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB CHARSET=utf8;

It’s very important to use the InnoDB table storage. You will see the reason later. For now, just choose that and submit. You can hit Save now.

Try adding some sample blog posts before we implement the capability of adding new posts directly from our application.

INSERT INTO `posts` (`id`, `title`, `content`, `created_at`) VALUES
(1, 'Article One',  'Lorem ipusm dolor one',    CURRENT_TIMESTAMP),
(2, 'Article Two',  'Lorem ipsum dolor two',    CURRENT_TIMESTAMP),
(3, 'Article Three',    'Lorem ipsum dolor three',  CURRENT_TIMESTAMP);

Connecting to the Database

Now, when the database is created and we have some posts in it, it’s the right time to display them on our new shiny page.

First, we need to tell our application which database to use. The database connection configuration is stored in app/config/config.neon. Set the connection DSN and your credentials. It should look like this:

database:
    dsn: 'mysql:host=127.0.0.1;dbname=quickstart'
    user: root
    password: *enter password here*

Be aware of indenting while editing this file. NEON format accepts both spaces and tabs but not both together! Configuration file in the Web Project uses tabs as default.

Whole configuration including is stored in app/config/ in files config.neon and config.local.neon. File config.neon contains global configuration of application and config.local.neon contains only the parameters specific to the environment (e.g. difference between develop and production server).

Injecting the Database Connection

The presenter (located in app/presenters/HomepagePresenter.php), which will list the articles, needs a database connection. To receive it, write a constructor like this:

<?php

namespace App\Presenters;

use Nette;


class HomepagePresenter extends Nette\Application\UI\Presenter
{
    /** @var Nette\Database\Context */
    private $database;

    public function __construct(Nette\Database\Context $database)
    {
        $this->database = $database;
    }

    // ...
}

Loading Posts from the Database

Now let’s fetch the posts from the database and pass them to the template, which will then render the HTML code. This is what the so called render method is for

public function renderDefault()
{
    $this->template->posts = $this->database->table('posts')
        ->order('created_at DESC')
        ->limit(5);
}

The presenter now has one render method renderDefault() that passes data to a view called default. Presenter templates can be found in app/presenters/templates/{PresenterName}/{viewName}.latte, so in this case the template will be located in app/presenters/templates/Homepage/default.latte. In the template, a variable named $posts is now available, which contains the posts from database.

Template

There is a generic template for the whole page (called layout, with header, stylesheets, footer, …) and then specific templates for each view (e.g. for displaying the list of blog posts), which can override some of layout template parts.

By default, the layout template is located in app/presenters/templates/@layout.latte, which contains:

...
{include content}
...

{include content} inserts a block named content into the main template. You can define it in the templates of each view. In this case we will edit the file app/presenters/templates/Homepage/default.latte like this:

{block content}
    Hello World
{/block}

It defines the content block, which will be inserted into the layout. If you refresh the browser, you’ll see a page with text “Hello world” (in source code also with HTML header and footer defined in @layout.latte).

Let’s display the blog posts – we will edit the template like this:

{block content}
    <h1 n:block="title">Můj blog</h1>

    {foreach $posts as $post}
    <div class="post">
        <div class="date">{$post->created_at|date:'F j, Y'}</div>

        <h2>{$post->title}</h2>

        <div>{$post->content}</div>
    </div>
    {/foreach}
{/block}

If you refresh your browser, you’ll see the list of your blog posts. The list isn't very fancy or colorful, so feel free to add some shiny CSS to www/css/style.css.

The {foreach} macro iterates over all posts passed to the template in $posts variable and displays a piece of HTML code for each post. Just like a PHP code would.

The |date: thing is called a filter. Filters are used to format the output. This one transforms the given timestamp (e.g. 2013-04-12) to a nice and readable date format (April 12, 2013). You can find more predefined filters in the documentation or you can add your own if you need to.

One more thing. We can make the code a little bit shorter and thus simpler. We can replace Latte macros with n: attributes like this:

<div n:foreach="$posts as $post" class="post">
    <div class="date">{$post->created_at|date:'F j, Y'}</div>

    <h2>{$post->title}</h2>

    <div>{$post->content}</div>
</div>

The n:foreach, simply wraps the div with a foreach block (it does exactly the same thing as the previous block of code).

Summary

We have a very simple MySQL database with some blog posts in it. The application connects to the database and displays a simple list of the posts.