The New Myth family: SprintPHP Bonfire Practical CodeIgniter 3

New Myth Media Blog

Serving the New Myth Media Family.

Practical CodeIgniter 3 Released

My new book about making the most of CodeIgniter 3 is out in Early Access!

Posts Filed Under “Tutorials”

Over the last week I've released two additional videos covering the Forge that should help you get up to speed on how to use it to dramatically increase your productivity.

Please, remember, the Forge generators simply generate code that functions as a starting point. They are not magical CRUD, so you can still customize the output (and should!) to meet your application's specific needs, but you'll shave hours of development time off of the typical application by using these.

Sprint got it's first video today.

In 7 short minutes I walk you through installing Sprint using Composer, provide a brief overview of all of the folders in the default install, and get your database setup and ready to go.

The is only the first in a number of planned videos intended to help you understand the concepts behind Sprint, not step you through copy-paste tutorials that only teach you steps. I'm a firm believer in understanding the concepts behind what you're trying to learn. From there, everything gets easier and it just becomes implementation details.

This is part 2 of a small tutorial that copies along the TutsPlus Ribbit project in CodeIgniter and Bonfire. Last time we managed to get the front of the site in place and styled, all using a single home controller.

The Database

I like to start projects by getting the data layer in place, and Bonfire makes this step very easy.

We will need two additional tables for this application:

  • ribbits - contains the actual ribbits, or posts.
  • follows - a list of who follows who

To make sure the data is portable between or development setup and our production server, we'll make use of a Migration. Create a new file at application/db/migrations/001_Initial_tables.php. This will hold all of the code to setup our initial tables and modify the users table. The file should look like this:

    class Migration_Initial_tables extends Migration {

    public function up()
    {
        $sql = array();

        // Ribbits
        $sql[] = "CREATE TABLE ribbits (
                id            INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
                user_id    INT(11) UNSIGNED NOT NULL,
                ribbit        VARCHAR(140),
                created_at    DATETIME,
                PRIMARY KEY(id, user_id)
            );";

        // Follows
        $sql[] = "CREATE Table follows (
                id            INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
                user_id       INT(11) UNSIGNED NOT NULL,
                followee_id   INT(11) UNSIGNED NOT NULL,
                PRIMARY KEY(id, user_id)
            );";

        foreach ($sql as $s)
        {
            $this->db->query($s);
        }
    }

    //--------------------------------------------------------------------

    public function down()
    {
        $this->dbforge->drop_table('ribbits');
        $this->dbforge->drop_table('follows');
    }

    //--------------------------------------------------------------------

}

The migration's up() method is ran to create the database changes. In this case, we're creating the ribbits and follows tables. The down() method reverses those changes by dropping the tables.

For this tutorial, we're assuming that the app is using MySQL and so we'll stick with MySQL-specific queries. We could have just as easily used dbforge for this, like we did in the down() method.

To apply this migration we could log into the admin area and run them manually. For now, though, let's have them run automatically. Open the file application/config/application.php and scroll down to the bottom of the file. Edit the migrate.auto_app and set it to TRUE so it will check for new migrations on every page load.

    $config['migrate.auto_app']     = TRUE;

Then reload any of the pages and the migration will be ran, creating the new tables in your database.

The Models

To quickly get up to speed we'll create 2 new models that extend Bonfire's MY_Model class. This provides all of the CRUD functionality, and more, so we just need to create skeleton files and tweak a few settings.

Ribbits

First, create the new ribbit model at application/models/ribbit_model.php. We will use the bare minimum code here to keep things clear, but the Models has quite a few options you can put to use in your own applications.

    <?php if (!defined('BASEPATH')) exit('No direct script access allowed');

    class Ribbit_model extends MY_Model {

        protected $table_name   = 'ribbits';
        protected $date_format  = 'datetime';

        protected $log_user             = true;
        protected $set_created          = true;
        protected $created_on_field     = 'created_at';
        protected $created_by_field     = 'user_id';

        //--------------------------------------------------------------------
    }

This creates the Ribbit_model class that extends MY_Model to take advantage of our existing functionality. We tell the model which table to use, set our date_format to datetime to match the table structure for created_at. We also tell it which fields to automatically populate with the creation date and who created it (user_id). During any insert() calls, the model will automatically fill in the created_at date and record the current user id in the user_id field for us.

Follows

The follow_model is pretty much the same, so create a new file, follow_model.php and edit it to reflect the following:

    <?php if (!defined('BASEPATH')) exit('No direct script access allowed');

    class Follow_model extends MY_Model {

        protected $table_name   = 'follows';

        //--------------------------------------------------------------------
    }

This time, the model is much simpler, since we don't need to record any of the creation info. All we need is the table name.

The Home Page

Now that we have the data layer in place, it's time to move back to our controller layer and start hooking the pieces together.

User Registration

To kick things off, let's make sure that new users can signup. This functionality already exists in Bonfire, but doesn't match the styling and positioning that our app requires. And it probably includes too much information, to be honest, with user_meta and all, so let's replace the signup for we did last time with this new one below. This is in the application/views/home/index.php file.

    <?= Template::message(); ?>

    <img src="/assets/images/frog.jpg">
    <div class="panel right">
    <h1>New to Ribbit?</h1>
    <?= form_open(REGISTER_URL); ?>
        <input type="hidden" name="redirect_url" value="/">

        <input name="email" type="text" placeholder="Email" value="<?= set_value('email') ?>">
        <input name="username" type="text" placeholder="Username" value="<?= set_value('username') ?>">
        <input name="password" type="text" placeholder="Password">
        <input name="pass_confirm" type="password" placeholder="Password (again)">
        <input type="submit" name="register" value="Create Account">
    <?= form_close(); ?>
    </div>

While I prefer my HTML to look like HTML, I always use the form_open and form_close tags since they help provide CSRF protection in the form submission process. So modify the home controller's index method to load the form helper.

    public function index()
    {
        $this->load->helper('form');
        
        Template::render();
    }

To use Bonfire's existing code, we send users to the users module upon form submittal. Unfortunately, that will take us back to users/register upon failure, so we will have to make some small modifications to the Users module. For real projects, you should copy the entire users module and paste it into application/modules before making changes. For this example, though, we'll simply modify the existing code.

Open up bonfire/modules/users/controllers/users.php. Scroll down to the register method, and let's get started.

The first thing we want to do is to make sure that we can tell it where we want to redirect to. We're including this in a hidden $_POST var, so add the following line just after the opening braces:

    $redirect_url = $this->input->post('redirect_url') ? $this->input->post('redirect_url') : REGISTER_URL;

Scroll down to around lines 583 and 588, and modify the redirect value to use our new value.

    Template::redirect($redirect_url);
    Template::set_message(lang('us_registration_fail'), 'error');
    redirect($redirect_url);

We also need to catch when validation fails, so add the following code after the closing brace around line 591:

    if ($this->input->post('redirect_url'))
    {
    Template::set_message( validation_errors() , 'error');
    redirect($redirect_url);
    }

This sets an error message for us and redirects us back to the home page, since we can't display validation errors like we normally would.

Now, validation will always fail since this method requires a few fields that we don't require. So, to fix things up, remove the required validation rules from language, timezone, and display_name on lines 460-462 since we're not collecting those. We'll leave the lines there, though, in case we change our minds in the future. Also, comment out lines 485-487 where we're composing the $data array to send to the user model.

If we try to submit an empty form, we see one more error we didn't expect: 'The Country field is required'. That is due to a required flag on a user_meta value. Let's remedy that. Open up application/config/user_meta.php and removed the required flag from the 'country' array's rules.

Flash Messages

When there are validation errors, this will show a small 'flash message' using the Template library's message features. We do need to style the message, though, since it's built to use Bootstrap for it's CSS.

Open up application/config/application.php config file again. This time, scroll down to line 175, where it's setting up the template to use for our flash messages. Let's simplify things a bit here.

    $config['template.message_template'] =<<<EOD
     <div class="alert {type}">
        {message}
    </div>
    EOD;

Then, edit our theme's CSS file to make these styles a bit more pleasing. Edit public/themes/ribbit/css/style.less and add the following code at the bottom:

.alert {
    padding: 0.5em;
    border: 1px solid #ccc;
    background: #efefef;
    margin-bottom: 1em;
    line-height: 1.5;
}
.alert.error {
    border-color: #ebccd1;
    background-color: #f2dede;
    p {
        color: #b94a48;
        margin: 0;
    }
}
.alert.success {
    border-color: #d6e9c6;
    background-color: #dff0d8;
    p {
        color: #468847;
        margin: 0;
    }
}

Ah. Much better.

Now, we can register a new user and we still have all of the validation types that Bonfire offers at our disposal (though we might have to style some more pages to get the full complement of functionality, like password resets, etc.)

User Login

Now that users can sign up, it's time to allow them to log in and start using the site.

First, we need to start by modifying the simple form in the page's header. Open up public/themes/ribbit/index.php and replace the existing form with the following code:

    <?php if ($this->auth->is_logged_in()) : ?>
         <a href="<?= site_url('logout') ?>">Logout</a>
    <?php else: ?>

      <?= form_open(LOGIN_URL, 'class="login"') ?>
           <input type="hidden" name="redirect_url" value="/">

           <input type="email" name="login" placeholder="Email">
           <input type="password" name="password" placeholder="Password">
           <input type="submit" name="log-me-in" value="Login">
      <?= form_close(); ?>
    <?php endif; ?>

If the user is not logged in, then a login form is shown. If they are, then a simple Sign Out link is shown. This requires that the Auth library be loaded, which it's not currently. So let's add a constructor to our home controller and load it there. We'll also move our form helper loading to here, since all pages will need it for the login menu.

    public function __construct()
    {
    parent::__construct();

    $this->load->library('users/auth');
    $this->load->helper('form');
    }

We need to adjust the CSS a little bit to allow for a submit button. Edit style.less again, adding the following couple of lines:

    form.login input[type="email"],
    form.login input[type="password"] {
    width: 190px;
    }

All on one line again. Whew.

Now that things are looking nicer, we need to make a quick tweak to our users controller again, just to allow us to redirect back to the home page on errors. Add the following quick redirect check at line 108, just after the closing brace for the if statement:

    if ($this->input->post('redirect_url'))
    {
    redirect($this->input->post('redirect_url'));
    }

Once a user is logged in, we don't want them to see the front page again, but the login script is going to send them back there anyway. So, let's modify the home controller's index page to check if the user is logged in. If they are, it should redirect them to the 'buddies' page.

    public function index()
    {
    if ($this->auth->is_logged_in())
    {
        redirect('buddies');
    }

    Template::render();
    }

Until Next Time

There we have it. We've gotten our data layer setup and ready to go, and integrated user registration, login and logout into Bonfire. And it's been pretty painless so far.

Next time, we'll wrap up and flesh out our buddies, ribbits and profiles pages.

Over at NetTuts, they've put together a series of tutorials that show how to build a simple Twitter clone, called Ribbit in a variety of different languages. Using their concept as an example, lets step through how you might build the exact same application with Bonfire acting as the admin area for us to manage users, etal.

This is the first of two or three tutorials covering the process. We'll cover the basics they cover, user login and signup, Ribbitting, and friends, etc. and might even delve into some topics they didn't, like the AJAX solution, etc.

This tutorial assumes that you already have a clean install of Bonfire up and running, with database, and with the index.php removed from the URL.

A repo of the code is being kept at Bonfire's GitHub account if you want to download a save a little time as you follow along. This code will assume it's already been installed, so you will need to edit the database config file and copy over a working table from another stock 0.7 install to use.

You've Got The Look

They did a good job of describing the benefits of LESS in their intro tutorial, so I won't touch on that here. Instead, we're going to focus on taking their code and design, and converting it to work in Bonfire.

Get Your Assets In Line

To get started, download their source files and extract them to some place safe on your hard drive. Move their basic assets over to a new theme inside the public/themes folder. We'll call the theme ribbit for simplicity.

Next, copy the base assets into place:

  • Move the files from the gfx folder to public/assets/images/.
  • Copy the styles.less file into a new folder at public/themes/ribbit/css
  • Copy the less.js file into a new folder at public/themes/ribbit/js

Now, we need to modify the less file to correctly point to the new images location. Open the style.less file in your favorite editor and do a global find/replace for gfx/ to be replaced with /assets/images/.

Base Template

Next, we need to create a new layout file for the theme. Bonfire will automatically use the index.php file as the base, so create that now at public/themes/ribbit/index.php. Copy and paste the contents of their index.html file into this new file.

    <!DOCTYPE HTML>
    <html>
    <head>
        <link rel="stylesheet/less" href="/themes/ribbit/css/style.less">
        <script src="/themes/ribbit/js/less.js"></script>
    </head>
    <body>
        <header>
            <div class="wrapper">
                <img src="/assets/images/logo.png">
                <span>Twitter Clone</span></p>

          </div>
       </header>
       <div id="content">
          <div class="wrapper">

          </div>
       </div>
       <footer>
          <div class="wrapper">
             Ribbit - A Twitter Clone Tutorial<img src="gfx/logo-nettuts.png">
          </div>
       </footer>
    </body>
    </html>

Now, to make sure that all public-facing pages are using the new theme, let's edit the Front_Controller to set the theme for us. The Front_Controller is a foundation controller that is used to put common functionality for the front pages in once place. It can be found at application/core/Front_Controller.php. Be sure to edit the tags for the less.js and styles.less paths so they point to the proper location.

It only has a single method, the constructor. Edit that method and tell the template library to use the new theme, by inserting the following line anywhere after loading the template library itself.

    Template::set_theme('ribbit', 'junk');

The first parameter is the name of the theme to set as the active theme. Since Bonfire supports parent/child theme relationships the second parameter would be the name of the parent theme, or the fallback theme. In this case, we don't want to use one, so we simply set it to something random.

If you try to reload your frontpage now, you'll see the same 'Welcome to Bonfire' page as before. That's because the default home controller does not extend from the Front_Controller. Let's fix that now by replacing the existing home controller with our own.

Erase the contents of the application/controllers/home.php file and replace it with:

    <?php if (!defined('BASEPATH')) exit('No direct script access allowed');

    class Home extends Front_Controller {

        public function index()
        {
            Template::render();
        }
        //--------------------------------------------------------------------
    }

The Template::render() function tells Bonfire to display the contents of a file found at application/views/home/index.php, within the current theme. Refresh your front page again, and you'll see your new theme, with the logo, background, footer, and a broken image in the bottom right corner. Let's fix that real quick so it doesn't just nag us the entire time.

Open up the theme's index.php file again and change the location to point to the proper location.

<img src="/assets/images/logo-nettuts.png">

Aaah. Much better.

Only problem is that our content isn't showing up within the template file itself. Easy enough to fix. Again in the theme's index file, we need to add a single line between the wrapper div open and close tags.

    <?= Template::content(); ?> 

Refresh again, and you'll see your content sitting pretty in it's place. Well, maybe not too pretty, yet, but we'll fix that up next.

The Home Page

The content for the home page sits at application/views/home/index.php Open up that page now and we'll insert their code for the login boxes and registration form. Remember, during this stage, we're just bringing over the looks. None of the functionality. We'll integrate that in the next tutorial.

Insert the following placeholder login form after the in the header, but before the closing <p> tag.

    <form>
        <input type="email">
        <input type="password">
    </form>

Now, on to the registration form and frog image. Erase the contents of the home/index file and insert the following code.

    <img src="/assets/images/frog.jpg">
    <div class="panel right">
        <h1>New to Ribbit?</h1>
        <form>
            <input name="email" type="text">
            <input name="password" type="text">
            <input name="password2" type="password">
            <input type="submit" value="Create Account">
        </form>
    </div>

The Buddies Page

In order for a new page to display, we'll need to create a new method to show it. We have a few options here. We could create a new Buddies controller to hold the page. We could create a new module, which might be useful if we thought we could re-use the code on another project, or simply wanted to take advantage of some of the context capabilities in the admin area. For this simple demonstration, though, we'll keep things simple and collect most of our functionality into the home controller.

So let's make a new method so that we can navigate to the page. This goes in the home controller we were just in.

    public function buddies()
    {
        Template::render();
    }

That will try to display a view file based on the controller name (as the folder) and the method name (as the file), so lets give it one. Create a new file at application/views/home/buddies.php buddies. Add the "Ribbit" box and the User Info area.

    <div id="createRibbit" class="panel right">
        <h1>Create a Ribbit</h1>
        <p>
            <form>
                <textarea name="text" class="ribbitText"></textarea>
                <input type="submit" value="Ribbit!">
            </form>
        </p>
    </div>


    <div id="ribbits" class="panel left">
        <h1>Your Ribbit Profile</h1>
        <div class="ribbitWrapper">
            <img class="avatar" src="/assets/images/user1.png">
            <span class="name">Frogger</span> @username
            <p>
                567 Ribbits<span class="spacing">45 Followers</span><span class="spacing">32 Following</span><br>
                Cras justo odio, dapibus ac facilisis in, egestas Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. <a href="#">http://net.tutsplus.com/tutorials/php/ ...</a>
            </p>
        </div>
    </div>


    <div class="panel left">
        <h1>Your Ribbit Buddies</h1>
        <div class="ribbitWrapper">
            <img class="avatar" src="/assets/images/user2.png">
            <span class="name">Kermit</span> @username <span class="time">15m</span>
            <p>
                Cras justo odio, dapibus ac facilisis in, egestas Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. <a href="#">http://net.tutsplus.com/tutorials/php/ ...</a>
            </p>
        </div>
        <div class="ribbitWrapper">
            <img class="avatar" src="/assets/images/user1.png">
            <span class="name">Frogger</span> @username <span class="time">15m</span>
            <p>
                Cras justo odio, dapibus ac facilisis in, egestas Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. <a href="#">http://net.tutsplus.com/tutorials/php/ ...</a>
            </p>
        </div>
        <div class="ribbitWrapper">
            <img class="avatar" src="/assets/images/user2.png">
            <span class="name">Kermit</span> @username <span class="time">15m</span>
            <p>
                Cras justo odio, dapibus ac facilisis in, egestas Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. <a href="#">http://net.tutsplus.com/tutorials/php/ ...</a>
            </p>
        </div>
        <div class="ribbitWrapper">
            <img class="avatar" src="/assets/images/user3.png">
            <span class="name">Hypnotoad</span> @username <span class="time">15m</span>
            <p>
                Cras justo odio, dapibus ac facilisis in, egestas Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. <a href="#">http://net.tutsplus.com/tutorials/php/ ...</a>
            </p>
        </div>
        <div class="ribbitWrapper">
            <img class="avatar" src="/assets/images/user2.png">
            <span class="name">Kermit</span> @username <span class="time">15m</span>
            <p>
                Cras justo odio, dapibus ac facilisis in, egestas Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. <a href="#">http://net.tutsplus.com/tutorials/php/ ...</a>
            </p>
        </div>
        <div class="ribbitWrapper">
            <img class="avatar" src="/assets/images/user3.png">
            <span class="name">Hypnotoad</span> @username <span class="time">15m</span>
            <p>
                Cras justo odio, dapibus ac facilisis in, egestas Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. <a href="#">http://net.tutsplus.com/tutorials/php/ ...</a>
            </p>
        </div>
    </div>

Navigate to /home/buddies and you should see your new page. We'll fix the URL to something nicer later.

Public Ribbits Page

This page shows the public information and doesn't require a user to be logged in. Again, we need to create a new method to hold the page, so create the ribbits method in our home controller.

public function ribbits()
{
    Template::render();
}

Then create a copy of the buddies.php view file, and name it ribbits.php. Strip out the div with an id of ribbits from the new file and your new page is ready. You can find it at /home/ribbits. Again, we'll clean up the URLs in a bit.

Public Profiles Page

This page presents a list of public profiles that users can search through. One last time, create a new method called profiles in the home controller.

    public function profiles()
    {
        Template::render();
     }

Then a new view at home/profiles.php.

        <div class="panel right">
        <h1>Search for profiles</h1>
        <p>
            <form>
                <input name="query" type="text">
                <input type="submit" value="Ribbit!">
            </form>
        </p>
    </div>



    <div id="ribbits" class="panel left">
        <h1>Public Profiles</h1>
        <div class="ribbitWrapper">
            <img class="avatar" src="/assets/images/user2.png">
            <span class="name">Kermit</span> @username <span class="time">625 followers <a href="#">follow</a></span>
            <p>
                Cras justo odio, dapibus ac facilisis in, egestas Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. <a href="#">http://net.tutsplus.com/tutorials/php/ ...</a>
            </p>
        </div>
        <div class="ribbitWrapper">
            <img class="avatar" src="/assets/images/user1.png">
            <span class="name">Frogger</span> @username <span class="time">329 followers <a href="#">follow</a></span>
            <p>
                Cras justo odio, dapibus ac facilisis in, egestas Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. <a href="#">http://net.tutsplus.com/tutorials/php/ ...</a>
            </p>
        </div>
        <div class="ribbitWrapper">
            <img class="avatar" src="/assets/images/user3.png">
            <span class="name">Hypnotoad</span> @username <span class="time">129 followers <a href="#">follow</a></span>
            <p>
                Cras justo odio, dapibus ac facilisis in, egestas Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. <a href="#">http://net.tutsplus.com/tutorials/php/ ...</a>
            </p>
        </div>
    </div>

Your new page is now ready at /home/profiles.

Cleaning up the URLs

Before we're done for today, lets make our site structure a little nicer by editing the routes. Open up application/config/routes.php file and we will setup a few aliases so that the site is nicer to get around in.

$route['buddies']   = 'home/buddies';
$route['ribbits']   = 'home/ribbits';
$route['profiles']  = 'home/profiles';

There we go. Now we can access the pages without having the home portion showing in the URL.

I should point out here that the URLs can still be accessed at /home/buddies, etc. In this case, that's not a problem, but there might be occassions that you would not want users to access the page at that location. In that case, you would simply provide an empty string on the right side of the statemen and it would block access.

Conclusion

That's a wrap for this part of the tutorial. We have all of the assets in place and the site is showing us the basic pages. Next time, we'll hook in our existing users' system, restrict access to a few pages, setup our models, and more.