-
Notifications
You must be signed in to change notification settings - Fork 1
Getting started with bonfire
This guide is designed for developers that are familiar with CodeIgniter, but are just getting started with Bonfire. It does not assume that you have any prior experience with Bonfire.
Bonfire is intended to provide a kickstart for new web applications built in CodeIgniter. If you don't know CodeIgniter you will need to get to speed with that first. There are a number of free tutorials out there, including:
Bonfire is a framework for your web application, built on top of the CodeIgniter PHP Framework. It is not a CMS, but a starting point for new projects that require ready-made tools like:
- Robust Role-Based-Access-Control
- Fully Modular Codebase. Built around HMVC.
- Database backup, migration, and maintenance.
- Powerful, parent/child capable theme engine.
- Simple Email queue to keep your ISP happy.
- and more...
Bonfire ships with a number of individual components that are described below. Don't get hung up on the details of of each component for now. Many of these will be described in more detail below.
MY_Model provides a robust set of standard methods for you to derive your models from. Methods covering all standard CRUD routines, as well as simple methods that chain together. For simple models, all you have to do is extend the MY_Model class and set a couple of variables and you'll be up and running without any extra code.
MY_Controller provides 4 different controllers that you can use to keep common functions within each 'zone' of your website: Base_Controller, Front_Controller, Authenticated_Controller and Admin_Controller. You can set different defaults in each controller for a different part of your site. For example, setting the admin theme in the Admin_Controller, or making sure the user is logged in with the Authenticated_Controller.
Bonfire's User module provides a flexible User_model, ready for your users to login with, as well as a flexible RBAC that is simple to use and flexible enough to fit most needs.
Quickly browse your database, perform backups, restore old bakups, and keep your database versioned with Migrations. Unlike CodeIgniter's built-in migrations, Bonfire extends them to allow for the core, your app, and each module to maintain their own set of migrations.
Very similar to CodeIgniter's Hooks, System Events allow you to hook into Bonfire's core code without modifying core files. It also provides a simple way for you to add hooks to your own code for other modules to use.
This library provides a simple way to log user activities, such as 'JohnDoe deleted the Page titled "Page 1"'. This makes it simple to keep a clear, consistent log of every important action of every user.
Easily store application-wide settings in the database, allowing your users to change settings simply and easily.
The best way to use this guide is to follow along, step-by-step, entering the code at each step. Everything you need to make this project work is included in this tutorial.
During this tutorial we will create a (simple) blog module that will have you mastering your way around Bonfire in no time.
Installation is a simple process mainly composed of uploading your files and running through a quick 2-step install.
Upload all of the files/folders from your package to your web server or development environment. The web root should point to the main folder that contains the following files and folders:
File/Folder | Purpose |
---|---|
assets/ | Holds your projects assets, like css, javascript and images. |
bonfire/ | Holds all of your application files, CodeIgniter, Modules and Themes. |
docs/ | Stores the latest built version of the documentation. |
index.php | The front-controller that all traffic should be routed to. |
install/ | Holds Bonfire's installer. |
license.txt | CodeIgniter's License File. |
README.markdown | The Readme file for GitHub with basic project information. |
tests/ | Holds all of your unit testing files, plus a SimpleTest install. |
unit_test.php | The main UI to your testing. Must not be present on public servers. |
If you do not have mod_rewrite installed on your server, change the Index File to include index.php within your bonfire/application/config/config.php file.
$config['index_page'] = 'index.php';
If you installed bonfire to a subdirectory and are using mod_rewrite you will need to edit two .htaccess files and set the value of RewriteBase to match your subdirectory.
For example if you installed bonfire to /bonfire/ you will need to carry out the following:
Edit /bonfire/.htaccess and change the line
RewriteBase /
to become
RewriteBase /bonfire/
Edit /bonfire/install/.htaccess and after the following line:
RewriteEngine on
Add the following:
RewriteBase /bonfire/install/
To start the installation, navigate to http://yoursite.com/install. At this point, navigating to any page on the site will redirect you to the install script.
You should be greeted by a Welcome Screen that asks you for your database connection info. Enter these and click the 'Test DB' button.
If all goes well, it should direct you to Step 2 (the final step), where you just need to enter your:
- Site Title - The name of your site, as it will appear in the browser’s title bar or tab.
- Username - Choose a username to log into the site with (if the site is setup that way).
- Password - Type a password and confirm it.
- Your Email - This will be the address that you use to log in with, as well as the address used to send system emails from. This can be changed later to be able to use separate addresses for login and system emails.
By default, Bonfire is setup to use emails to login with, and not use usernames at all. This can be easily configured on the main settings screen.
Assuming that everything proceeds without a hitch, you will be redirected to the login screen. Enter the email and password you just used, and you will be sent to the admin dashboard where you can start building your app.
Once you log into the admin portion of Bonfire, you will find a menu with 4 categories across the top: Content, Reports, Settings, and Developer. These four categories are what Bonfire calls Contexts.
A Context is a way to group related content from different modules. It all happens automatically based upon the names of the controller and a little behind-the-scenes magic in the routes file. You don't need to understand all of the details about contexts just yet, but we will touch on how to use them in this tutorial.
You can create as many custom contexts as you want, and even remove the Content and Reports contexts, to meet the needs of your app. The Settings and Developer contexts are a central part of how Bonfire works and cannot be removed.
Contexts don't have to be visible to everyone who uses your admin area, though. They can be hidden individually per user role.
Bonfire is primarily a collection of modules that handle all of the various parts. This makes it easy to create your own modules that can be reused and passed around with a minimum of work.
If you navigate the project, look in the main bonfire folder. You will find the following folder structure:
Folder | Purpose |
---|---|
application | Holds Bonfire's primary files and the core modules. |
codeigniter | Holds the CodeIgniter system files. |
modules | Holds your own custom modules. |
themes | Holds all of your themes. |
A module is a mini-application that can contain assets (like CSS or JS), config files, controllers, models, libraries, helpers, and views. This is all powered by Modular Extensions HMVC and allows for HMVC usage (which we will touch on later in the tutorial).
The first step in creating our blog module is to get the folder structure created and make sure the settings are correct for it to show up.
Within the bonfire/modules folder, create a new folder named blog, and create the following subfolders: assets, config, controllers, migrations, models, views. When you're done your module should look like:
bonfire/
modules/
blog/
assets/
config/
controllers/
migrations/
models/
views/
To make sure that your blog shows up in the admin area, we need to create a config file that holds various settings that let Bonfire know about your module. The config file is not required, but allows more control over how your module appears throughout the system.
Create a new file in your new config folder, called config.php.
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
$config['module_config'] = array(
'name' => 'Blog',
'description' => 'A Simple Blog Example',
'author' => 'Your Name',
'homepage' => 'http://...',
'version' => '1.0.1',
'menu' => array(
'context' => 'path/to/view'
),
'weights' => array(
'context' => 0
)
);
Not all of these settings will be used for the Blog module, but you should understand how they work.
Name is the human-readable name of your module.
Description is a short description of your module and will appear in a list of installed modules.
Author is your name or the name of your organization/team.
Homepage is the URL to the homepage of your module. Will appear as a link in the installed modules page.
Version is a simple version string.
Menu is an array that lets you specify sub-menus that will appear under each context. It points to a view file within your module that contains a <ul> of your options.
Weights is an array that allows you to order the menu items within the main context navigation. Think of it like the menu is a bowl of water. The heavier the weight (or the larger the value), the farther down the menu it will sink.
In order to manage your blog, we will create a new entry under the Content menu that takes us to all of the blog management features. To get started we will need one controller and one view.
Create a new controller, blog/controllers/content.php. Each context uses a controller of the same name. In this case we want to create some actions for the Content Context, so we create a controller named content.php.
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Content extends Admin_Controller {
public function __construct()
{
parent::__construct();
Template::set('toolbar_title', 'Manage Your Blog');
}
//--------------------------------------------------------------------
public function index()
{
Template::render();
}
//--------------------------------------------------------------------
}
Notice that the class is named the same as the Context, also, and it extends from Admin_Controller. The Admin_Controller is one of several controllers provided by Bonfire to take care of a few functions for you. In this case, the controller makes sure that we are logged in, sets up pagination defaults, sets the theme to the admin theme, and gets our form_validation library loaded and setup so that it works properly with HMVC.
In the __construct() method we are doing one thing currently, and that is to set the text that shows up in the sub-navigation bar just below the main menu in the admin area. We'll make more use of this bar in the future.
The index() method is the method that will be called by default when you click on the Blog menu item. We use the Template library's render() method to display the view for this page. By default, this will look for a view in the module's views folder under the context name and method name. In this case, it would be searching for blog/views/content/index.php. Create that file now.
<h1>Blog Index</h1>
We are keeping it simple just to make sure everything is in working order for now.
Save the files, then navigate to http://yoursite.com/admin/content/blog to view your new page.
To add a link to the blog in the Content dropdown menu, you first need to add the permissions to the administrator role. The quick guide below will help, but please make sure you have a good read through the Roles and permissions documentation to cement your understanding.
- Navigate to your admin area => Settings => Permissions.
- Click Create in the sub navigation bar.
- Enter Blog.Content.View in the name field.
- Enter Allow users to view the Blog Content Context in the Description field.
- Click the Save Permission Button.
- Navigate to your admin area => Settings menu => Roles.
- Click Administrator in the table.
- Locate Blog under the Permissions header.
- Check the View box inline with Content.
You should now be able to see the blog link in the content's dropdown menu.
In order to view any posts, we will need to create a model that interacts with the database for us. Thanks to Bonfire's MY_Model base class, this is a very simple task.
Create a new model file at blog/models/post_model.php.
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Post_model extends MY_Model {
protected $table = 'posts';
protected $key = 'post_id';
protected $set_created = true;
protected $set_modified = true;
protected $soft_deletes = true;
protected $date_format = 'datetime';
//---------------------------------------------------------------
}
This is everything needed to get some pretty flexible CRUD setup and running for your post model.
$table is the name of the database table that the data is stored in.
$key is the name of the primary key the table uses.
$set_created tells the system whether it should automatically store the date the object was created. In order for this to work, you must have a created_on datetime field in your table.
$set_modified tells the system whether it should automatically store the date the object was last modified. In order for this to work, you must have a modified_on datetime field in your table.
$soft_deletes tells the system whether a delete function should permanently delete the row (a 'hard' delete), or simply set a deleted flag in the table. This requires that you have a deleted tinyint(1) field in your table.
With the settings above, our post_model will:
- Store our data in the posts table in our database.
- Each row will have a primary key called post_id.
- Store the date the row was created in the created_on field.
- Store the date the row was last modified in the modified_on field.
- Set the deleted field to 1 when a row is deleted, instead of permanently removing it.
Since the post_model will be used in nearly every method in our content controller, we will autoload it in the controller's __construct() method.
public function __construct()
{
parent::__construct();
$this->load->model('post_model');
Template::set('toolbar_title', 'Manage Your Blog');
}
To allow for simple versioning of your database we will use Bonfire's migrations. This is great for working in teams where modifications to the database might be made by several members on the development team. It also makes it fairly simple to bring your changes to the production database. In the case of a module like our blog module, it allows us to easily re-use the module in another application and quickly get the database setup.
Migrations are simply a set of commands that are run to make changes to the database or remove changes. They are stored in the module's migrations folder, using sequentially numbered files.
Create a new file at blog/migrations/001_Initial_tables.php.
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Migration_Initial_tables extends Migration {
public function up()
{
$this->load->dbforge();
$this->dbforge->add_field('post_id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT');
$this->dbforge->add_field('title VARCHAR(255) NOT NULL');
$this->dbforge->add_field('slug VARCHAR(255) NOT NULL');
$this->dbforge->add_field('body TEXT NULL');
$this->dbforge->add_field('created_on DATETIME NOT NULL');
$this->dbforge->add_field('modified_on DATETIME NULL');
$this->dbforge->add_field('deleted TINYINT(1) NOT NULL DEFAULT 0');
$this->dbforge->add_key('post_id', TRUE);
$this->dbforge->create_table('posts');
}
//--------------------------------------------------------------------
public function down()
{
$this->load->dbforge();
$this->dbforge->drop_table('posts');
}
//--------------------------------------------------------------------
}
The up() method is ran whenever this migration is 'installed'. The down() method is ran whenever the migration is 'un-installed'.
We use CodeIgniter's dbforge class here to create the table, but you could use the database class to run raw queries, if you wanted to.
To install this migration, navigate to Developer / Database / Migrations. Click on the Modules tab. You will see your Blog module shows up there. Select the 001_Initial_tables.php from the dropdown, and click 'Migrate Module'. Your up() method will be ran, installing the posts table into your database.
It's now time to expand our blog's content index method to show us a list of all posts in the system. Add the following lines to the index() method of the content controller, before the Template::render() method.
$posts = $this->post_model->where('deleted', 0)->find_all();
Template::set('posts', $posts);
This calls our post_model's find_all() method to retrieve all posts in the system. However, we don't want any deleted posts, so we filter those out with the where() method.
We then use the Template class' set() method to make the data available to our view. In the view, this can be accessed as the $posts variable.
Edit your index.php view file to reflect the following:
<div class="admin-box">
<h3>Blog Posts</h3>
<?php echo form_open(); ?>
<table class="table table-striped">
<thead>
<tr>
<th class="column-check"><input class="check-all" type="checkbox" /></th>
<th>Title</th>
<th style="width: 10em">Date</th>
</tr>
</thead>
<tfoot>
<tr>
<td colspan="3">
With selected:
<input type="submit" name="submit" class="btn" value="Delete">
</td>
</tr>
</tfoot>
<tbody>
<?php if (isset($posts) && is_array($posts)) :?>
<?php foreach ($posts as $post) : ?>
<tr>
<td><input type="checkbox" name="checked[]" value="<?php echo $post->post_id ?>" /></td>
<td>
<a href="<?php echo site_url(SITE_AREA .'/content/blog/edit_post/'. $post->post_id) ?>">
<?php e($post->title); ?>
</a>
</td>
<td>
<?php echo date('M j, Y g:ia'); ?>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="3">
<br/>
<div class="alert alert-warning">
No Posts found.
</div>
</td>
</tr>
<?php endif; ?>
</tbody>
</table>
<?php echo form_close(); ?>
</div>
This creates a table that will list each blog post, if any exist. If they don't then a notice will be displayed.
Most of this should be self-explanatory, but there is one new function nestled in there, e(). This method is a convenience method that you should consider using wherever you are displaying user-entered data. It simply echos out the string, using the htmlspecialchars() function to help protect against XSS attacks.
Now we just need a way to create new posts. Let's start by creating a new sub-menu that allows us to access other pages. This is not intended for long menus, but to provide a short list of major areas within your module. You will see this used throughout Bonfire, and it appears on the right side of the page, just under the main menu. This is the same bar that holds your $toolbar_title.
First, we create a view file holds the menu itself. Create a new file at blog/views/content/sub_nav.php.
<ul class="nav nav-pills">
<li <?php echo $this->uri->segment(4) == '' ? 'class="active"' : '' ?>>
<a href="<?php echo site_url(SITE_AREA .'/content/blog') ?>">Posts</a>
</li>
<li <?php echo $this->uri->segment(4) == 'create' ? 'class="active"' : '' ?>>
<a href="<?php echo site_url(SITE_AREA .'/content/blog/create') ?>">New Post</a>
</li>
</ul>
Each link in this list will take us to a method within our content controller. We also check the url to see if this is an active link or not.
To make this menu show up, we need to add it to our content controller's __construct() method.
public function __construct()
{
parent::__construct();
$this->load->model('post_model');
Template::set('toolbar_title', 'Manage Your Blog');
Template::set_block('sub_nav', 'content/sub_nav');
}
Reload your page in the admin area, and you will see the new menu appear. Clicking on 'New Post' throws an error since we haven't created that method yet. We will do that next.
We will start things simple by just displaying the form to create a new post, then deal with saving the post later.
Create a new create() method in your content controller.
public function create()
{
Template::set('toolbar_title', 'Create New Post');
Template::set_view('content/post_form');
Template::render();
}
This sets the toolbar_title of the page, says that we want to use the view named views/content/post_form.php, and renders the form.
Now we need create the form itself. We're using a file called post_form because we want to be able to use the form for both the create and edit pages.
<div class="admin-box">
<h3>New Post</h3>
<?php echo form_open(current_url(), 'class="form-horizontal"'); ?>
<div class="control-group <?php if (form_error('title')) echo 'error'; ?>">
<label for="title">Title</label>
<div class="controls">
<input type="text" name="title" class="input-xxlarge" value="<?php echo set_value('title', isset($post) ? $post->title : ''); ?>" />
<?php if (form_error('title')) echo '<span class="help-inline">'. form_error('title') .'</span>'; ?>
</div>
</div>
<div class="control-group <?php if (form_error('slug')) echo 'error'; ?>">
<label for="slug">Slug</label>
<div class="controls">
<div class="input-prepend">
<span class="add-on"><?php echo site_url() .'blog/' ?></span>
<input type="text" name="slug" class="input-xlarge" value="<?php echo set_value('slug', isset($post) ? $post->slug : ''); ?>" />
</div>
<?php if (form_error('slug')) echo '<span class="help-inline">'. form_error('slug') .'</span>'; ?>
<p class="help-block">The unique URL that this post can be viewed at.</p>
</div>
</div>
<div class="control-group <?php if (form_error('body')) echo 'error'; ?>">
<label for="body">Content</label>
<div class="controls">
<?php if (form_error('body')) echo '<span class="help-inline">'. form_error('body') .'</span>'; ?>
<textarea name="body" class="input-xxlarge" rows="15"><?php echo set_value('body', isset($post) ? $post->body : '') ?></textarea>
</div>
</div>
<div class="form-actions">
<input type="submit" name="submit" class="btn btn-primary" value="Save Post" />
or <a href="<?php echo site_url(SITE_AREA .'/content/blog') ?>">Cancel</a>
</div>
<?php echo form_close(); ?>
</div>
Most of this is straight-forward. I do want to point out that for the values, we are checking whether a $post value is set or not, and then uses the form helper's set_value() method to set the value in the case of errors on the form.
We also use the form_validation library's form_error() function to setup individual errors for each field.
Saving the Post
Now, let's actually make it functional. In your content controller, add the following method:
private function save_post($type='insert', $id=null)
{
$this->form_validation->set_rules('title', 'Title', 'required|trim');
$this->form_validation->set_rules('slug', 'Slug', 'trim');
$this->form_validation->set_rules('body', 'Body', 'required|trim|strip_tags');
if ($this->form_validation->run() === false)
{
return false;
}
// Compile our post data to make sure nothing
// else gets through.
$data = array(
'title' => $this->input->post('title'),
'slug' => $this->input->post('slug'),
'body' => $this->input->post('body')
);
if ($type == 'insert')
{
$return = $this->post_model->insert($data);
}
else // Update
{
$return = $this->post_model->update($id, $data);
}
return $return;
}
//--------------------------------------------------------------------
And then modify the create() method to look like:
public function create()
{
if ($this->input->post('submit'))
{
if ($this->save_post())
{
Template::set_message('You post was successfully saved.', 'success');
redirect(SITE_AREA .'/content/blog');
}
}
Template::set('toolbar_title', 'Create New Post');
Template::set_view('content/post_form');
Template::render();
}
What we've done here is to create a single method that can handle both creating and updating of a single post, save_post(). This method runs any form validation on the inputs and then saves the post. In the create() method we check to see if the form was submitted and tries saving it, if so. On a successful save, we're redirected back to the main posts listing.
Editing our posts is very simple to do now. Simply add the following edit_post() method to your controller and you're up and running:
public function edit_post($id=null)
{
if ($this->input->post('submit'))
{
if ($this->save_post('update', $id))
{
Template::set_message('You post was successfully saved.', 'success');
redirect(SITE_AREA .'/content/blog');
}
}
Template::set('post', $this->post_model->find($id));
Template::set('toolbar_title', 'Edit Post');
Template::set_view('content/post_form');
Template::render();
}
Now that we have basic administration pages in place, it's time to actually let the users view your awesome blog posts. This requires that we create a new controller, in the same blog module, called blog. This will handle what we call your Public Context and is simply a front-facing controller that will directly map to the URI. In this case, you can view this controller at http://yoursite.com/blog.
Create a new file, modules/blog/controllers/blog.php
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Blog extends Front_Controller {
public function __construct()
{
parent::__construct();
$this->load->model('post_model');
}
//--------------------------------------------------------------------
public function index()
{
$this->load->helper('typography');
Template::set('posts', $this->post_model->order_by('created_on', 'asc')->limit(5)->find_all());
Template::render();
}
//--------------------------------------------------------------------
}
This is just a typical CodeIgniter controller, which means that anything you can do in straight CodeIgniter, you can do here.
In this case, we're loading our model in the constructor, since we know that we'll be using it in every method. Then we create an index method to list the 5 most recent posts.
Then, it looks for a view file at blog/views/index.php. Create that file now.
<?php if (isset($posts) && is_array($posts) && count($posts)) :?>
<?php foreach ($posts as $post) :?>
<div class="post">
<h2><?php e($post->title) ?></h2>
<?php echo auto_typography($post->body) ?>
</div>
<?php endforeach; ?>
<?php else : ?>
<div class="alert alert-info">
No Posts were found.
</div>
<?php endif; ?>
This is a very simple view, but should be enough to give you an understanding.
Navigate to http://yoursite.com/blog and you should see the 5 most recent blog posts for you.
If this were a real application, there would be much left to add, but this is enough to get you started. At this point, you should have enough understanding of how to create modules and code for Bonfire to get you well on your way to creating the next killer app.
Getting Started With Bonfire
Installing Bonfire
Change Log
Bonfire Models
Bonfire Migrations
Bonfire Controllers
Contexts
Layouts and Views
Working With Assets
Changing Admin URL
Global Helpers
Keyboard Shortcuts
Module Builder
System Events
Performance Tips
How to Contribute
API Documentation Guidelines
Bonfire Guides Guidelines
Bonfire Bug Report Guide