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!

Bonfire has been around for almost 4 years now! That's kind of hard to believe, and harder to believe still that, after the ups and downs with the project, and with CodeIgniter in general, we're still getting new people finding the project and getting excited about it. You know the real reason that the project is still alive and kicking, though? You.

Over the years I've heard from a number of people whom the project helps everyday. Most of them are web developers who enjoy the time savings and power. Occasionally, though, I get an awesome email from someone who dabbles in coding and... well, let Ross tell you in his own words.

Hi lonnieezell,

I thought i'd send you a message, just to thank you for Bonfire.

I'll give you a little background. I'm a 35yr old Electrician by trade. And have always dabbled with PHP since the days of 3, for the clan, for a mate, sometimes even for a bit of cash.

3, maybe 4 years ago, I found CI, I looked at other frameworks, and I just could not grasp them. CI, well just, was fluid, easy for a dumb sparks like me (that is electrician). My better half, which is a hairdresser. (Typical electrician getting it on with a hairdresser you may say ) Decided she wanted to open up her own salon, she did, I built the site using CI (http://sasshair.co.uk) if you want to criticize.

However, since finding BF, I am now completely overhauling it, moreso the back-end, its not finished yet, but it won't be long.

I Have built, a full blog, gallery, products section, appointment calendar, & client records section, & a few other bits & bobs. All of which would probably take yourself a couple of weeks to code, if that!

Anyway. Thought I'd just say thanks for BF, as it has saved me loads of time & hassle. Keep up the good work.

All the best

Ross

Keep the messages coming, guys. I love hearing how the project has helped you. Those messages are a big part of what keeps the project going.

Have a wonderful Holiday season and we'll be back cracking next year!

I started coding up Sprint on September 11, 2014, at least that's the date of the first commit. Here we are, just barely over 3 months later, and I thrilled to announce that the project has reached ALPHA state!

I know, it's kinda strange to be excited about an Alpha release, but there it is, still. It's been a lot of work to get here but I'm mostly thrilled with the systems that are in place and feel it's a huge boost in productivity for anyone that might use it.

What's New?

Since the last time that I mentioned Sprint I've managed to get everything on this short list at the bottom in place, and then some. Here's the big updates.

Auth

A new Authorization system is in place that allows for very flexible group/permissions and allows users to be in more than one group (finally!). We don't call them roles, since the groups are flexible enough you could create one for a group of beta testers, or a secret place on your blog to organize your kids birthday party. And still manage the admins, moderators, editors, etc.

To make it easy to protect your controllers, I've introduced the AuthTrait that provides convenient methods to restrict current user's access, and can be attached to any controller or library you might need it in.

System Events

An Events system is available that allows you to easily tap into existing events that Sprint publishes, or publish your own. All without modifying any core files.

Cron System

A cron system is in place that allows you to easily schedule actions to happen, whether those actions are in a library or some other fully-namespaced file. You still need to setup a cronjob on your server to hit your application, but then this will take care of the rest. It even has a CLI-based interface for when you're app is acting up and you need to ssh into the server and suspend one or more cronjobs until you get things fixed up.

The Forge

Speaking of the command-line, an all new code generator, the forge is now ready. It's CLI-only in Sprint, but very flexible and simple to use. Takes full advantage of the upgraded CLI tools to ask you for details if you don't remember the right parameters, and will even build out migrations and models from existing database tables. Designed to encourage you to create your own custom collections with generators specific to your workflow or company standards.

Flexible Email System

I couldn't leave well enough alone, and added an email system and queue to project. It works great with the system events, is fully themeable (and ships with a generic theme based on Zurb Ink, and mega-flexible.

UIKits

A good beginning on UIKits is in place that allows you to create views that will work no matter what front-end CSS framework you're using. This is best used inside of Forge generators or views in packages that you will reuse. Currently ships with UIKits for Bootstrap 3 and Foundation 5. Since I'm looking into use it for Bonfire Next, there's a strong possibility that support for Semantic-UI will show up in the near future.

What's Next?

So what does Alpha mean, anyway? It means that all of the features that I intend on having in the first release are in place, but they may have bugs, documentation errors, etc. Before the Beta release I will be adding additional tests to the system, additional docs, and trying to verify that docs are correct, and possibly streamlining some code or implementing some todos in the code I missed. If you use the project and find changes to the code or docs, pull requests are accepted :) Preferably with tests in place, though I won't be enforcing that at this point.

Oh, yeah, and creating a site for it. Know any designers that want to work for free? :)

Get Started Today

Download it and start exploring. I'm sure you'll find plenty of power for your next project, but if you have any ideas, suggestions or issues, please report them.

P.S. There's Docs Oh - I almost forgot. Once you download it, just visit /docs on your site to view the current docs. They're always included with your downloads.

First off, for those of you that haven't heard, work has begun on the next major version of Bonfire, called Bonfire Next. It's a complete, ground-up rewrite that thinks about things quite differently than the previous version. With any luck, though, it will be even easier to use than the current version, and yet much more powerful.

Why Next?

I had gotten where I didn't even use Bonfire as a starting point for the last few projects I did. There were a few little quirks that annoyed me too much - the way Contexts worked being one of the big ones. Plus, I wanted it to be more integrated with Composer and the rest of the PHP world that's going on right now. Instead of writing CodeIgniter applications, I wanted it to be more likely writing applications that just happened to use CodeIgniter as the primary framework. I say primary, because with all of the high-quality code available and easy to install and update through Composer, we're kicking ourselves in the foot by limiting ourselves.

Where Are We Now?

Honestly, we're really just getting started, but here's a quick overview of what's in place, though it all needs more refinement and completion.

CodeIgniter 3 Out of the Box

This is all based on constantly updated version of CodeIgniter 3, which is nearing it's release date, if the issues over at GitHub are any indication. Still with HMVC Modular Extensions, of course. Though it's having to be tweaked slightly to work with CI 3, it seems. No worries - we'll get there!

For those wishing there was a nightly version of the docs: yeah, so was I. So I made one. Head on over to our hosted nightlies of CodeIgniter 3 Documentation.

Template-Engine Interface

I know that everyone has different opinions about what makes a great template engine, so I knew I wanted people to be able to easily integrate their own engines for the frontend. A very simple interface was created and the Plates Template Engine has been integrated. It's working great. It's should be easy enough to create a simple class that implementst the TemplateInterface and you could integrate Dwoo, Twig, or your very own best-of-breed, lighter-than-air template engine.

I'm not going to promise that Plates will stay as the default engine. I have some ideas that may require me to do something different here. More about that below.

Asset Pipeline

After trying out a few of the other solutions out there, I was unable to find anything quite like I wanted. So I built a simple one. This is very similar in nature to Rails' Asset Pipeline though not identical. Basically, it allows you to store your files in any path and then be able to serve them up dynamically or build them out to a static file for faster processing. You can specify exactly which files to combine together into a single file for better front-end performance, and exactly how that type of file should be processed. Should we compress the combined CSS files? Minify it? Fix the URLs? Combine the images into sprites? Resize images on the fly? You get the idea. It's all about front-end performance, here, and usability for the backend dev. Any modules can have their own assets that you can combine with the rest without moving things around.

This is working, but more filters need to be written out and another advanced feature or two put in place.

Updated Docs System

The docs system is getting huge overhaul in both appearance and power. I'm thrilled with how it's going so far.

Bonfire Next Docs

I think it looks 100% better than the old docs, already, and we've added a few bonuses for even better usage.

  • Auto Document Maps - Document Maps will analyze the Markdown file and pull out any h2 and h3 tags to build a list of links to within this document. You see an example on the right-hand side of the page in the image above.
  • TOC Ordering - the Table of Contents is still built from an ini file, but we will now automatically flow the links into columns, keeping them as even as possible without splitting the groups up.
  • Infinite Groups - Before, you had either application docs or bonfire docs. That's been tweaked to allow you to specify any number of groups to organize it however you want. I'm currently working on allowing the ini files to include tags that will be replaced with the correct path from other locations, like modules, etc, so you have even more control over the documentation you present to your users.

What's Down the Road?

Obviously, the big picture is to get us back to the functionality we currently have, or at least something close. I will warn you that not everything is going to make the cut and be part of the core package. They will, however, be simply to install and use if you need it.

Here's some of my big-picture thoughts, though I make no guarantees about what features make it into it.

  • Site Building - This is definitely my big goal with this project. Many pages in our applications don't need to be generated dynamically all of the time, and that's a waste of resources. I'm picturing the ability to tell it to build your site and it will generate static HTML files for you. Of course, you'll have full control over what's static and what's dynamic. You should be able to tell it to make all of page X static, but keep these couple of sections dynamic, and have it convert those to an AJAX call to update that section. This isn't caching, but truly creating HTML files to be served. You can't get an better performance than that. This is also why I might need to do a custom template engine.
  • Customizable Code Generator - I want to expand the current module builder out into something that's standalone and can be used by anyone, anywhere. It will be integrated into Bonfire as needed, of course, to keep it part of the system. This would allow you to customize your own versions of how things your files are built. Customize your Models to be setup just like you want them everytime. Build out forms and views that use the CSS framework of your choice. All with an elegant GUI. I'm hoping this puts our current stuff to shame.
  • Route Filters - Of course, we owe Laravel for this idea. Awesome feature, though.
  • Assets from Anywhere - The Asset Pipeline already makes use Flysystem for it's file-handling. I would love to implement a way to pull assets from anywhere, like an Amazon S3 account, Dropbox, or even an FTP site. These would be pulled together into the final static file.

If you've got ideas of your own, or thoughts on these, please comment below!

As I announced a little while back, some new module code was being tested and some custom Router and Loader classes put in place that would replace the previous HMVC functionality. That code was just pushed to the develop branch and is now officially part of the project.

This also means that a few miscellaneous changes happen throughout the project, including:

  • Separated all Bonfire code from your Application's code.
  • Most bonfire specific code now uses a BF_ prefix instead of the MY_ prefix your application would use.
  • The module_* methods in the application_helper file have been moved to the Modules class. The application_helper methods are still there but are considered deprecated.

I believe those are the biggest things that you'll notice. If I missed anything, feel free to call me on it.

The next two things on my plate are a revised docs system that's a bit more powerful, slightly different theming, and maybe, just maybe, some basic search capabilities. Also coming next is the new menu system that's been hinted at. I'll write another post in the next day or two to explain why the menu system needs to be redone and why Contexts were not quite as great as I originally imagined them to be.

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.