Creating a new page

Before we begin this tutorial, it is important that you understand what a web page really is. Unfortunately, many "classic" approaches to building a website give developers the mistaken impression that a web page "is" either a static .html page or a scripted .php page. Neither of these approaches are conceptually correct.

In reality, a web page is simply an HTTP GET request to which the server responds with an HTML body. For a more detailed explanation of this, please see The Client-Server Conversation.

It is true that with more primitive approaches to building a website or web application, there might be a one-to-one correspondence between pages and .html or .php files. Indeed, most web servers are configured by default to look for a web page or PHP script with the same name and relative path as the request URL (for example, http://example.com/admin/user.php and /var/www/admin/user.php). But, there is no law that says that we must use this approach.

In fact, UserFrosting adds several layers of abstraction between the request URL and the content and functionality of a page. Most notably, UserFrosting uses the front controller pattern and templating to isolate the URL of a page from its content and functionality.

In this tutorial, you will learn how to set up the components required to implement a basic web page:

  • Defining a route
  • Creating a new controller class and method
  • Creating a new page template file

Set up your site Sprinkle

If you haven't already, set up your site Sprinkle, as per the instructions in "Your First UserFrosting Site". For the purposes of this tutorial, we will call our Sprinkle site.

Defining a route

The first thing we'll need to do is set up a route for our page. This basically tells UserFrosting how it should respond to a request for a particular URL and HTTP method.

Let's say we want to define a page with the URL http://example.com/members/. We'll need to define a GET route for the /members URL.

To define routes in our Sprinkle, we'll need to create a routes/ directory. Inside this directory, we'll create a PHP file routes.php where we can define our route:

app
└── sprinkles
    └── site
        ├── config/
        ├── routes/
        │   └── routes.php
        ├── src/
        └── composer.json

In routes.php, we define our route as follows:

<?php

$app->get('/members', 'UserFrosting\Sprinkle\Site\Controller\PageController:pageMembers')
    ->add('authGuard');

Notice the ->add('authGuard') part. This is a piece of middleware which checks to make sure that only authenticated users can access this page. If you wanted to create a "public" page, you would simply remove this part of the route definition.

That's it! We've told UserFrosting that whenever someone requests the /members URL, that it should invoke the pageMembers method of the PageController class. What's that you say? I don't have a PageController class? Well then, we'll just have to create it now.

Creating a new controller class and method

The controller class is where the meat of our application logic will reside. In your Sprinkle's src/ directory, create a new directory Controller, and inside that create PageController.php:

app
└── sprinkles
    └── site
        ├── config/
        ├── routes/
        ├── src/
        │   └── Controller/
        │       └── PageController.php
        └── composer.json

PageController.php should look like this:

<?php

namespace UserFrosting\Sprinkle\Site\Controller;

use UserFrosting\Sprinkle\Core\Controller\SimpleController;

class PageController extends SimpleController
{
    public function pageMembers(Request $request, Response $response, array $args)
    {
        return $this->ci->view->render($response, 'pages/members.html.twig');
    }
}

Notice how we've defined the namespace for this class. If you correctly set up your Sprinkle's composer.json file, the namespace of the Sprinkle (UserFrosting\Sprinkle\Site) should correspond to the src/ directory of your Sprinkle.

Any subdirectories in src/ correspond to sub-namespaces in UserFrosting\Sprinkle\Site. Since our PageController class is in the Controller/ subdirectory, its full namespace would be UserFrosting\Sprinkle\Site\Controller.

This convention of mapping directories to namespaces is not our invention! It comes from the PSR-4 specifications.

Since SimpleController is in a different namespace, we need to use the use keyword to tell PHP where to find it. Otherwise, PHP will try to look in the same namespace that we defined with the namespace keyword by default. In this case, it would cause an error, because SimpleController is in UserFrosting\Sprinkle\Core\Controller while PageController is in UserFrosting\Sprinkle\Site\Controller.

Notice that pageMembers only does one thing - it renders the contents of a template file (pages/members.html.twig), and appends it to the response. We then return this (modified) response to the main application, which sends it off to the client.

Next we'll create the page template itself, which contains the actual HTML content we want to render.

Creating a new page template file

All templates live in the templates/ directory of your Sprinkle. These files end with .html.twig, which tells UserFrosting that a file is a Twig template for rendering an HTML document. Of course, you could use Twig to dynamically render Javascript (.js.twig) or other types of content as well.

In templates/ create a subdirectory pages/, and inside that create your template file, members.html.twig:

app
└── sprinkles
    └── site
        ├── config/
        ├── routes/
        ├── src/
        ├── templates/
        │   └── pages/
        │       └── members.html.twig
        └── composer.json

Your members.html.twig template should look something like this:

{% extends 'pages/abstract/default.html.twig' %}

{# Overrides blocks in head of base template #}
{% block page_title %}Members{% endblock %}

{% block page_description %}The members-only section of OwlFancy.com.{% endblock %}

{% block body_matter %}
    Welcome, {{ current_user.first_name }}!
{% endblock %}

Notice that we extend the default.html.twig abstract template, which is the same abstract template used by the "home" and "about" pages. If we wanted to create a "dashboard" style page, we would extend the pages/abstract/dashboard.html.twig template instead.

Then, we simply have to fill in some of the blocks defined in the abstract template with our page content. As a simple example of using Twig to produce dynamic content, we reference the current_user global Twig variable to get and display the user's first name.

The default UserFrosting theme is based on AdminLTE. Check it out while building your pages. It comes with pretty cool features and widgets you can use in your own pages !

Next steps

This recipe only covers the basics of setting up a new page. From here, you might want to try: