Turbulences Tutorial - Chapter 2 : Using models and CRUD actions

We will continue where Chapter 1 ended. You should have completed Chapter 1 in order to complete this.

We will explain :

  • How to setup a table for your model
  • Basic CRUD actions :
    • adding an object (create - the C)
    • listing objects and showing a particular object (reporting - the R)
    • updating an object (the U)
    • deleting an object (the D)

CRUD actions are just the most common actions that we perform on data. Every modern framework must provide easy ways of solving these basic tasks. Please see this for more information about CRUD.

Schema and data

Turbulences serializes data to a database. Each model has its table in which lines correspond to individual objects - objects in the data modelization sense, but also in the OOP sense, as you usually create and use an object that represents the exact entity serialized in a certain database table line.

In order to create a table that will get added to the database at the installation stage of the application, you must create a db directory in your module directory (in modules/hello/db). The module generator script should have done this already. This directory may hold several types of file:

  • schema - this script creates the table : {module_name}.schema.sql
  • data - initial data for populating a table : {module_name}.data.sql
    • For example, this is useful to pre-fill a country table with the world's countries.
  • triggers : {module_name}.triggers.sql
  • test data, also known as a test fixture : {module_name}.data_test.sql

For these scripts to be run at installation, you must as well have a modules/hello/module.ini file that will list the scripts to play, like this:

[tables]
hello=schema,data,trigger; important put the schema before the data

All scripts but schema are optional. Even schema is optional if your module does not require a table. The data_test script is used by the test unit launchers. They populate a test database using these scripts instead of the normal data scripts.

Sample db files

  • hello.schema.sql
    -- table hello
    DROP TABLE IF EXISTS hello;
    CREATE TABLE hello (
        id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
        name VARCHAR(255) NOT NULL,
        PRIMARY KEY (id),
        INDEX name(name),)
    ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
    
  • hello.data.sql
    INSERT INTO `hello` ( `id` , `name` )
    VALUES ( 1, 'Test' );
    INSERT INTO `hello` ( `id` , `name` )
    VALUES ( 2, 'John' );
    INSERT INTO `hello` ( `id` , `name` )
    VALUES ( 3, 'Kate' );
    

You may launch again the install script (php myapp/script/install.php), and the hello module SQL files will be detected and installed after your confirmation.


List data

We are going to list all of our table's contents.

For that, we first define or update our model Hello in modules/hello/Hello.php:

class Hello extends TURB_Object    // your model class MUST extend TURB_Object
{
    var $_table = 'hello';  // define here the name of the SQL table your model matches; here, table `hello`
}

If your Hello model already exists, all you need to add is the new line.

Now we define a new list action in our controler (HelloController.php); in the HelloController class, add:

/**
 * An action is always defined as action_nameOfTheAction; so here, action_list
*/
public function action_list()
{
    $names = Hello::getFor('Hello'); // see 1.
    $view   = new HelloView();
    return $view->html_list($names);
}

The getFor() method is a static TURB_Object method. It returns an array of objects of the class you ask for (here Hello) matching the conditions you provide (here, none). You could, for instance, specify Hello::getFor('Hello', array('age' => 12, 'name' => 'junior'));.

Note that we "duplicate" the Hello info: we use the Hello model object and we ask the static method for a Hello object; this is because PHP 5.2 poorly handles static class methods; it will hopefully be fixed with 5.3 and we will upgrade the code then.

Now, create the associated view in HelloView:

//HelloView.php
protected function _list($names)
{
    $this->assign('names', $names);
}

Finally, we register the template for the list (in modules/hello/views/html/list.tpl):

{if $names|@count neq 0} 
    {foreach from=$names item=name}
        {$name->id}-{$name->name|strip_tags}
    {/foreach}
{else}
    <p>Nothing in the list.</p>
{/if}

Here we are. You may now test the list action by going to /hello/list


Display/show just an object

We are going to show a dynamic "hello $name". The name will be read from the database.

We add the show action to the controller:

// in HelloController
public function action_show()
{
    if  (isset($_GET['id']) && is_numeric($_GET['id']))
    {
        $name = Hello::getByPKey('Hello',$_GET['id']);
        $view = new HelloView();
        return $view->html_show($name);
    }
    else
        HTTP::redirect('/');

}

We add the view:

// in HelloView
protected function _show($name)
{
    $this->assign('name', $name);
}

And finally, the template (in views/html/show.tpl):

<p>Hello {$name->name} !</p>

Test it by going to http://your_vhost/hello/show/1. It should display "Hello Test !"

Addition

We are going to add a new name to the database. First, add the add method to the controller :

// in HelloController
public function action_add()
{
    if (isset($_POST['name']))
    {
        // we add a new name
        $name = new Hello();
        $name->name = $_POST['name'];  // the model will take care of escaping strings 
        $name->save();
        HTTP::redirect('/hello/show/'.$name->id);  // we go to the show action for this user after it is registered 
    }
    else
    {
        // we just show the addition form
        $view = new HelloView();
        $view->html_add();
    }
}

We may skip the definition of the HelloView _add() as the view method is optional but we still have to define the form template:

// in modules/hello/views/html/add.tpl
<form action="" method="post">
    <label for="name">Name: </label><input type="text" name="name" />
    <input type="submit" value="Enregister" />
</form>

Test the add method by going to http://your_vhost/hello/add/ Submit a value. A new object will be created, serialized to the database and the page should be redirected to the its show action (/hello/show/<OBJECT_ID>).

Editing/updating an object

We are going to modify the add() method to handle updates as well. It will receive the ID of the object in the URL, like this : /hello/add/2.

// in HelloController
public function action_add()
{
    if (isset($_GET['id']) && is_numeric($_GET['id']))
        $name = Hello::getByPKey('Hello',$_GET['id']);
    else
        $name = new Hello();

    if (isset($_POST['name']))
    {
        $name->name = $_POST['name'];
        $name->save();
        HTTP::redirect('/hello/show/'.$name->id);
    }
    else
    {
        $view = new HelloView();
        $view->assign('name', $name);
        $view->html_add($name);
    }
}

We add the editing links to the form:

// in views/html/show.tpl
{if $names|@count neq 0} 
    {foreach from=$names item=name}
        {$name->id}-{$name->name|strip_tags} <a href='/hello/add/{$name->id}/'>Edit</a>
    {/foreach}
{else}
    <p>Aucun élément dans la liste</p>
{/if}

Test this by going to /hello/list, then click on one of the Edit links. This should display the same thing as add, but the form should be pre-filled with the existing name.

In the next chapter you will see how to validate data and how to handle simple object relations like one-to-one.