V. FuelPHP basics

Now that we have installed a working version of FuelPHP, let's analyze, on a very basic level, how the framework works. We won't go into the details here; the idea is to only understand the necessary information to use the framework. In this section, you are recommended to follow and check our explanations on your installed instance; don't hesitate to explore files and folders, this will make you more comfortable when we will begin our project's implementation. In this section, we will approach the following:

  • The FuelPHP file system hierarchy
  • MVC, HMVC, and how it works on FuelPHP
  • The oil utility

The FuelPHP file system hierarchy

Let's dive into the directory where we have installed FuelPHP. You might want to follow along using a file browser. As this book is being written, the current version of FuelPHP has the following directory hierarchy:

  • /docs: contains an HTML version of the framework documentation
  • /fuel, which contains:
    • /fuel/app: Everything related to your application. This is where you will work most of the time. We will look into this directory in the upcoming The app directory section.
    • /fuel/core: The core classes and configuration. You should not change anything inside it, unless of course you want to contribute to the FuelPHP core.
    • /fuel/packages: Packages are core extensions, they are bundles containing reusable classes and configuration files. Using the FuelPHP default configuration, this is the only directory where you can install packages (your own as well as from external sources). Notice that there are already five installed packages. We will use each of them in this book.
    • /vendor: This directory contains third-party packages and libraries that are generally not FuelPHP-specific.
  • /public: This directory is accessible by external visitors. You want to put here files publicly available, as CSS or JS files for instance.

The app directory

As written earlier, the app directory is where you will work most of the time. Thus, you should be familiar with its hierarchy, which is given as follows:

  • /cache: This directory is used to store cache files that improve your application's performance.
  • /classes: Classes used by your application:
    • /classes/controller: Where you have to implement your controllers (see the MVC, HMVC, and how it works on FuelPHP section)
    • /classes/model: Where you have to implement your models (see the MVC, HMVC, and how it works on FuelPHP section)
    • /classes/presenter: Where you have to implement your presenters (see the MVC, HMVC, and how it works on FuelPHP section).
  • /config: Every configuration file. Since some files are important, we will list them as well:
    • /config/config.php: Defines important FuelPHP configuration items such as activated packages or security settings.
    • /config/db.php: Defines database connection information.
    • /config/routes.php: Defines the application's routes (we will
      approach them later in this chapter).
    • /config/development, config/production, config/staging, config/test: All configuration files in the config/ENV directory, ENV being the current environment, are merged with the ones in the config folder. For instance, if the FuelPHP environment is set to development (as it is by default), the config/development/db.php file will be recursively merged with the config/db.php file. In concrete terms, this means that configuration items defined in the config/ENV/db.php file overwrite those in the config/db.php file. We will illustrate this through an example in The oil utility and the oil console section.
  • /lang: Contains the translation files.
  • /logs: Contains the log files. The log file path depends on the day it is written. For instance, if you log a message on July 1, 2015, it will be saved in the file located in logs/2015/07/01.php.
  • /migrations: Contains the migration files, which allow you to easily alter your database in a structured manner. For instance, if many people are working on the same project, or if there are many instances of the same project (development/production), they make the database change easier. We will often use them in the book.
  • /modules: Contains your application's modules. Each module can be described as a bundle of code that can respond to requests and be easily reused on other projects. We will create a module for the blog project in Chapter 3, Building a Blog Application.
  • /tasks: Contains task files, which are classes that can be executed from the command line (for cron jobs for instance).
  • /tests: Contains test files, which can be used to automatically test your application. We will approach them in Chapter 5, Building Your Own RESTful API, to test our application.
  • /tmp: Contains temporary files.
  • /vendor: This directory contains third-party libraries and packages only used by your application.
  • /views: Contains the view files used by your application (see the MVC, HMVC, and how it works on FuelPHP section).

The packages

The fuel/packages directory contains five default packages that, when activated, can add interesting features to FuelPHP:

  • The auth package provides a standardized interface for user authentication. We will use this package in Chapter 5, Building Your Own RESTful API.
  • The email package provides an interface to send e-mails using different drivers. We will use this package in Chapter 3, Building a Blog Application.
  • The oil package allows you to speed up your application's implementation by generating code files, launching tests and tasks, or providing a CLI PHP console. We will use this package in all chapters and we will explore its features in The oil utility and the oil console section.
  • The orm: This package is an improvement of the FuelPHP's core models; it allows them to fetch complex queries and to define the relations between them. We will use this package in Chapter 2, Building a To-do List Application.
  • The parser: This package allows your application to render view files in common template systems such as Twig or Smarty. We will use this package in Chapter 5, Building Your Own RESTful API.

We will also create our own package in Chapter 4, Creating and Using Packages.

Class name, paths, and coding standard

In FuelPHP, there are five constants that define the location of the most important directories as follows:

  • APPPATH: The application directory (fuel/app)
  • COREPATH: The core directory (fuel/core)
  • PKGPATH: The packages directory (fuel/packages)
  • DOCROOT: The public directory (public)
  • VENDORPATH: The vendor directory (fuel/vendor)

You are recommended to read the official documentation about these constants at http://fuelphp.com/docs/general/constants.html (It can be accessed through the FuelPHP website by navigating to DOCS | TABLE OF CONTENTS | FuelPHP | General | Constants)

Please keep in mind, that we will often use these constants in the book to shorten file paths.

An interesting point is that FuelPHP allows you to change quite easily the folder structure: for instance, you can change in the public/index.php file the value of the constants that we just introduced, or you can change the directory where FuelPHP will load modules by changing the module_paths key in the APPPATH/config/config.php configuration file.

You might also have noticed that class names are related to their own path, as given in the following:

  • In the app directory, the classes/controller/welcome.php class is named Controller_Welcome
  • The classes/model/welcome.php class is named Model_Welcome
  • You can notice that classes are named the same way in the fuel/core directory

This result was not achieved by accident; FuelPHP follows by default the PSR-0 standard. You are recommended to read the official documentation about this standard at http://www.php-fig.org/psr/psr-0/.

MVC, HMVC, and how it works on FuelPHP

We will now look into one major aspect of the FuelPHP framework – the MVC and HMVC software architecture patterns.

What is MVC?

Model-view-controller (MVC) is a software architecture pattern that states that the code should be separated in three categories: models, views, and controllers.

For those who are not familiar with it, let's illustrate this through an example:

Mvc

Suppose a user tries to access your website. The following are some URLs he/she might request:

http://my.app/
http://my.app/welcome/
http://my.app/welcome/hello

Depending on the requested URL, your website is generally expected to return some HTML code and it also sometimes needs to update the database, for instance when you want to save the users' comments.

The returned HTML code is generated by the views, because this is what is received by the browser and indirectly seen by the user.

The database is generally updated through models. In concrete terms, instead of executing raw SQL code to access and update the database, the best practice is to use classes and instances to do so. Each class represents a model that is related to a specific table: for example, the car model would access the cars table. Each class' instance is a model instance linked to a specific row in a table: for example, your car's information can be saved as a car instance that will be linked to a specific row in the cars table. As we use classes instead of raw SQL code, the framework has already implemented frequently needed features such as reading, creating, saving, or deleting model's instances. A further advantage is that, as we used packaged and well-implemented methods to access our database, it can prevent most unintended security breaches that we can create when requesting the database using raw SQL.

The controllers allow the website to handle the user's request by selecting the correct view to send back (the response) and updating the database (through models) if necessary. Controllers handle a specific section of the website: for instance, the car controller will handle everything that is related to cars. Controllers are subdivided by actions that will handle specific features: for instance, the list action of the car controller will return a list of cars in HTML code. In practice, controllers are classes and actions are methods.

When the user requests a URL, the framework will select an action inside a controller to handle it. Those are generally chosen by convention; for instance, when requesting http://my.app/welcome/hello, the framework will choose the hello action inside the welcome controller. Sometimes, they can also be chosen using a routes configuration file that matches URLs to actions and controllers.

The views sometimes need to access models; for example, we need to access the car model's instances when we want to display a list of cars. However, views should never update models or the database; only the controllers and preferably models should do that.

Please note that additional code components as helpers or presenters can be added to ease the development process, but if you understood this section, you got the most important points.

How it works on FuelPHP

Let's illustrate how it works by testing our newly created website. We suppose that your application is available at the following URL:

http://my.app/

Actions and controllers

If you request a random URL, you will probably get a 404 exception. For instance:

http://my.app/should_display_404

But, if you request the following URL, you will display the same page as the home page:

http://my.app/welcome/index

If you request the following URL, you will display a different page:

http://my.app/welcome/hello

Let's first explain how the last two requests worked. You can notice that both URLs contain the welcome word just after the base URL. You can also find this word in the file name fuel/app/classes/controller/welcome.php; it turns out that welcome is a controller. Now, open this file using your preferred text editor. You will then read the following:

//...
class Controller_Welcome extends Controller
{
    //...
    public function action_index()
    {
        //...
    }
    //...
    public function action_hello()
    {
        //...
    }
    //...
}

You can notice the action_index and action_hello methods. These functions are called actions. Now, as you have probably guessed, when you request http://my.app/welcome/index, the action_index method will be called. In a more general manner, if you request http://my.app/CONTROLLER/ACTION, the action_ACTION method of the CONTROLLER controller will be called. Let's test that. Edit the action_index function to add a simple echo at the beginning:

public function action_index()
{
    echo 'Test 1 - Please never print anything inside an action';
    //...
}

Now, if you request http://my.app/welcome/index, you will read the printed content at the beginning of the web page. Though this is an easy way to test how things work, never print anything in your action or controller. When you print a message, you are already implementing the view entity; thus, printing something in the controller breaks the MVC pattern.

Views

But then how are the pages rendered? Let's analyze the only line of code in our index action:

public function action_index()
{
    return Response::forge(View::forge('welcome/index'));
}

View::forge('welcome/index') returns a View object generated from the fuel/app/views/welcome/index.php view file. We will use this function a lot in this chapter and this book, and will cover all its parameters, but you can read its official documentation in the FuelPHP website:

http://fuelphp.com/docs/classes/view.html#/method_forge. (It can be accessed through the FuelPHP website by navigating to DOCS | TABLE OF CONTENTS | Core | View)

Response::forge(View::forge('welcome/index')); returns a response object created from the View object. Additional parameters allow us to change headers or the page status. A response object contains all the necessary information that will be sent to the browser: the headers and the body (generally the HTML code). You are recommended to read the official documentation on the FuelPHP website at http:// fuelphp.com/docs/classes/response.html#method_forge (It can be accessed through the FuelPHP website navigating to DOCS | TABLE OF CONTENTS | Core | Response)

Since the view is generated from the fuel/app/views/welcome/index.php file, open it to discover its content. You can notice that this is the same HTML code as the one displayed when requesting the URL. Just after <h1>Welcome!</h1>, add <p>This is my first view change.</p>. Now, if you refresh your browser, you will see this message appear under the Welcome! title.

Parameters

It is possible to indicate parameters, both to the actions and to the views. For instance, replace your index action by the following code:

public function action_index($name = 'user', $id = 0)
{
    return Response::forge(
        View::forge(
        'welcome/index',
            array(
                'name' => $name,
                'id' => $id,
            )
        )
    );
}

And in the fuel/app/views/welcome/index.php view file, replace

<h1>Welcome!</h1>

by

<h1>Welcome <?php echo ($name.' (id: '.$id.')'); ?>!</h1>

Now, if you request the following URL:

http://my.app/welcome/index

the title will display Welcome user (id: 0)!

If you request the following URL:

http://my.app/welcome/index/Jane

the title will display Welcome Jane (id: 0)!

And if you request the following URL:

http://my.app/welcome/index/Jane/34

the title will display Welcome Jane (id: 34)!

You might have understood that if you request the following URL:

http://my.app/CONTROLLER/ACTION/PARAM_1/PARAM_2/PARAM3

The action_ACTION method of CONTROLLER will be called with the PARAM_1, PARAM_2, and PARAM_3 parameters. If there are less parameters defined in the URL than required in the method, either, if defined, the parameters take their default values (as illustrated previously), or, if no default value is defined, it will trigger a 404 error.

You can notice that we replaced

View::forge('welcome/index')

By

View::forge('welcome/index', array(
        'name' => $name,
        'id' => $id,
    )
)

View parameters are sent by the second parameter of \View::forge in an associative array. Here, the associative array has two keys, name and id, and their values are available inside the view file through the $name and $id variables.

In a more general manner, if you call the following:

View::forge('YOUR_VIEW', array(
        'param_1' => 1,
        'param_2' => 2,
    )
)

When the view file will be executed, parameters will be available through the $param_1 and $param_2 variables.

Routes

Though what we previously observed explains how the standard cases operate

http://my.app/CONTROLLER/ACTION

we haven't explained why the two following URLs return content though no associated controller and action can be found:

http://my.app/
http://my.app/should_display_404

For understanding why we have to open the fuel/app/config/routes.php configuration file:

<?php
return array(
    '_root_' => 'welcome/index', // The default route
    '_404_' => 'welcome/404', // The main 404 route
    'hello(/:name)?' => array('welcome/hello', 'name' => 'hello'),
);

You can first notice the following two special keys:

  • _root_: This defines which controller and action should be called when requesting the website root URL. Note that the value is welcome/index, you can now understand why http://my.app and http://my.app/welcome/index are returning the same content.
  • _404_: This defines which controller and action should be called when throwing a 404 error.

Beside specials keys, you can define the custom URLs you want to handle. Let's add a simple example at the end of the array:

'my/welcome/page' => 'welcome/index',

Now, if you request the following URL:

http://my.app/my/welcome/page

it will display the same content as in the following URL:

http://my.app/welcome/index

You have probably noticed that there is also another key already defined: hello(/:name)?. The routing system is quite advanced, and to fully understand it you are recommended to take a look at the official documentation:

http://fuelphp.com/docs/general/routing.html (It can be accessed through the FuelPHP website by navigating to DOCS | TABLE OF CONTENTS | FuelPHP | Routing)

Presenters

You might have seen that the hello action doesn't use the View class to display its content, but instead it uses the Presenter class:

public function action_hello()
{
return Response::forge(Presenter::forge('welcome/hello'));
}

Let's analyze what is happening in this case. First, you can notice that, as for the views, a view file exists at the following path: fuel/app/views/welcome/hello.php. If you open this file, you will see that the code is the same as the one displayed when requesting the URL http://my.app/welcome/hello, except for one tiny difference. You can find the following code:

<h1>Hello, <?php echo $name; ?>!

In a normal view, we would have to define the name parameter, except here we didn't. Though, when displaying the web page, this parameter seems to have a defined value (it displays Hello, World!). Where could it be defined then?

Probing a little further, you can find another file located at fuel/app/classes/presenter/welcome/hello.php. It contains the following:

class Presenter_Welcome_Hello extends Presenter
{
    //...
    public function view()
    {
        $this->name = $this->request()->param('name', 'World');
    }
}

This file contains a Presenter class. The view function is called before rendering the view and it is here that the name parameter is set. It tries to get the name from the request parameter, name, but if it is not defined, the default value is World.

If you wonder how to change this parameter, refer to the routes. For instance, request the URL http://my.app/hello/Jane.

One could then wonder the use of Presenter classes, since we could change the previous code into a more classic view and controller approach.

Let's show its usefulness by an illustration. Suppose you have created an internal website managing the clients of your corporation. Each client is associated to a client category. In your creation, edition, and other forms, you thus display a selectable list of client categories. Each time you display the exact same selectable list, though you access it by using different controllers and actions. You can come up with three solutions:

  • You can create a classic view for your selectable list, load the list of client categories inside each of your actions, and pass this list to each view until you reach the location where you want to display your list. The problem is that it would induce a lot of code repetition.
  • You can create a classic view and load the list of clients inside this view. This way, you wouldn't have to pass along the necessary parameter. The problem is that you would break the MVC pattern by mixing models and views.
  • You can create a Presenter class, load the list inside the Presenter class, use it inside the view file, and display the view file using Presenter::forge. This solution is the best because it doesn't mix views and models but still limits the code duplication.

What is HMVC?

FuelPHP is a Hierarchical Model-View-Controller (HMVC) framework, meaning that it allows you to request internal controllers from your application. In concrete terms, the following code:

echo Request::forge('welcome/index')->execute();

will print exactly what the following URL would return:

http://my.app/welcome/index

Though we suggest you to use this feature in moderation, it can come handy when you want to implement and display widgets on several web pages.

You are recommended to read the following resources if you want to learn more about this pattern:

http://en.wikipedia.org/wiki/Hierarchical_model-view-controller

http://stackoverflow.com/questions/2263416/what-is-the-hmvc-pattern

The oil utility and the oil console

The oil utility is a very handy command-line tool. As the rails utility of Ruby on Rails, oil allows you to do the following:

  • Easily generate code files: models, controllers, migrations, and entire scaffoldings
  • Run tasks and migrations
  • Easily install, update, or remove packages
  • Test your code using PHPUnit test or a real-time console
  • Even run a PHP-built-in web server hosting your FuelPHP application (for PHP >= 5.4)

Though we will use all these features, except the last one, in this book, we recommend that you take a look at the official documentation at:

http://fuelphp.com/docs/packages/oil/intro.html (It can be accessed through the FuelPHP website by navigating to DOCS | TABLE OF CONTENTS | Oil | Introduction)

In this section, we are going to use the oil console, which is an important tool if you want to test your website, or, as in this case, a FuelPHP feature.

First, open your command-line utility and go to the root of your website directory. Then, enter the following line:

php oil console
If you use a web development platform such as WAMP or MAMP, you are recommended to use the PHP executable inside the platform directory for launching the oil utility (it might not work otherwise). As I wrote this book, this executable is located at WAMP_DIRECTORY\bin\php\phpVERSION\php.exe for WAMP, and at MAMP_DIRECTORY/bin/php/phpVERSION/bin/php for MAMP (VERSION depends on the version of PHP you installed, the best is to check this directory by yourself using a file explorer).

This will open the command-line interface oil provides. When you press Enter, something similar to the following should appear:

Fuel 1.7.2 - PHP 5.4.24 (cli) (Jan 19 2014 21:18:21) [Darwin]
>>>

You can now type any PHP code and it will be executed. Let's start with
something simple:

>>> $a = 2

If you press Enter, nothing will be printed, but the $a variable will be set to 2. Now, if you want to check a variable value, simply enter its name and then press Enter:

>>> $a
2

It also works for more complex variables:

>>> $a = array('a' => 'b', 'c' => 'd')
>>> $a
array (
'a' => 'b',
'c' => 'd',
)

But be aware, that you might have trouble displaying complex objects.

Let's now test a FuelPHP feature. Earlier, when discussing the app directory structure, we explained that the configuration files in the fuel/app/config directory were merged with the ones with the same filenames in the fuel/app/config/ENV directory, ENV being FuelPHP's current environment. We will now test this behavior.

First, let's check FuelPHP's current environment:

>>> Fuel::$env
development

The environment should be set to development.

Now, create a PHP file located at fuel/app/config/test.php where you will write:

<?php
return array(
    'this_is_the_root_config_file' => true,
);

Then create another PHP file located at fuel/app/config/development/test.php and write the following:

<?php
return array(
    'this_is_the_dev_config_file' => true,
);

and an additional one located at fuel/app/config/production/test.php, where you will write the following:

<?php
return array(
    'this_is_the_prod_config_file' => true,
);

Now, if you return to the command-line interface, you can load the test configuration file by writing the following:

>>> $conf = Config::load('test', true)

You are recommended to read the Config::load official documentation for more information at:

http://fuelphp.com/docs/classes/config.html#/method_load. (It can be accessed through the FuelPHP website by navigating to DOCS | TABLE OF CONTENTS | Core | Config)

As explained before, the value returned will be a mix of the fuel/app/config/test.php and the fuel/app/config/development/test.php configuration files:

>>> $conf
array (
  'this_is_the_root_config_file' => true,
  'this_is_the_dev_config_file' => true,
)

If we change the FuelPHP environment to production:

Fuel::$env = 'production'; // only do that for testing purposes

And load again the test configuration file:

>>> Config::load('test', true, true)
array (
  'this_is_the_root_config_file' => true,
  'this_is_the_prod_config_file' => true,
)

The merging will be done with the configuration file in the production folder.

You have probably noticed that we added a third parameter for Config::load. This parameter allows you to clear the configuration cache. If we didn't set it to true, the method would have returned the old configuration we loaded when we were in the development environment.

But what happens when the fuel/app/config/production/test.php and fuel/app/config/test.php configuration files contain the same key? The console can find the answer for us.

Change the content of the fuel/app/config/test.php configuration file to the following:

<?php
return array(
    'complex_value' => array(
        'root' => true,
    ),
    'this_is_the_root_config_file' => true,
);

and change the content of the fuel/app/config/production/test.php configuration file to the following:

<?php
return array(
    'complex_value' => array(
        'prod' => true,
    ),
    'this_is_the_root_config_file' => false,
    'this_is_the_prod_config_file' => true,
);

Let's now reload the test configuration files as follows:

>>> Config::load('test', true, true)
array (
  'complex_value' =>
  array (
    'root' => true,
    'prod' => true,
  ),
  'this_is_the_root_config_file' => false,
  'this_is_the_prod_config_file' => true,
)

It is interesting to analyze how the preceding two configuration files have been merged:

  • The this_is_the_root_config_file key shared by the two configuration files is associated in both cases to a simple value. In the resulting configuration, it is the value from the production file that prevails.
  • The complex_value key is associated in both cases to an array. The two arrays seem to have been merged in the resulting configuration.

This is because the configuration files are not merged by the array_merge native PHP function, but instead by the Arr::merge FuelPHP function, which merges arrays recursively. You are recommended to take a look at its official documentation at http://fuelphp.com/docs/classes/arr.html#/method_merge (It can be accessed through the FuelPHP website by navigating to DOCS | TABLE OF CONTENTS | Core | Arr)

It should be clear now that the console is a great tool that allows you to test your application. It can also be used as a great complement to the documentation, as you can try FuelPHP methods and their parameters without changing any files in your application.

This tutorial contains the first chapter of my book FuelPHP application development blueprints. I released this chapter freely so that you can have an idea of what the book looks like and get a first overview of the FuelPHP framework. Don't hesitate to comment or contact me if you have any question. You can buy the book on the Packt Publishing website, on Amazon, on BN.com and most internet book retailers.