Newer posts are loading.
You are at the newest post.
Click here to check if anything new just came in.

December 12 2011

21:30

Build an Admin Panel with the Fuel PHP Framework

In the first part of this series, we took a look at the basics of the FuelPHP framework. In this second-part, we’ll be stepping it up a gear and move onto some more advanced topics! We’ll be creating an admin panel for our application, cover the common uses of the ORM and use the Authentication package to restrict access.

Join me after the break to get started!


Introduction

Since the first article Fuel has been renamed to FuelPHP. Also unlike the first article which was based on v1.0.1 this article requires v1.1.0 so a few little things are different. All the theory you have learnt from the first article is still accurate and not much has changed so this should be easy.


Step 1 - Set Up Oil

If you have not already installed the command-line utility oil and are lucky enough to be running on Linux, Mac, Solaris, etc then do so with the following command:

$ curl get.fuelphp.com/oil | sh

Step 2 - Create a New App

The oil command will help you create new projects with a few key-strokes and alias php oil when inside your FuelPHP applications:

$ oil create Sites/blog
$ cd Sites/blog

This will set up a blog application for us and assuming you have Apache or some other web server running on your “Sites” folder you should be able to load the following URL and see the welcome page.

alt text

Now that FuelPHP is alive and your web server is clearly working, we can start configuring our app.


Step 3 - Configuring Your App

Open up your favourite editor and we’ll start by setting up a database connection and configure the application. This is almost identical to v1.0.x, so create a database and set up your SQL users however you normally do. When it comes to database config there are two differences:

  • PDO is now the default driver
  • FuelPHP v1.1 has environment-based config folders now.

These changes are pretty simple, but you can swap back to using the native MySQL driver if you like. PDO is more useful for developers as it means that your application will work with just about any database engine not just the few that had specific FuelPHP drivers built for them. That means this could just as easily be SQLite or PostgreSQL.

Just open up fuel/app/config/development/db.php and modify your config, where dbname=blog is whatever your database is called and your own username and password for the database server:

return array(
  'default' => array(
 	 'connection'  => array(
 		 'dsn'  	=> 'mysql:host=localhost;dbname=blog',
 		 'username' => 'root',
 		 'password' => 'password',
 	 ),
  ),
);

Next you will just need to open fuel/app/config/config.php and enable the auth and orm packages as suggested in the first post.

/**************************************************************************/
/* Always Load                    */
/**************************************************************************/
'always_load'  => array(

	'packages'  => array(
		'auth',
'orm',
	),

In this config file, we need to make one small change to the whitelisted_classes array which will allow us to pass validation objects to the view:

	'whitelisted_classes' => array(
		'Fuel\Core\Response',
		'Fuel\Core\View',
		'Fuel\Core\ViewModel',
		'FuelCoreValidation',
		'Closure',
	),

Setting up Groups

The auth package included with FuelPHP is driver based and by default we are using “SimpleAuth” which is the only driver included in the package. When you are more experienced with FuelPHP, you can start to create custom drivers to integrate with other users’ systems – such as third-party forums, content management systems, etc.

To enable groups for SimpleAuth, we simply open up fuel/packages/auth/config/simpleauth.php and set groups to the following:

return array(
  'groups' => array(
 	  -1 => array('name' => 'Banned', 'roles' => array('banned')),
 	  0	=> array('name' => 'Guests', 'roles' => array()),
 	  1	=> array('name' => 'Users', 'roles' => array('user')),
 	  50 => array('name' => 'Moderators', 'roles' => array('user', 'moderator')),
 	  100  => array('name' => 'Administrators', 'roles' => array('user', 'moderator', 'admin')),
  ),
);

These could, of course, be anything, but are pretty standard for most apps and will work for this tutorial.


Step 4 - Creating Users

As we are building an admin panel, we need to create the users table and then populate it with a user record who will be the first administrator. We could use something like phpMyAdmin or a GUI like Navicat, but it’s better to do things via Oil so we stay within our codebase. This means password hashing is salted correctly with whatever driver is being used – and is how we’ll be doing it:

$ oil generate model users username:varchar[50] password:string group:int email:string last_login:int login_hash:string profile_fields:text
  Creating model: /Users/phil/Sites/blog/fuel/app/classes/model/user.php
  Creating migration: /Users/phil/Sites/blog/fuel/app/migrations/001_create_users.php
$ oil refine migrate

This creates a user model for us and creates another migration that will build our user table when the oil refine migrate task is run. Next we have to create an Administrator user, which, again, we could do via a GUI but where’s the fun in that?

$ oil console
Fuel 1.1-rc1 - PHP 5.3.6 (cli) (Sep  8 2011 19:31:33) [Darwin]
>>> Auth::create_user('admin', 'password', 'phil@example.com', 100);
1
-- Ctrl + C to exit

We’ve used the Oil Console to write code in real-time and get a response. Auth::create_user() was passed a username, password, email address and the group_id for admins, which for now we’ll just use 100 – which we set in the config. The 1 is a response from the code, which means user_id has a value of 1.


Step 5 - Code Generation

Generators are a great way to build a bunch of code from scratch and getting a running start.

As explained in the first article we can use scaffolding to build large chunks of an application quickly. This is done through the oil command and is all very optional, but it is a great way to build a bunch of code from scratch. Some people look at code generation as “tools for people who don’t know how to code” or think its some kind of black magic, but if you are new to a framework and do not want to have to learn how to put everything together then having a system make code for you is not such a bad thing.

FuelPHP v1.1 takes the code generation one small step further. Instead of just creating scaffolding (unprotected CRUD) you can now generate Admin code. This works in exactly the same way but implements a simple admin template, admin controller, etc and uses the auth package to lock down your generated code. Taking advantage of the Twitter Bootstrap, this all looks good enough to use and with only a bit of tweaking you’ll have an app that you can ship.

This is done via the command line using the oil command. We’ll generate some code then walk through it.

$ oil generate admin posts title:string slug:string summary:text body:text user_id:int
	Creating controller: /Users/phil/Sites/blog/fuel/app/classes/controller/base.php
	Creating controller: /Users/phil/Sites/blog/fuel/app/classes/controller/admin.php
	Creating views: /Users/phil/Sites/blog/fuel/app/views/admin/template.php
	Creating views: /Users/phil/Sites/blog/fuel/app/views/admin/dashboard.php
	Creating views: /Users/phil/Sites/blog/fuel/app/views/admin/login.php
	Creating migration: /Users/phil/Sites/blog/fuel/app/migrations/002_create_posts.php
	Creating model: /Users/phil/Sites/blog/fuel/app/classes/model/post.php
	Creating controller: /Users/phil/Sites/blog/fuel/app/classes/controller/admin/posts.php
	Creating view: /Users/phil/Sites/blog/fuel/app/views/admin/posts/index.php
	Creating view: /Users/phil/Sites/blog/fuel/app/views/admin/posts/view.php
	Creating view: /Users/phil/Sites/blog/fuel/app/views/admin/posts/create.php
	Creating view: /Users/phil/Sites/blog/fuel/app/views/admin/posts/edit.php
	Creating view: /Users/phil/Sites/blog/fuel/app/views/admin/posts/_form.php
	Creating view: /Users/phil/Sites/blog/fuel/app/views/template.php

This is quite a bit of code because it is the first time it has been run. FuelPHP will set up a few basic templates and files, then build the MVC components for the posts section. Remember, this is just like writing the code yourself, but quicker. You can take a look at this output by going to /blog/public/admin/posts:

Posts - Admin Generated code

Understanding Controllers

We’ve added a Controller_Base which will contain logic for your entire app, so every controller can extend from this. The file simply contains:

abstract class Controller_Base extends Controller_Template {

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

		// Assign current_user to the instance so controllers can use it
		$this->current_user = Auth::check() ? Model_User::find(Arr::get(Auth::get_user_id(), 1)) : null;

		// Set a global variable so views can use it
		View::set_global('current_user', $this->current_user);
	}

}

By extending Controller_Template, all views will be wrapped by a template automatically. Then in the before() function we do a little bit of logic to get the current user and make it available as $this->current_user in controllers and $current_user in views.

Another controller will be built called Controller_Admin which extends Controller_Base, so as well as having the current user being built we can check to see if a user is actually an admin:

abstract class Controller_Admin extends Controller_Base {

	public $template = 'admin/template';

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

		if ( ! Auth::member(100) and Request::active()->action != 'login')
		{
			Response::redirect('admin/login');
		}
	}

	// ....

}

You’ll notice that this controller sets a custom template so instead of looking for fuel/app/views/template.php, it will look for fuel/app/views/admin/template.php. Then if a user does not match member(100) – the admin group ID set earlier – they will be sent off the the login page.

Extending Controllers

One very handy feature that FuelPHP has is to allow you to extend other controllers. Normally they are just loaded directly by the Request class after being routed to by the URL, but sometimes it is useful to extend controllers to share logic or methods. In this case, we are already checking permissions in Controller_Admin so we can extend that controller to reuse this logic.

Controller_Admin_Posts extends Controller_Admin. This means it contains the same before() check and therefore is protected in the same way as any other controller in your admin panel.

Now What?

Code generation is basically just the first step in working on an application. We still need to tweak our forms and create a frontend. For example, if you go to create or edit a post it will show the user_id field as a text box.


Step 6 - Updating the CRUD Forms

We’re going to want to modify our action_create() method in fuel/app/classes/admin/posts.php so we have a list of users available. To do this we can replace the method with this code:

public function action_create($id = null)
{
	$view = View::forge('admin/posts/create');

	if (Input::method() == 'POST')
	{
		$post = Model_Post::forge(array(
			'title' => Input::post('title'),
			'slug' => Inflector::friendly_title(Input::post('title'), '-', true),
			'summary' => Input::post('summary'),
			'body' => Input::post('body'),
			'user_id' => Input::post('user_id'),
		));

		if ($post and $post->save())
		{
			Session::set_flash('success', 'Added post #'.$post->id.'.');
			Response::redirect('admin/posts');
		}

		else
		{
			Session::set_flash('error', 'Could not save post.');
		}
	}

	// Set some data
	$view->set_global('users', Arr::assoc_to_keyval(Model_User::find('all'), 'id', 'username'));

	$this->template->title = "Create Post";
	$this->template->content = $view;
}

This is the same as the code before with two changes:

$view = View::forge('admin/posts/create');

This creates a new View object. We can assign properties to this view by setting them as properties, so our users data can be passed easily and would normally work a little like this:

$view->users = array(1 => "User 1", 2 => "User 2");

Now we make a similar update to action_edit():

public function action_edit($id = null)
{
	$view = View::forge('admin/posts/edit');

	$post = Model_Post::find($id);

	if (Input::method() == 'POST')
	{
		$post->title = Input::post('title');
		$post->slug = Inflector::friendly_title(Input::post('title'), '-', true);
		$post->summary = Input::post('summary');
		$post->body = Input::post('body');
		$post->user_id = Input::post('user_id');

		if ($post->save())
		{
			Session::set_flash('success', 'Updated post #' . $id);
			Response::redirect('admin/posts');
		}

		else
		{
			Session::set_flash('error', 'Could not update post #' . $id);
		}
	}

	else
	{
		$this->template->set_global('post', $post, false);
	}

	// Set some data
	$view->set_global('users', Arr::assoc_to_keyval(Model_User::find('all'), 'id', 'username'));

	$this->template->title = "Edit Post";
	$this->template->content = $view;
}

However, because the create.php and edit.php views share a theme partial _form.php, and properties only set variables to that one specific view we need to use the View::set_global() method:

$view->set_global('users', Arr::assoc_to_keyval(Model_User::find('all'), 'id', 'username'));

This uses the Model_User object to get all of our users, then flattens them to an associative array for our form.

The HTML now needs to change, so delete the div wrapping “slug” – we’ll do that ourselves and change the user_id field from being a input box to a select box:

<div class="clearfix">
	<?php echo Form::label('User', 'user_id'); ?>

	<div class="input">
		<?php echo Form::select('user_id', Input::post('user_id', isset($post) ? $post->user_id : $current_user->id), $users, array('class' => 'span6')); ?>

	</div>
</div>
User dropdown form

This will set the box to show all users and default to the current logged in user if none are provided. That is the only tweak we need to make at this point, so let’s make a frontend!

The Frontend

Creating a basic blog frontend is really simple so won’t worry about using code generation.

Create a new controller fuel/app/classes/controller/blog.php:

class Controller_Blog extends Controller_Base
{
	public function action_index()
	{
		$view = View::forge('blog/index');

		$view->posts = Model_Post::find('all');

		$this->template->title = 'My Blog about Stuff';
		$this->template->content = $view;
	}
}

And the fuel/app/views/blog/index.php view file:

<h2>Recent Posts</h2>

<?php foreach ($posts as $post): ?>

	<h3><?php echo Html::anchor('blog/view/'.$post->slug, $post->title) ?></h3>

	<p><?php echo $post->summary ?></p>

<?php endforeach; ?>

This is just a simple loop through the $posts array which contains all your articles.

Comment form

A foreach loop, a hyperlink and a summary is all we need on this view file and we’ll make another view file to actually see the post. We’ll call this controller method action_view() and make a view file called view.php:

<h2><?php echo $post->title ?></h2>

<p><strong>Posted: </strong><?php echo date('nS F, Y', $post->created_at) ?> (<?php echo Date::time_ago($post->created_at)?>)</p>

<p><?php echo nl2br($post->body) ?></p>

This is the method for the blog controller:

public function action_view($slug)
{
	$post = Model_Post::find_by_slug($slug);

	$this->template->title = $post->title;
	$this->template->content = View::forge('blog/view', array(
		'post' => $post,
	));
}

The find_by_slug() method is a “magic method” that will build WHERE slug = "foo" in your query and return a single Model_Post instance with that data.

Comment form

Now people can look at your – horribly unstyled – website and see a basic blog, and you have an admin interface to manage it!


Step 7 - Using the ORM

So far we’ve been using the ORM for our models to do basic CRUD but we’ve not yet seen anything to do with relationships. The ORM makes this incredibly easy as, well it stands for “Object Relational Mapping” for a reason. To set up relationships all we need to do is modify our models a little bit to explain how the ORM should relate the data.

A post is created by one user, so we say it “belongs to” a user. In Model_Post we can add:

protected static $_belongs_to = array('user');

The user can post multiple blogs, so we add this line:

protected static $_has_many = array('posts');

A quick way to test this works is by firing up the oil console:

Fuel 1.1-rc2 - PHP 5.3.6 (cli) (Sep  8 2011 19:31:33) [Darwin]
>>> $post = Model_Post::find('first');
>>> $post->user->username
admin
-- Ctrl + C to exit

Nice, the relationships work!

Now in the view we can work with $post->user and output their name, so let’s change fuel/app/views/blog/view.php:

<h2><?php echo $post->title ?></h2>

<p>
	<strong>Posted: </strong><?php echo date('nS F, Y', $post->created_at) ?> (<?php echo Date::time_ago($post->created_at)?>)
	by <?php echo $post->user->username ?>
</p>

<p><?php echo nl2br($post->body) ?></p>

Doing things this way is called lazy loading. What basically happens is that when you ask for $post->user the ORM returns the user object based on the contents of the user_id field. This means to get the post and the first user is two queries, getting the second will be a third, etc and so it can end up being very slow with all these additional queries.

To improve performance you can switch from lazy loading to eager loading which basically tells the ORM you are going to be joining users on so it should do it as part of the first query. This can make for one larger query but the ORM will slice it up nicely for you and you wont notice the difference.

$post = Model_Post::find_by_slug($slug, array('related' => array('user')));

If you look at the query the ORM produces, it will be something like this:

SELECT <code>t0</code>.<code>id</code> AS <code>t0_c0</code>, <code>t0</code>.<code>title</code> AS <code>t0_c1</code>, <code>t0</code>.<code>slug</code> AS <code>t0_c2</code>, <code>t0</code>.<code>summary</code> AS <code>t0_c3</code>, <code>t0</code>.<code>body</code> AS <code>t0_c4</code>, <code>t0</code>.<code>user_id</code> AS <code>t0_c5</code>, <code>t0</code>.<code>created_at</code> AS <code>t0_c6</code>, <code>t0</code>.<code>updated_at</code> AS <code>t0_c7</code>, <code>t1</code>.<code>id</code> AS <code>t1_c0</code>, <code>t1</code>.<code>username</code> AS <code>t1_c1</code>, <code>t1</code>.<code>password</code> AS <code>t1_c2</code>, <code>t1</code>.<code>group</code> AS <code>t1_c3</code>, <code>t1</code>.<code>email</code> AS <code>t1_c4</code>, <code>t1</code>.<code>last_login</code> AS <code>t1_c5</code>, <code>t1</code>.<code>login_hash</code> AS <code>t1_c6</code>, <code>t1</code>.<code>profile_fields</code> AS <code>t1_c7</code>, <code>t1</code>.<code>created_at</code> AS <code>t1_c8</code>, <code>t1</code>.<code>updated_at</code> AS <code>t1_c9</code> FROM (SELECT <code>t0</code>.<code>id</code>, <code>t0</code>.<code>title</code>, <code>t0</code>.<code>slug</code>, <code>t0</code>.<code>summary</code>, <code>t0</code>.<code>body</code>, <code>t0</code>.<code>user_id</code>, <code>t0</code>.<code>created_at</code>, <code>t0</code>.<code>updated_at</code> FROM <code>posts</code> AS <code>t0</code> ORDER BY <code>t0</code>.<code>id</code> ASC LIMIT 1) AS <code>t0</code> LEFT JOIN <code>users</code> AS <code>t1</code> ON (<code>t0</code>.<code>user_id</code> = <code>t1</code>.<code>id</code>) WHERE (<code>t0</code>.<code>slug</code> = 'women-love-guys-who-use-fuelphp') ORDER BY <code>t0</code>.<code>id</code> ASC

At first this might look insane, but the ORM knows exactly what is going on. Using this approach in the past I have reduced a application from running 300+ queries on a busy page (very slow) down to about 2 (very fast).


Step 8 - Adding Comments

Lots of “make a blog” tutorials stop before they get to comments which I think is a major under-sight. Every blog needs comments and we want to add them quickly so we can go and do something more fun, so we can use code generation to build the admin interface:

$ oil generate admin comments name:string email:string website:string message:text post_id:int
	Creating migration: /Users/phil/Sites/blog/fuel/app/migrations/003_create_comments.php
	Creating model: /Users/phil/Sites/blog/fuel/app/classes/model/comment.php
	Creating controller: /Users/phil/Sites/blog/fuel/app/classes/controller/admin/comments.php
	Creating view: /Users/phil/Sites/blog/fuel/app/views/admin/comments/index.php
	Creating view: /Users/phil/Sites/blog/fuel/app/views/admin/comments/view.php
	Creating view: /Users/phil/Sites/blog/fuel/app/views/admin/comments/create.php
	Creating view: /Users/phil/Sites/blog/fuel/app/views/admin/comments/edit.php
	Creating view: /Users/phil/Sites/blog/fuel/app/views/admin/comments/_form.php

$ oil refine migrate
Migrated app:default to latest version: 3.

Comments will need some similar tweaks as Post did so make those then we set up the relationships.

Model_User

protected static $_has_many = array('posts', 'comments');
protected static $_belongs_to = array('user');

Model_Post

protected static $_belongs_to = array('user');
protected static $_has_many = array('comments');

Model_Comment

protected static $_belongs_to = array('post', 'user');

Add a comment through the interface the cheating way, by going to http://localhost/blog/public/admin/comments/create and entering one

Comment admin

Now we can test the relationship in the console again:

Fuel 1.1-rc2 - PHP 5.3.6 (cli) (Sep  8 2011 19:31:33) [Darwin]
>>> Model_Post::find(1)->comments
array (
  1 =>
  Model_Comment::__set_state(array(
   '_is_new' => false,
   '_frozen' => false,
   '_data' =>
  array (
  'id' => '1',
  'name' => 'Phil Sturgeon',
  'email' => 'email@philsturgeon.co.uk',
  'website' => 'http://philsturgeon.co.uk/',
  'message' => 'This is an epic article.',
  'post_id' => '1',
  'created_at' => '1322931744',
  'updated_at' => '1322931744',
  ),
   '_original' =>
  array (
  'id' => '1',
  'name' => 'Phil Sturgeon',
  'email' => 'email@philsturgeon.co.uk',
  'website' => 'http://philsturgeon.co.uk/',
  'message' => 'This is an epic article.',
  'post_id' => '1',
  'created_at' => '1322931744',
  'updated_at' => '1322931744',
  ),
   '_data_relations' =>
  array (
  ),
   '_original_relations' =>
  array (
  ),
   '_view' => NULL,
   '_iterable' =>
  array (
  ),
  )),
)

Output of an array of ORM objects is a little verbose, but at least we can see the data. This means the relationship is working nicely, so let’s modify the ORM query in the blog controllers action_view() method to include comments:

$post = Model_Post::find_by_slug($slug, array('related' => array('user', 'comments')));

Now update the blog view to output the comments and have a form to add more:

<h2><?php echo $post->title ?></h2>

<p>
	<strong>Posted: </strong><?php echo date('nS F, Y', $post->created_at) ?> (<?php echo Date::time_ago($post->created_at)?>)
	by <?php echo $post->user->username ?>
</p>

<p><?php echo nl2br($post->body) ?></p>

<hr />

<h3 id="comments">Comments</h3>

<?php foreach ($post->comments as $comment): ?>

	<p><?php echo Html::anchor($comment->website, $comment->name) ?> said "<?php echo $comment->message?>"</p>

<?php endforeach; ?>

<h3>Write a comment</h3>

<?php echo Form::open('blog/comment/'.$post->slug) ?>

<div class="row">
	<label for="name">Name:</label>
	<div class="input"><?php echo Form::input('name'); ?></div>
</div>

<div class="row">
	<label for="website">Website:</label>
	<div class="input"><?php echo Form::input('website'); ?></div>
</div>

<div class="row">
	<label for="email">Email:</label>
	<div class="input"><?php echo Form::input('email'); ?></div>
</div>

<div class="row">
	<label for="message">Comment:</label>
	<div class="input"><?php echo Form::textarea('message'); ?></div>
</div>

<div class="row">
	<div class="input"><?php echo Form::submit('submit'); ?></div>
</div>

<?php echo Form::close() ?>

This code will output all comments in a very basic style followed by a really simple form using Twitter Bootstrap markup. I’m sure you can whip up some styling for the comments section.

Comment form

We can see the comment is being output and any that are added will also be put in. So the next and final stage in the process is to get comments saved.

The line with Form::open('blog/comment/'.$post->slug) will set the action to blog/comment/women-love-guys-who-use-fuelphp which means Controller_Blog needs a new method action_comment($slug) which should look something like this:

public function action_comment($slug)
{
	$post = Model_Post::find_by_slug($slug);

	// Lazy validation
	if (Input::post('name') AND Input::post('email') AND Input::post('message'))
	{
		// Create a new comment
		$post->comments[] = new Model_Comment(array(
			'name' => Input::post('name'),
			'website' => Input::post('website'),
			'email' => Input::post('email'),
			'message' => Input::post('message'),
			'user_id' => $this->current_user->id,
		));

		// Save the post and the comment will save too
		if ($post->save())
		{
			$comment = end($post->comments);
			Session::set_flash('success', 'Added comment #'.$comment->id.'.');
		}
		else
		{
			Session::set_flash('error', 'Could not save comment.');
		}

		Response::redirect('blog/view/'.$slug);
	}

	// Did not have all the fields
	else
	{
		// Just show the view again until they get it right
		$this->action_view($slug);
	}
}

Summary

This article skipped a few things like how to set up a pretty URL instead of localhost/blog/public and how to use form validation in the model or the controller, but all of this can be found in the FuelPHP Documentation. I planned to touch on things like File Uploads and Migrations a little more but again this is all documented already.

You should have enough code now to pick apart and play with, and whatever project you choose to work on with FuelPHP already has a blog now – so throw up a splash page, design it and you are ready to make the next SquareFaceTwitBook.


June 03 2011

16:07

Quick Tip: Integrate Compass into an Existing CodeIgniter Project

Advertise here

I was recently asked about how to use Compass in an existing CodeIgniter project. It seems that the emailer was under the impression that Compass was made for Ruby and Rails. But that’s not the case at all!

Note that Ruby will need to be installed on your system in order for Compass to work correctly. It’s installed by default on a Mac. Otherwise, the easiest way to install the latest version of Ruby is with RVM. Refer here for detailed instructions.

Choose 720p for optimal viewing.
Subscribe to our YouTube and Blip.tv channels to watch more screencasts.

Let us walk you through the process of getting started with Sass and Compass from scratch.

Sponsored post
feedback2020-admin
14:56
you are awesome!
Reposted bysirthomasbolton sirthomasbolton

February 23 2011

18:38

Create your First Tiny MVC Boilerplate with PHP


I’m often asked about how to create an extremely simple MVC boilerplate for small projects which don’t require a massive framework, like CodeIgniter or CakePHP. We’ll build a solution in ten minutes or so.

It’s important for me to note that I’m not advocating that you shouldn’t use large frameworks. They absolutely have their places, and I use them often. That said, there are definitely times when they can be overkill for smaller projects. When your only requirement is code organization, it’s typically better to scrape together your own MVC boilerplate.


Conclusion

If you enjoyed this video, and would like to take our tiny framework to the next level, we can definitely do so together. Let me know in the comments!

February 11 2011

05:33

CodeIgniter from Scratch: Search Results without Query Strings


In this episode of CodeIgniter from Scratch, we will implement search features, and display the results pages without the use of query strings. You can use these methods to keep your URL’s clean, and continue using the uri segments convention of the CodeIgniter framework.

Press the HD button for a clearer picture.

Subscribe to our YouTube page to watch all of the video tutorials!

November 17 2010

14:00

Basecamp Style Subdomains With CodeIgniter


CodeIgniter is a simple and lightweight PHP framework used to create powerful web applications. Today, we are going to do something nifty: we’ll combine user names and subdomains to make for a more cohesive user experience.

If you’re a bit confused, this feature allows your users to access their accounts by typing in a custom url, which maps to their user name, such as harbinger.yourapp.com.


Overview

In this tutorial, we are going to be creating both a front-end view and a back-end application view, similar to the configuration used in Basecamp, where visiting basecamphq.com shows the homepage, but visiting a subdomain displays the login page.

We will be using the fictional address nettutsapp.com, and will create a sample “dashboard” page; however, you could incorporate this into an existing project with relative ease.

basecamp-comapre

Before you begin, make sure that you have a web server with PHP installed. You also need to download CodeIgniter; in the example, I am using CodeIgniter 2.0, but the code should work on 1.7.2. So let’s get started!


Step 1: DNS Configuration

First, we need to configure our DNS settings to make all subdomains resolve to a single address. If you are working on a live server, then you will have to change your DNS settings with the company who handles your DNS. This is most commonly your web host or domain registrar.

To cover each configuration in this tutorial would take too much time. Instead, ask your host for some help to set up wildcard subdomains. You might expect to add something like the line below to your DNS configuration.

*.nettutsappapp.com.      IN  A    91.32.913.343

Instead, if you are working on a local server, adding a wildcard subdomain to the hosts file is quite tricky. What I prefer to do is add single entries for testing purposes. These can be deleted after you have finished. For our domain, we need to add four entries as follows:

  • 127.0.0.1     nettutsapp.com
  • 127.0.0.1     user1.nettutsapp.com
  • 127.0.0.1     user2.nettutsapp.com
  • 127.0.0.1     user3.nettutsapp.com

Mac Hosts Configuration

To do this on a Mac, open Terminal and type sudo nano /etc/hosts. Use the arrow keys to move to the bottom of the document, and add the entries to the bottom of the file. Once done, press Ctrl+X and Y to confirm the save.

dns-terminal1

Windows Hosts Configuration

If you are using Windows, browse to the directory C:\Program Files\system32\drivers\etc and open the hosts file in Notepad, or your preferred text editor. Add four entries, shown above, and save the file.

If you’ve made DNS changes to a live server, it will take a while before you notice any effect. If you made changes to your hosts file, the changes will be immediate.


Step 2: Apache Configuration

The aim here is to set up two Virtual Hosts in the Apache Configuration,:one serves the front end page (Basecamp Homepage), and the other serves the page seen when accessed via a subdomain (Dashboard Page).

To add new entries, you need to open the httpd.conf file, which can be found in the Apache installation directory. It’s often found in the bin/apache/conf folder. However, depending on your server configuration, the location may vary.

Once opened, you need to add the two entries, shown below. Be sure to change the DocumentRoot to a location of your choice, which you have created.

Note: Remember the wildcard and the different directory path on the second VirtualHost.

WAMP Users: I recommend reading this post, which describes how to set up VirtualHosts. You may also encounter permission problems; so I recommend placing your VirtualHost directories inside the www directory.

	<VirtualHost *:80>
		DocumentRoot "/Users/densepixel/Sites/MAMP PRO/nettutsappfront"
		ServerName nettutsapp.com
		ServerAlias nettutsapp.com

		<Directory "/Users/densepixel/Sites/MAMP PRO/nettutsappfront">
			Options -Indexes
			Options FollowSymLinks
			AllowOverride All
		</Directory>
	</VirtualHost>

	<VirtualHost *:80>
		DocumentRoot "/Users/densepixel/Sites/MAMP PRO/nettutsapp"
		ServerName nettutsapp.com
		ServerAlias *.nettutsapp.com

		<Directory "/Users/densepixel/Sites/MAMP PRO/nettutsapp">
			Options -Indexes
			Options FollowSymLinks
			AllowOverride All
		</Directory>
	</VirtualHost>

Once you have saved the file, you need to restart Apache for the changes to take effect.

Make sure that you have created the directories you specified in the httpd.conf file before starting the server.


Step 4: Testing Our Server Configuration

Before we test the configuration, place a basic html page in each of the directories you created earlier. Maybe add a single line of text to each, so you can differentiate them. I have copied an example for you to use.

<!DOCTYPE html>
<html lang="">
<head>
	<meta charset="utf-8">
	<title>NetTuts App Front</title>
</head>
<body>
	NetTutsApp Front
</body>
</html>

Next, open your favorite browser, and first check the address nettutsapp.com. If everything works, you should be looking at the page you placed in the ‘nettutsappfront‘ directory.

Next, check a subdomain, eg. user1.nettutsapp.com; this should show you the other page you created in the directory.

config-success

You can go on to check the other subdomains you specified in the hosts file, which should all show the page saved within our directory.

Step 5: CodeIgniter Installation

This tutorial assumes that you know how to install CodeIgniter. If not, you should take a look at this video tutorial by Jeffrey Way, which explains the process in detail.

Place the CodeIgniter files into our directory after deleting the html page we created before. If you are using CodeIgniter 1.7.2, you may want to take the application folder out of the system folder.

nettutsapp-ci

Test the installation by browsing to the URL user1.nettutsapp.com, and you should see the CodeIgniter Welcome Page.

nettutsapp-ci-welcome

Step 6: Setting up CodeIgniter

Set up CodeIgniter as you normally would, as described in this tutorial. You may want to remove the index.php from the URL, autoload some libraries or helpers, etc. For the purposes of this tutorial, we need to autoload the database library and the url helper. Open the autoload.php file in the config directory, and add the relevant entries.

We also need to change the default controller to one which we will be making, called dashboard. This value can be changed within the /config/routes.php file.

Base_url

For the user subdomains, we need to make the base_url variable dynamic, as the application will be receiving requests from a number of potential subdomains.

The easiest way to do this is by using the HTTP_HOST variable. Open the config.php file, and find the variable $config['base_url'], and replace it with the following code:

if(isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == "on"){$ssl_set = "s";} else{$ssl_set = "";}
$config['base_url'] = 'http'.$ssl_set.'://'.$_SERVER['HTTP_HOST'];

This code allows for HTTPS connections, however, if you never plan on using HTTPS, you can simplify it to base_url = $_SERVER['HTTP_HOST'].

Database Settings

Before we go ahead and add the database settings into our CodeIgniter application, we need to create both the database and a sample table.

This example application uses one database and one table. This table will hold all the subdomains currently assigned, and some basic information about them. If you’ve decided to use this code in your own application, you’ll have to generally assign multiple users to a single subdomain, however that database schema is beyond the scope of this tutorial.

The table is named nt_subdomains, within the database ntapp, and has four fields:

  • subdomain_id(primary, auto_increment)
  • subdomain_name
  • user_fname
  • user_sname

I have also populated the table with two records, which match the subdomains we’ve added to our hosts file:

php-my-admin

Now we can open the database config, file found in /application/config/database.php, and edit the following values so that they match your personal configuration settings.

$db['default']['hostname'] = 'localhost';
$db['default']['username'] = 'root'; //MAMP default
$db['default']['password'] = 'root'; //MAMP default
$db['default']['database'] = 'ntapp';

And we’re done setting up our CodeIgniter installation. Let’s start using the subdomains in our application.


Step 7: Creating Our Controllers and Views

For this application, we are going to create two controllers. The first is an error controller, which displays an error if the subdomain has not been registered for use in the application. The other controller is our main dashboard controller, which is what the user sees if the subdomain has been added to our database.

Error Controller

Let’s go ahead and create our error controller. First, create a new file in the /application/controllers directory, and call it error.php.
Note: The name of the file is important

<?php

class Error extends Controller {

	function Error()
	{
		parent::Controller();
	}

	function index()
	{
		$this->load->view('error');
	}
}

Add the code above to our new error.php file. The index function loads a view called ‘error‘, which we will be creating later.

Dashboard Controller

Now we need to create the main dashboard controller, which will be loaded when a user enters one of the subdomains. The controller will then check whether the subdomain has been registered, and will redirect as necessary. We’ll add this code later, but first, we need to create the basic controller.

Create a new file within the controllers directory, and name it dashboard.php. Within this file, we need to create the controller and load the dashboard view. Copy the code below and save the file.

<?php

class Dashboard extends Controller {

	function Dashboard()
	{
		parent::Controller();
	}

	function index()
	{
		$this->load->view('dashboard');
	}
}

Error View

The error page will be displayed when a user tries to access a subdomain, which has not been registered for use by the application. For the purposes of the tutorial, simply create a basic page, displaying the message Subdomain Not Registered. Add the code below to a new file called error.php and save it within the application/views folder.

<html>
<head>
	<title>Application Error : Nettuts App</title>
</head>
<body>

	<h1>Nettuts Application Error</h1>

	<p>Subdomain Not Registered</p>

</body>
</html>

Dashboard View

initial-dashboard

For the time being, we will only be creating a basic dashboard page. You can use the same structure as the error view, and just change it to read Nettuts Dashboard, or something along those lines. Save the page as dashboard.php, within the application/views folder.

Test the two views by visiting the URLs:

  • user1.nettutsapp.com/index.php/error
  • user1.nettutsapp.com/index.php/dashboard

Working? Let’s move on.


Step 8: Extending Our Dashboard Controller (Part 1)

The next step is to extract the subdomain name in our controller so we can use it in a database query.

We are going to insert our subdomain checking code into the construct function within the dashboard controller. (Underneath the parent::Controller()). This means that the subdomain will be checked when any of the functions within the dashboard controller are accessed.

The easiest way to extract the subdomain name is to use the PHP explode function, and set the delimiter to ‘.’. As we only need the first part, we can split it into two parts, and then assign the first part (the subdomain name) to a variable.

To test this, we can echo the variable out in the controller itself. See the code below:

<?php

class Dashboard extends Controller {

	function Dashboard()
	{
		parent::Controller();

		$subdomain_arr = explode('.', $_SERVER['HTTP_HOST'], 2); //creates the various parts
		$subdomain_name = $subdomain_arr[0]; //assigns the first part
		echo $subdomain_name; // for testing only
	}

}

Access your subdomain URLs, and you should see the correct subdomain echoed on your page, as below.
Note: You can delete the echo statement now.

subdomain-parse1

Now that we have access to the subdomain name in our controller, we can check to see if it has been added the table we created earlier.

We will be using CodeIgniter’s ActiveRecord Class to build our queries, which will check the table for the accessed subdomain. If it is present, then the user will be able to access the dashboard page. If, on the other hand, the subdomain has not been entered, then they are denied access, and are then redirected to the error page we created earlier.

For this tutorial, we won’t be using models as it makes the tutorial much easier to follow. CodeIgniter is quite flexible in that it doesn’t force you to use them.

First, we need to assemble the query as show below. This code will only work in PHP5, as it uses method chaining; however, you can change it to your liking.

// adds on from rest of construct //
$this->db->from('nt_subdomains')->where('subdomain_name', $subdomain_name);
$query = $this->db->get();

We can use the CodeIgniter function row() to check whether that subdomain exists in the table. If it doesn’t, then we need to use the redirect function to redirect our users to the error controller. The next part of the code is below:

// adds on from previous code //
if($query->num_rows() < 1)
		{
		redirect ('error');
		}

Let’s test this by accessing user1.nettutsapp.com, which should direct you to the dashboard page. Now, try user3.nettutsapp.com, which should redirect you to the error page as it was not entered into the table.

subdomain-comapre1

Step 9: Extending Our Dashboard Controller (Part 2)

Now we can use the information in the table to display specific information for each subdomain.

We’ll add to the index function in our dashboard controller. First, copy the subdomain name code and the database query we used before.

function index()
	{
		$subdomain_arr = explode('.', $_SERVER['HTTP_HOST'], 2);
		$subdomain_name = $subdomain_arr[0];

		$this->db->from('nt_subdomains')->where('subdomain_name', $subdomain_name);
		$query = $this->db->get();

	}

We are using CodeIgniter’s row() function to retrieve the result of the query. The row function returns a single result row, which means we don’t need to use a foreach loop; it’s unnecessary.

// adds on from rest of index function //
$subdomain_info = $query->row();

Then, assign the user_fname and user_sname column values to the array, $data, which is then passed to the view.

$data['fname'] = $subdomain_info->user_fname;
$data['sname'] = $subdomain_info->user_sname;
$this->load->view('dashboard', $data);

We can use these values within our view by using the variables $fname and $sname. Open the dashboard view and edit it to read:

	<p>Welcome to your dashboard
	<b><?php echo $fname; ?> <?php echo $sname ?></b>
	</p>

And we’re done! Let’s test it.


Step 10: Testing

Try all of the URLs, and hopefully, if everything went according to plan, the results should be as follows:

  • nettutsapp.com → Front End Page
  • user1.nettutsapp.com → Dashboard (John Doe)
  • user2.nettutsapp.com → Dashboard (Steve Smith)
  • user3.nettutsapp.com → Error Page
finalresult

Controller and View Code

Here’s the complete cote used for our controllers and views:

Dashboard Controller

<?php

class Dashboard extends Controller {

	function Dashboard()
	{
		parent::Controller();

		$subdomain_arr = explode('.', $_SERVER['HTTP_HOST'], 2);
		$subdomain_name = $subdomain_arr[0];

		$this->db->from('nt_subdomains')->where('subdomain_name', $subdomain_name);
		$query = $this->db->get();

		if($query->num_rows() < 1)
		{
		redirect ('error');
		}

	}

	function index()
	{
		$subdomain_arr = explode('.', $_SERVER['HTTP_HOST'], 2);
		$subdomain_name = $subdomain_arr[0];

		$this->db->from('nt_subdomains')->where('subdomain_name', $subdomain_name);
		$query = $this->db->get();

		$subdomain_info = $query->row();
		$data['fname'] = $subdomain_info->user_fname;
		$data['sname'] = $subdomain_info->user_sname;
		$this->load->view('dashboard', $data);
	}
}

Error Controller

<?php

class Error extends Controller {

	function Error()
	{
		parent::Controller();
	}

	function index()
	{
		$this->load->view('error');
	}
}

Dashboard View

<html>
<head>
	<title>Dashboard : Nettuts App</title>
</head>
<body>

	<h1>Nettuts Dashboard</h1>

	<p>Welcome to your dashboard
	<b><?php echo $fname; ?> <?php echo $sname ?></b>
	</p>

</body>
</html>

Error View

<html>
<head>
	<title>Application Error : Nettuts App</title>
</head>
<body>

	<h1>Application Error</h1>

	<p>Subdomain Not Registered</p>

</body>
</html>

Conclusion

Of course, this tutorial describes only one way of obtaining this nifty functionality. I’m sure there are many more; so feel free to chime in with your thoughts and opinions in the comments section below.

November 10 2010

01:02

How to Create a Config Variables Loader in CodeIgniter: New Premium Tutorial


If you’ve been using CodeIgniter or another MVC framework for any length of time, you’ve probably started to realize the advantage of abstracting data and logic from presentation. But efficiency is not without its drawbacks, and, in the case of CodeIgniter, one of these drawbacks is the need to constantly bounce variables out of your controllers so that they are available within your views.

In this tutorial, I’ll show you a really cool way to eliminate this hassle. Become a Premium member to gain access to this tutorial, as well as hundreds of other tutorial, screencasts, and freebies from the Tuts+ network.

We’re going to build a config loader that automatically loads all of your important site variables and makes them available globally. Even more cool, your site variables will be stored in a separate config file that you can append or edit whenever you like. Not only will this save you a lot of time (and errors!), but it will help you develop a killer workflow that you can build on for all your projects. Lets get started!


Join Net Premium

Screencasts and Bonus Tutorials

For those unfamiliar, the family of Tuts+ sites runs a premium membership service. For $9 per month, you gain access to exclusive premium tutorials, screencasts, and freebies from Nettuts+, Psdtuts+, Aetuts+, Audiotuts+, Vectortuts+, and CgTuts+. For the price of a pizza, you’ll learn from some of the best minds in the business. Become a Premium member to read this tutorial, as well as hundreds of other advanced tutorials and screencasts.

October 22 2010

00:37

How to Create a Layout Manager with CodeIgniter: New Premium Tutorial


This Premium video, and companion article will teach you how to create a simple, yet powerful library to handle layouts in the popular CodeIgniter framework. The library you’ll create will allow you to maximize your efficiency, save time and code, modularize your views and even your Javascript and CSS files.

The basic functionality of this library will be very simple. We’ll take the contents of a view, render them with the appropriate data, then take the rendered content and assign it to a variable. Now, we’ll render the layout itself, and replace a part of the layout with the contents of this variable. Simple, but powerful enough.

The idea is to mimic the calls to $this->load->view(). When we call this method, we pass the name (and location) of our view, and then an array of data that will be accessible from the view. Here’s an example:

function method($url_param)
{
  $this->load->view('controller_views/method_view', array('url_param' => $url_param));
}

The above code will take the file system/application/views/controller_views/method_view.php, pass it the url_param variable, and then send it to the browser. Here’s where we come in. We will not send the content to the browser yet. Instead, we’ll send it to the layout, then to the browser. But how do we do that?

…Become a Premium member to follow along with this screencast/article and find out!


Join Net Premium

NETTUTS+ Screencasts and Bonus Tutorials

For those unfamiliar, the family of Tuts+ sites runs a premium membership service. For $9 per month, you gain access to exclusive premium tutorials, screencasts, and freebies from Nettuts+, Psdtuts+, Aetuts+, Audiotuts+, Vectortuts+, and CgTuts+ For the price of a pizza, you’ll learn from some of the best minds in the business. Become a Premium member to read this tutorial, as well as hundreds of other advanced tutorials and screencasts.

August 05 2010

20:31

CodeIgniter from Scratch: Profiling, Benchmarking & Hooks


In this 15th episode of the series, we are going to learn about three subjects: Profiling, Benchmarking and Hooks. You can use these tools to analyze your CodeIgniter applications performance, and figure out what part of the code you need to optimize. We are also going to make even further improvements to the Profiler library to suit our needs.


Catch Up


Day 15: Profiling, Benchmarking & Hooks

Premium Members: Download this Video ( Must be logged in)
Day 15

Thanks for watching! Any questions/thoughts?

June 30 2010

14:44

CodeIgniter from Scratch: Security


In this fourteenth episode of the series, our subject is ‘Security.’ We will go over topics such as: password encryption, message encoding/decoding, XSS filtering, output filtering, sql injection, session security, private controller methods and more.


Catch Up


Day 14: Security


Thanks for watching! Any questions/thoughts?

June 09 2010

16:09

Magento for Designers: Part 5


Magento is a stunningly powerful e-commerce platform. In this miniseries, we’ll learn how to get started with the platform, getting to know the terminologies, setting up a store and all related aspects of it and finally learn how to customize it to make it our very own.

In this fifth part, we’ll be building one of the main views of our store – the product information page. Excited? Let’s get started!


The Full Series


A Quick Recap

In the last part, we laid down the framework for the theme by building the repeating portions of the theme and essentially defining the general layout of the theme.

We also took a practical look at how theming in Magento works by going through how the layout is constructed, how the blocks work and how all the different pieces of the puzzle fit in together.


What are We Building Today?

Today, we’ll build the individual product view page. Just like before, the source files, both the front end and back end, are included. You can use it to see how the page looks but outside of that, the theme should look broken since, you know, we haven’t touched the rest of the views yet. So till we build the rest, try to not venture out of our sandbox.


Goals for the Page

Our goal for this specific page is relatively simple. We want a no-nonsense page that does only the essentials. Thus, I’ve drawn up a short list of elements that I think it needs:

  • The product’s title: Fairly obvious
  • Ability to show an image of the product
  • A quick and full overview
  • Availability and price of the product and finally
  • An add to cart button to place it in the cart

That’s it. I want to keep it as simple as possible and thus opted to not over do it. Once you’ve learnt the general principles, feel free to add as many whizbang features as possible.


The Basic Look

The page basically has to look like so:


Step 1 – The HTML

We’ll first look at the HTML for the content part alone. I’m assuming you’re fairly fluent in HTML and CSS so I’ll skip to the fairly important parts.

<div id="content" class="product">
<div id="main-product-image"><img src="images/primg.gif" /></div>

<div id="product-details">
<div id="product-availability">Availability <span class="available">In stock</span></div>
<div id="product-price">Price <span>$29.00</span></div>
<a class="button" href="#">Add to cart</a>
</div>

<h1>Photoshop to HTML</h1>

<h2>Quick Overview</h2>
<p class="quick-overview">
</p>

<h2>Product Description</h2>
<div class="product-description">

</div>

First up, notice that I’ve wrapped the product image with a div to make it easier to add features in the future. Suppose you want to add a quick caption to the displayed image in the future, this way will let us add this a lot more quicker.

We have another div element holding the availability and pricing information along with the add to cart button. We’ll be sprucing it up with a little CSS3 in a bit.

The rest of the HTML is very straightforward. The title resides in a h1 section while the individual section headings take up h2. The quick overview is nested in a paragraph while the full overview takes up a div.

We can move on to the styling part now.


Step 2 – The CSS

/* Product page */

#main-product-image {
	margin: 0 20px 10px 0;
	padding: 10px;
	float: left;
	border: 1px solid #E1E1E1;
	background: #F3F3F3;
}

#product-details {
	width: 180px;
	padding: 10px;
	float: right;
	border: 1px solid #E1E1E1;
	background: #F3F3F3;
	margin: 0 0 0 20px;
}

#product-availability span.available, #product-price span {
	color: #7db000;
	float: right;
}

.button {
	margin: 10px auto;
	display: block;
	width: 140px;
	padding: 5px 10px;
	text-align: center;
	text-decoration: none;
	color: #555;
	border: 1px solid #ccc;
	font-size: 18px;
	background: #ddd;
	border-radius: 12px;
	-webkit-border-radius: 12px;
	-moz-border-radius: 12px;
	box-shadow: 1px 1px 2px rgba(0,0,0,.5);
	-webkit-box-shadow: 1px 1px 2px rgba(0,0,0,.5);
	-moz-box-shadow: 1px 1px 2px rgba(0,0,0,.5);
	text-shadow: #fff 0px 1px 1px;
	background: -webkit-gradient(linear, left top, left bottom, from(#eeeeee), to(#cccccc));
	background: -moz-linear-gradient(top,  #eeeeee,  #cccccc);

	}

.button:hover {
	background: #014464;
 	background: -webkit-gradient(linear, left top, left bottom, from(#cccccc), to(#999999));
	background: -moz-linear-gradient(top,  #cccccc,  #999999);
	color: #000;
	}

.button:active {
	-moz-box-shadow: 0 2px 6px black;
	-webkit-box-shadow: 0 2px 6px black;
	}

Nothing fancy here. Very basic CSS to place the elements in position.

I’ve also used a bit of CSS3 to make the buttons a little better looking.


Step 3 – Creating our catalog.xml File

As I mentioned in the earlier part, each module gets its own XML file to dictate what items to include and the general layout of the page. The page we’re building today relies on a file called catalog.xml to define its contents and structure.

This file should be present in the layout folder so let’s create an XML file and name it catalog.

The complete file for today looks like so. I’ll explain each bit part by part below.

<?xml version="1.0"?>
<layout version="0.1.0">
    <catalog_product_view translate="label">
        <label>Catalog Product View (Any)</label>
        <!-- Mage_Catalog -->
        <reference name="root">
            <action method="setTemplate"><template>page/1column.phtml</template></action>
        </reference>
		<reference name="head">
            <action method="addCss"><stylesheet>css/product.css</stylesheet></action>
        </reference>
        <reference name="content">
            <block type="catalog/product_view" name="product.info" template="catalog/product/view.phtml">
               <block type="catalog/product_view_media" name="product.info.media" as="media" template="catalog/product/view/media.phtml"/>
               <block type="catalog/product_view_description" name="product.description" as="description" template="catalog/product/view/description.phtml"/>
               <block type="catalog/product_view_type_simple" name="product.info.simple" as="product_type_data" template="catalog/product/view/type/simple.phtml"/>
               <block type="catalog/product_view" name="product.info.addtocart" as="addtocart" template="catalog/product/view/addtocart.phtml"/>
            </block>
        </reference>
    </catalog_product_view>
</layout>

Disregard the initial XML and layout version declarations. They’re of no significance to us now.

<catalog_product_view translate="label">

First, we let the system know that we’re intending to modify the product view part of the system. This is because catalog.xml houses the layout for a number of other views and so it’s imperative that we specify which view we intend to modify.

<reference name="root">
            <action method="setTemplate"><template>page/1column.phtml</template></action>
</reference>

Now, we tell Magento to load up the 1column.phtml file as the main master template for this view. This is because each individual view can use any predefined structure. For example, your home page could use a very complex custom structure, your product page a dual column and your search page a single column layout.

If nothing is specified, it’ll load up the default template mentioned in page.xml. Since we’re using it for everything else, this part is redundant but when you’re modifying this template for your personal use, editing the file’s name is a lot easier than adding chunks of XML to the layout file.

<reference name="head">
    <action method="addCss"><stylesheet>css/product.css</stylesheet></action>
</reference>

And now we come across one of the niftier parts of Magento. Yes, we can throw all our view specific CSS into one giant CSS file but we aren’t cavemen, are we? Cries of multiple HTTP requests aside, this method lets us streamline our CSS better.

First we acquire a reference to the head section of the file and then insert our page specific content into it. Here, I’m inserting a file called product.css which contains all the page specific CSS we looked at above.

Note that you aren’t limited to CSS. Including JS and other assets is possible too.

<block type="catalog/product_view" name="product.info" template="catalog/product/view.phtml">

We now ask Magento to use a specific template for the content portion of the page

 <block type="catalog/product_view_media" name="product.info.media" as="media" template="catalog/product/view/media.phtml"/>
 <block type="catalog/product_view_description" name="product.description" as="description" template="catalog/product/view/description.phtml"/>
 <block type="catalog/product_view_type_simple" name="product.info.simple" as="product_type_data" template="catalog/product/view/type/simple.phtml"/>
 <block type="catalog/product_view" name="product.info.addtocart" as="addtocart" template="catalog/product/view/addtocart.phtml"/>

This block defines all the individual blocks inside the main content block. Essentially, we use individual templates for displaying the product’s image, overview and description, availability/price and finally the add to cart functionality.

And with this, our base catalog.xml file is complete.


Step 4 – Creating our Main Template

Ok, now that we’ve specified our layout we can move on to creating the catalog/product/view.phtml file that we specified as the main template for the content section in the XML earlier.

This file is slightly different from the skeleton template we created in the last part because we’ll be adding some small things directly instead of going through the template path to avoid bloat along with a smidgen of API calls to help us.

<?php
    $_helper = $this->helper('catalog/output');
    $_product = $this->getProduct();
?>

<form action="<?php echo $this->getAddToCartUrl($_product) ?>" method="post" id="product_addtocart_form"<?php if($_product->getOptions()): ?> enctype="multipart/form-data"<?php endif; ?>>
        <div class="no-display">
            <input type="hidden" name="product" value="<?php echo $_product->getId() ?>" />
            <input type="hidden" name="related_product" id="related-products-field" value="" />
<div id="main-product-image"><?php echo $this->getChildHtml('media') ?></div>

<div id="product-details">
<?php echo $this->getChildHtml('product_type_data') ?>
<?php echo $this->getChildHtml('addtocart') ?>
</div>

<h1><?php echo $_helper->productAttribute($_product, $_product->getName(), 'name') ?></h1>

<?php if ($_product->getShortDescription()):?>
                    <h2><?php echo $this->__('Quick Overview') ?></h2>
                    <p class="quick-overview"><?php echo $_helper->productAttribute($_product, nl2br($_product->getShortDescription()), 'short_description') ?></p>
<?php endif;?>

<?php echo $this->getChildHtml('description') ?>
</form>
</div>

If you take a quick look, you’ll notice that we’re making a number of getChildHtml calls to acquire that blocks content. These work just like as expected and the contents of these contents will be covered a little later below.

<?php
    $_helper = $this->helper('catalog/output');
    $_product = $this->getProduct();
?>

At the moment, we don’t need to know what this means. In layman’s terms though, we’re essentially asking Magento for this specific product’s store information so we can process it and then display it on the page.

<form action="<?php echo $this->getAddToCartUrl($_product) ?>" method="post" id="product_addtocart_form"<?php if($_product->getOptions()): ?> enctype="multipart/form-data"<?php endif; ?>>

Here we use one of Magento’s API methods to dynamically generate the URL the form needs to post to.

<h1><?php echo $_helper->productAttribute($_product, $_product->getName(), 'name') ?></h1>

We use another of Magento’s methods to acquire the product’s title directly.

<?php if ($_product->getShortDescription()):?>
                    <h2><?php echo $this->__('Quick Overview') ?></h2>
                    <p class="quick-overview"><?php echo $_helper->productAttribute($_product, nl2br($_product->getShortDescription()), 'short_description') ?></p>
<?php endif;?>

And again, since the quick overview is going to be just a sentence or two, I’m opting to import it directly instead of using a template. We use the same function we used to acquire the title.


Step 5 – Creating the Templates for our Blocks

With all the main parts done, we can concentrate on the individual blocks now. Now that we’ve already dealt with this when constructing the main template, this should be a lot easier now.

We’ll tackle each in order of appearance in our code:

Product image

getChildHtml(‘media’) maps directly to catalog/product/view/media.phtml. Our file looks like so:

<?php
    $_product = $this->getProduct();
    $_helper = $this->helper('catalog/output');
?>

    <?php
        $_img = '<img id="image" src="'.$this->helper('catalog/image')->init($_product, 'image').'" alt="'.$this->htmlEscape($this->getImageLabel()).'" title="'.$this->htmlEscape($this->getImageLabel()).'" />';
        echo $_helper->productAttribute($_product, $_img, 'image');
?>

Some simple PHP code. We use the helper methods to acquire the product’s image and then render it on screen.

Availability/Price

getChildHtml(‘product_type_data’) maps directly to catalog/product/view/type/simple.phtml. Our file looks like so:

<?php $_product = $this->getProduct() ?>
<div id="product-availability">Availability
<?php if($_product->isSaleable()): ?>
    <span class="available"><?php echo $this->__('In stock') ?></span>
<?php else: ?>
    <span class="unavailable"><?php echo $this->__('Out of stock') ?></span>
<?php endif; ?>
</div>
<div id="product-price">Price <span><?php echo $this->getPriceHtml($_product) ?></span></div>

We first check whether the item is available or not and then output the required HTML. Acquiring the price of the product is a simple method call away!

Add to cart

getChildHtml(‘addtocart’) maps directly to catalog/product/view/addtocart.phtml. Our file looks like so:

<?php $_product = $this->getProduct() ?>

<?php if($_product->isSaleable()): ?>
        <button title="<?php echo $this->__('Add to Cart') ?>" class="button btn-cart"><?php echo $this->__('Add to Cart') ?></button>
<?php endif; ?>

As is logical, we check whether an item is up for sale before we output the required HTML. It’s a simple button as is apparent.

Product Description

getChildHtml(‘description’) maps directly to catalog/product/view/description.phtml. Our file looks like so:

<?php $_description = $this->getProduct()->getDescription(); ?>
<?php if ($_description): ?>
        <h2>Product Description</h2>
		<div class="product-description"><?php echo $this->helper('catalog/output')->productAttribute($this->getProduct(), nl2br($_description), 'description') ?></div>
<?php endif; ?>

Similar to how we included the quick overview, we use Magento’s inbuilt methods to acquire the necessary information. We do check for the description’s existence before we render it.

In case you’re curious, I typically tend to include the description separately via templates mostly because in production templates you’ll almost always be post procession the description in some way. In those scenarios, it’s easier to split this logic off onto its own template. A quick overview, on the other hand, are usually quite small and thus are included directly.


What We’ll be Building in the Next Part

.. is completely up to you. There are quite a number of views you could design but it’s rather presumptuous of me to pick one. Thus I’m asking you, the reader, to suggest a view to cover in the next part through a comment in the comments section below. Don’t forget to tell me which one to cover next!


The Last Word

And we are done! Today, we created the first view of our custom Magento theme, the product view along with looking a how Magento lets us add page specific assets and content. Hopefully this has been useful to you and you found it interesting. Since this is a rather new topic for a lot of readers I’ll be closely watching the comments section so chime in there if you’re having any doubts.

Questions? Nice things to say? Criticisms? Hit the comments section and leave me a comment. Happy coding!

June 04 2010

15:46

10 Compelling Reasons to Use Zend Framework


Trying to decide which framework to use for your new project? In this article, we’ll go in-depth about why Zend Framework should absolutely be your PHP framework of choice.


Introduction

Whether you’re starting a new project or improving an existing one, in this article, I’ve provided ten reasons why you should use Zend Framework for your next project, and hopefully, it helps you in making an informed decision.


Reason 1. Extend Classes like There’s no Tomorrow

Zend Framework is a fully object-oriented framework, and as such, it utilizes a lot of object-oriented (OO) concepts like inheritance and interfaces. This makes most, if not all, of ZF’s components extendable to some point. It allows developers to implement their own special variations of individual components without having to hack into the ZF codebase itself. Being able to customize ZF this way allows you to create functionality that is unique to your project, but because of its object-oriented nature, you’ll be able to use this functionality in other projects as well.

Example

Zend Framework has a very extensive Validation component, which you can use to validate data coming from forms. In ZF, forms are treated as objects as well, and are represented by the Zend_Form component.

Let’s assume that you want to create a custom URL validator to restrict URL input from the user. The quickest way to do this would be to just validate the input using something like:

$isValid = filter_var($submitted_url, FILTER_VALIDATE_URL);

But this won’t adhere to the OO nature of form objects, since it’s not used within the context of the form. To solve this, we can create a new Zend_Validator class by extending the Zend_Validate_Abstract class:

<?php
class Zend_Validate_Url extends Zend_Validate_Abstract
{
	const INVALID_URL = 'invalidUrl';

	protected $_messageTemplates = array(
		self::INVALID_URL   => "'%value%' is not a valid URL.",
	);

	public function isValid($value)
	{
		$valueString = (string) $value;
		$this->_setValue($valueString);

		if (!Zend_Uri::check($value)) {
			$this->_error(self::INVALID_URL);
			return false;
		}
		return true;
	}
}

This actually uses the Zend_Uri class, which already has a URL checking method we can use. But since it doesn’t extend the Zend_Validate_Abstract class, we implemented a wrapping class which does implement the needed abstract class. This lets us use the Zend_Uri URL checking function in our Zend_Form objects like so:

<?php
class Form_Site extends Zend_Form
{
	public function init()
	{
		$this->setMethod('POST');
		$this->setAction('/index');

		$site= $this->createElement('text', 'siteurl');
		$site->setLabel('Site URL');
		$site->setRequired(true);

		//  Adding the custom validator here!
		$site->addValidator(new Zend_Validate_Url());
		$this->addElement($site);
		$this->addElement('submit', 'sitesubmit', array('label' => 'Submit'));
	}

}

If we wanted to check that our URLs are valid YouTube video URLs, we could do something like this:

<?php
class Zend_Validate_YouTubeUrl extends Zend_Validate_Abstract
{
	const INVALID_URL = 'invalidUrl';

	protected $_messageTemplates = array(
		self::INVALID_URL   => "'%value%' is not a valid URL.",
	);

	public function isValid($value)
	{
		$valueString = (string) $value;
		$this->_setValue($valueString);

		if (strpos($value, "http://www.youtube.com/watch?v=") !== 0) {
			$this->_error(self::INVALID_URL);
			return false;
		}
		return true;
	}
}

If we added this to our site form object as a validator, it would ensure that all URLs submitted begin with the correct YouTube video URL prefix.


Reason 2. Object-oriented Goodness


Image courtesy of http://www.developer.com

In Zend Framework, everything is an object, as proven by our example above. This poses its own disadvantages, such as making things more complicated to code. Its main advantage, though, is the ability to make code reusable, and since nobody likes to repeat themselves, this is a very good thing.

Example

We already have our Zend_Validate_Url and Form_Site class from our example above, so let’s reuse them in this example.

<?php

class IndexController extends Zend_Controller_Action
{

    public function indexAction()
    {
		$siteform = new Form_Site();

		if( $this->_request->isPost() && $siteform->isValid($this->_request->getPost()) ) {
			//stuff to do if the input is correct
			$this->_redirect("/index/correct");
		}
		$this->view->siteform = $siteform;
    }

    public function correctAction()
    {
		// Yay, we're re-using our Form_Site object!
		$this->view->siteform = new Form_Site();
    }
}

Here’s what it would look like on a browser:

If you tried to submit an invalid URL, you can see our URL validator at work:

Here, you can see what would happen if you did input a valid URL:

As you can see, we’ve never had to repeat our form object code.

“Zend_Validate classes can be used in other ways as well, not only within the context of Zend_Form classes. You simply instantiate a Zend_Validate class and call the isValid($parameter) method, passing it the value you want to validate.”


Reason 3. Use What you Need, Forget Everything Else

Image courtesy of http://dev.juokaz.com

By design, Zend Framework is simply a collection of classes. Normally, you’ll use Zend MVC components to create a fully-functional ZF project, but in any other case, you can just load the components you need. ZF is very decoupled, which means we can take advantage of the components as individual libraries, instead of the framework as a whole.

If you’ve been looking at other framework articles, you’ve probably heard of the term glue framework. ZF, by default, is a glue framework. Its decoupled nature makes it easy to use as “glue” to your already existing application.

There’s a debate between using glue frameworks vs. full-stack frameworks. Full-stack frameworks are those that provide you everything you need to create your project, like ORM implementations, code-generation, or scaffolding. Full-stack frameworks require the least amount of effort to create a project, but fall short in terms of flexibility, since it imposes strict conventions on your project.

Example

Let’s say you need a way to retrieve information about a specific video on YouTube. Zend_Gdata_Youtube is a ZF component which allows you to access data from YouTube via the GData API. Retrieving the video information is as simple as:

//Make sure you load the Zend_Gdata_Youtube class, this assume ZF is in your PHP's include_path
include_once "Zend/Gdata/Youtube.php";

$yt = new Zend_Gdata_YouTube();
// getVideoEntry takes in the YouTube video ID, which is usually the letters at the end
// of a YouTube URL e.g. http://www.youtube.com/watch?v=usJhvgWqJY4
$videoEntry = $yt->getVideoEntry('usJhvgWqJY4');
echo 'Video: ' . $videoEntry->getVideoTitle() . "<br />";
echo 'Video ID: ' . $videoEntry->getVideoId() . "<br />";
echo 'Updated: ' . $videoEntry->getUpdated() . "<br />";
echo 'Description: ' . $videoEntry->getVideoDescription() . "<br />";
echo 'Category: ' . $videoEntry->getVideoCategory() . "<br />";
echo 'Tags: ' . implode(", ", $videoEntry->getVideoTags()) . "<br />";
echo 'Watch page: ' . $videoEntry->getVideoWatchPageUrl() . "<br />";
echo 'Flash Player Url: ' . $videoEntry->getFlashPlayerUrl() . "<br />";
echo 'Duration: ' . $videoEntry->getVideoDuration() . "<br />";
echo 'View count: ' . $videoEntry->getVideoViewCount() . "<br />";

Code sample courtesy of Google Developer’s Guide

This code would output:

One thing to note here: this Zend Framework component (GData) is the official PHP library endorsed by Google to access its API. The framework’s decoupled nature allows us to use the component in any project, regardless of the framework we used to build it.


Reason 4. It lets you do a Lot of Things!

One of the things I love most about Zend Framework is that it has A LOT of components. Need a way to authenticate a user? Use Zend_Auth. Need to control access to your resources? Look up Zend_Acl. Need to create some forms? We have Zend_Form. Need to read an RSS feed? You can use Zend_Feed for that. It’s basically the Swiss Army knife of PHP classes!

Zend actually comes with some demos that show how to use its different components:

To view these, the best way is to simply download the Full Package Version of Zend Framework and test them out on your machine.

For a complete list of all the components, you can check out the Zend Framework Manual.


Reason 5. No Model Implementation – Choose your Own Adventure!

Image courtesy of http://www.vintagecomputing.com

This is actually one of the reasons most developers don’t use Zend Framework – it has no Model implementation on its own. For those who don’t know what a Model is, it’s the M in MVC, which stands for “Model-View-Controller”, a programming architecture that’s used by most PHP Frameworks.

Does that mean that Zend Framework is only a “VC” Framework?

Yes, and no.

Yes, it’s a VC framework because it doesn’t have its own Model implementation. This makes it hard for some people to use ZF, especially if they’re coming from a framework which does have a Model implementation (like CakePHP, Symfony, or even Ruby on Rails).

On the other hand, no, it’s an MVC framework as well, since apart from providing the generic ways to access the database (using Zend_Db), it actually still relies on some sort of Model implementation. What it does differently is that it leaves this kind of implementation up to the developer ñ which some say should be the case since models are actually where the business logic of the application resides, and therefore, they’re not something which can be developed as a generic component. Zend Framework Philosophy states that model implementations are unique to the projectóit’s impossible to create an abstract implementation of it since they don’t really know what you need. They believe that models should be implemented by the developers themselves.

How is this a good thing?

Not having a Model implementation means that the developer is free to use whatever means he has to implement it, or even just integrate existing implementations. Being free of predefined restraints, the developer is then allowed to create more complex implementations, rather than just simple representations of tables, which is how usual Model implementations are created. Models contain your business logic. They should not be restrained by your database tables; rather, they should dictate the connections of these tables to one another. This lets you put most of your programming code in your Models, therefore satisfying the “Thin Controllers, Fat Models” paradigm of MVC.

So how will I use Zend Framework if I have no idea how to create my own models?

For beginners, the Zend Framework Quickstart tutorial shows us a good way to implement models. In the tutorial, they implement an ORM approach to the models, wherein you would create three filesóthe actual Model, which is an abstract representation of your object; a Mapper, which maps data from the database to your Model; and a Database Table object, which is used by the mapper to get the data. You can check out the code in the ZF Quickstart tutorial, where they used this approach to implement the model of a simple Guestbook application.

For those asking “Why do I have to code this myself while other frameworks do the work for me?”, this is a perfect segue to my next reason…


Reason 6. Integrate with Whatever you Want!

Zend Framework’s decoupled nature makes it very easy to integrate other libraries that you want to use. Let’s say you want to use Smarty as your templating system. It can be done by simply creating a wrapping class for Zend_View_Abstract, which uses a Smarty instance to render the view.

This works both ways, as you can integrate ZF into other libraries as well. For example, you can integrate ZF into Symfony. They’re planning to do this with Symfony 2, using the Zend_Cache and Zend_Log components from ZF.

Example

For our example, we’ll try using Doctrine to implement our Model. Continuing from our site example above, say you’ve already implemented your DB table like so:

CREATE TABLE  `site` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `url` varchar(100) CHARACTER SET latin1 NOT NULL,
  PRIMARY KEY (`id`)
);

To integrate Doctrine into ZF, we’ll have to make sure that the proper settings are defined. I’ve followed this tutorial from dev.juokaz.com about using Doctrine with ZF.

Assuming everything works out, you’ll just have to generate your model files by running the doctrine-cli.php php file from the tutorial like so:

php doctrine-cli.php generate-models-db

You should see this success message:

Afterwards, you can check the folder which you set as the place to store the generate Model classes.

Then in our controller class, we can simply use our site model class.

<?php

class IndexController extends Zend_Controller_Action
{

    public function indexAction()
    {
		$siteform = new Form_Site();

		if( $this->_request->isPost() && $siteform->isValid($this->_request->getPost()) ) {
			//stuff to do if the input is correct
			$site = new Model_Site();
			$site->url = $this->_request->getParam('siteurl');
			$site->save();
			//redirect to our success page
			$this->_redirect("/index/correct");
		}
		$this->view->siteform = $siteform;
    }

    public function correctAction()
    {
		// Yay, we're re-using our Form_Site object!
		$this->view->siteform = new Form_Site();
    }
}

If we check our sites table, we’ll see that our records is there

Now, every time we submit a site, our controller will use our Doctrine model implementation to save to our database. Isn’t that nice and easy? Setup may be a bit complicated, but on the plus side, our project is now able to take advantage of a tool which has been developed specifically for Model implementation. Our project now has the power of two very developed technologies behind it.


Reason 7. Guidelines and Standards

Zend Framework is developed in conjunction with a very extensive Contributor Guide, which basically states that:

  1. Every contributor for both documentation and/or code, at any level (either a few lines of code, a patch, or even a new component) must sign a Contribute License Agreement (CLA).
  2. Code MUST be tested and covered by a unit test using PHPUnit. And…
  3. Code must adhere to strict Coding Standards

These strict guidelines ensure that you only use readable, high-quality code that has been tested thoroughly.


Reason 8. All Code is Guilty Until Proven Innocent (aka Test-Driven Development)

Image courtesy of http://www.codesmack.com/

Test-driven development is a programming technique that requires a developer to write tests for the function he is supposed to code before writing code for the function itself. By writing the tests first, it ensures that the programmer:

  1. Thinks of the possible use-cases of his code
  2. Creates a whitelist of input and output
  3. Makes it easier to refactor his code
  4. Makes it easier to pass code from one person to another

The test-driven development Cycle
Image courtesy of Wikipedia

Zend Framework makes it easy to do TDD via Zend_Test, which uses PHPUnit, a popular unit testing framework. PHPUnit lets you test not only your Controllers, but also your library and model functions. To add to this, Zend_Tool, which is Zend Framework’s scaffolding utility, already makes provisions for PHPUnit when you use it to create your project.

Integrating PHPUnit and Zend Framework

Setting up Zend Framework and PHPUnit is not that difficult. Basically, once you’re done with it, you’ll be able to use the same setup for your future projects. Just as a side note, the following steps assume that you’ve used Zend_Tool to scaffold your project structure and files, but it should be relatively simple to change for other setups.

First, we need to install PHPUnit. The best way is to install it via PEAR:

pear channel-discover pear.phpunit.de
pear install phpunit/PHPUnit

Afterward, we’ll open our phpunit.xml, an XML file generated by Zend_Tool. You’ll find it inside the tests folder in the root directory of your project. Add the following lines:

<phpunit bootstrap="./TestHelper.php" colors="true">
    <testsuite name="Zend Framework Unit Testing">
        <directory>.</directory>
    </testsuite>

    <filter>
        <whitelist>
            <directory suffix=".php">../library</directory>
            <directory suffix=".php">../application</directory>
            <exclude>
                <directory suffix=".phtml">../application</directory>
            </exclude>
        </whitelist>
    </filter>
</phpunit>

After saving phpunit.xml, create a new file inside the same folder as phpunit.xml called TestHelper.php. This PHP file will help us setup the environment for our tests.

<?php
// start output buffering
ob_start();

// set our app paths and environments
define('BASE_PATH', realpath(dirname(__FILE__) . '/../'));
define('APPLICATION_PATH', BASE_PATH . '/application');
define('APPLICATION_ENV', 'testing');

// Include path
set_include_path(
    '.'
    . PATH_SEPARATOR . BASE_PATH . '/library'
    . PATH_SEPARATOR . get_include_path()
);

// We wanna catch all errors en strict warnings
error_reporting(E_ALL|E_STRICT);

require_once 'ControllerTestCase.php';

We then create our parent Controller Test Case class, which all of our Controllers will extend. This will help implement methods which would usually be the same throughout all of the Controller test classes.

<?php
require_once 'Zend/Application.php';
require_once 'Zend/Test/PHPUnit/ControllerTestCase.php';

abstract class ControllerTestCase extends Zend_Test_PHPUnit_ControllerTestCase
{
    public $application;

    public function setUp()
    {
        $this->application = new Zend_Application(
            APPLICATION_ENV,
            APPLICATION_PATH . '/configs/application.ini'
        );

        $this->bootstrap = $this->application;
        parent::setUp();
    }

    public function tearDown()
    {
        $this->resetRequest();
        $this->resetResponse();
        parent::tearDown();
    }
}

Lastly, we create a controller test class:

<?php
require_once realpath(dirname(__FILE__) . '/../../ControllerTestCase.php');

class IndexControllerTest extends ControllerTestCase
{
    public function testCallingRootTriggersIndex()
    {
        $this->dispatch('/');
        $this->assertController('index');
        $this->assertAction('index');
    }

    public function testCallingBogusTriggersError()
    {
        $this->dispatch('/bogus');
        $this->assertController('error');
        $this->assertAction('error');
        $this->assertResponseCode(404);
    }
}

All that’s left is to run our test. Open your command prompt and go to the tests folder and type:

phpunit

Your command line should output the following:


PHPUnit and Zend Framework setup tutorial and code courtesy of http://www.dragonbe.com

Reason 9. Community and Documentation

Due to its multiple components, complexity, and fully object-oriented approach, Zend Framework has a very steep learning curve. It becomes easier to learn due to the comprehensiveness of its documentation and its thriving community. First of all, the Zend Framework Programmer’s Reference Guide boasts a complete guide to all ZF components, with examples, code, and usage theories.

Aside from this, there are a lot of blogs out there that share Zend Framework tips and tricks. For example, Phly, boy, phly, the blog of Matthew Weier O’Phinney, a Core Contributor to Zend Framework, provides a lot of insights, clever uses, and component explanations for Zend Framework. Zend also has a site called Zend Developer Zone, which aside from publishing tutorials for Zend Framework, has stuff like Zend Framework Webinars, podcasts, and articles about PHP in general. Another site, called Zend Casts, offers a lot of useful video tutorials on different Zend Framework components as well. Last but not least, there’s a free online book called Zend Framework: Surviving the Deep End” written by P·draic Brady, another Zend Framework contributor.

As you can see, there is no lack of support from the community, the documentation, and the developers. If you have any questions or need any clarifications, a quick search with the right keywords should almost always give you relevant results. If not, there’s still the Zend Framework Mailing Lists, the official Zend Framework Forums, the unofficial Zend Framework Forums or the unofficial Zend Framework IRC channel


Reason 10. Certifications Ahoy!

If you’re still unconvinced about learning and using Zend Framework, this reason is actually the one that I feel most distinguishes Zend Framework from all the others. Zend not only offers Zend Framework Certification, but PHP Certification as well. By offering certifications, Zend helps you use your expertise in PHP and Zend Framework to boost your portfolio or CV. The Zend Certification site lists a number of reasons to get certified, some of which are:

  1. Differentiate yourself from competitors when looking for a new job
  2. Get your resume/CV noticed
  3. Have your profile displayed in Zend’s Yellow Pages for PHP Professionals
  4. Be part of the Linkedin Group Exclusively for ZCE’s
  5. Get special discounts on Zend PHP conferences worldwide

Addendum

Just to keep things balanced, here’s a quick list of reasons why you might not want to use Zend Framework:

  1. VERY steep learning curve. It’s not very hard for advanced PHP users, but for beginners, there’s a lot to learn!
  2. Big footprint. Since Zend Framework has a lot of components, it’s total size is relatively higher than other Frameworks. For example, CodeIgniter’s system folder has a 1.5MB footprint compared to Zend Framework’s 28MB footprint.
  3. No solid scaffolding tool. Although Zend_Tool offers some functionality, it’s not much compared with the scaffolding utilities of full-stack frameworks like CakePHP or Symfony.
  4. Not shared webhosting friendly. The folder structure generated by Zend_Tool suggests that the public folder be the only directory accessible via http ó which assumes that a user is able to create a virtual host for the project. This is something you aren’t able to do in most shared web hosting environments.
  5. Too gluey. Since everything is in separate classes, it’s sometimes hard to envision how everything works. This wouldn’t be a problem with full-stack frameworks, since they mostly take care of everything for you. Without Zend_Tool, it would be extremely difficult to set up a working project structure

Conclusion

A lot of advancements have been made in the world of PHP Frameworks in the past few years. I’ll be honest, there’s a whole lot of fish in the sea. There are frameworks like Codeigniter, CakePHPand Symfony which are also good to use. Some of you might be thinking, “Why did this guy focus on Zend Framework? Why didn’t he run performance tests to compare the different frameworks?” To that, I reply with this quote from a very entertaining article entitled “PHP Framework Benchmarks: Entertaining But Ultimately Useless” by P·draic Brady:

To create a positive benchmark, you need to understand that all frameworks were born as festering piles of unoptimised stinking crap. They were all born bad and get worse with age. This sounds quite sad, but actually it’s an inevitable compromise between performance and features. It’s also a compromise between performance and ease-of-use. So you see, performance is unfairly faced by two opponents: features and ease-of-use. All performance is sacrificed in the name of serving the needs of rapid development, flexibility, prototyping, and making your source code look prettier than the other guy’s. As if.

What happens if you move away from the enemies of performance and do some propping up behind the scenes? You get…wait for it…oodles of extra performance!

When it comes down to it, which framework you choose to use really depends on how comfortable you are with it. None of these frameworks would be of help to you if you aren’t able to take full advantage of their features. You can spend weeks or even months using a specific framework, only to trash it in the end because you just aren’t as comfortable using it as you were with some other framework.

So I invite you to at least try out Zend. If it works for you, that’s great! It does for me. But if it’s not a good fit, go ahead and try out the others. Soon enough, you’ll find the framework that’s just right for your needs.

April 02 2010

14:58

CodeIgniter from Scratch: Extending the Framework

In today’s screencast we are going to learn how to create our own helpers and libraries. We are also going to learn how to extend the existing helpers and libraries that come with CodeIgniter. At the end of the tutorial we will extend the Form Validation library to give it the ability to check for password strength.


Catch Up


Day 13: Extending the Framework


March 25 2010

18:30

CodeIgniter from Scratch: Shopping Cart

Today, we are going to take a look at the Shopping Cart library for CodeIgniter. This useful class allows us to add and remove items to a shopping cart, update them, and calculate prices. I will demonstrate how you can build a simple shopping cart system with the help of this library..


Catch Up


Day 12: Shopping Cart

Final Project


March 11 2010

22:47

CodeIgniter from Scratch: File Operations

In today’s episode, we are going to be working with several helper functions, related to files, directories, and downloads. We are going to learn how to read, write, download files, and retrieve information about both files and directories. Also at the end we will build a small file browser that utilizes jQuery as well.

Catch Up

Day 11: File Operations

Get the Flash Player to see this player.

Final Example


February 26 2010

18:03

CodeIgniter from Scratch: The Calendar Library

In this tenth episode of the CodeIgniter From Scratch screencast series, we will be exploring the Calendar library. We are also going to utilize the database class and jQuery AJAX. I will show you how to build a simple and CSS-styled calendar page, which will have the ability to store and display content for each day.

Final Example

Catch Up

Day 10: The Calendar Library

Other Viewing Options


February 19 2010

16:58

CodeIgniter from Scratch: File Uploading and Image Manipulation

In lesson nine of our CodeIgniter series, we’ll build a small image gallery that allows you to upload files, and automatically create thumbnails.

Final Example

Catch Up

Day 9: File Uploading and Image Manipulation

Other Viewing Options


February 11 2010

15:30

CodeIgniter from Scratch: Day 8 – AJAX

The CodeIgniter from Scratch series was unexpectedly, and significantly popular. Today, I’m pleased to announce that, with the help of one of my best authors, Burak, we’ll be continuing the series! Additionally, the most often requested topic is the subject for today’s screencast: combining CodeIgniter and jQuery.

Remember, it is not required that you watch the previous lessons in order to understand today’s screencast. It’s a self-contained article. However, if you’re new to CodeIgniter, we do recommend that you start from the beginning!

Catch Up

Day 8: AJAX

Other Viewing Options

February 04 2010

13:47

Working with RESTful Services in CodeIgniter

CodeIgniter is becoming well known for its power as a PHP based web application framework, but it’s not often we see examples of it being used for anything else. Today we’ll learn how we can use CodeIgniter to create a RESTful API for your existing web applications and demonstrate how to interact with your own API or other RESTful web-services such as Facebook and Twitter.

Tutorial Details

Introduction

If you have been following the CodeIgniter From Scratch series you will know by now that it is relatively quick and easy to put together simple web applications, such as blogs, CMS systems, brochure sites, etc. One thing you may not have thought about is using CodeIgniter to create an interactive API. After trying several existing REST implementations, I found they not only lacked simplicity but were missing most of the features you would expect from a RESTful implementation; so I built my own. This tutorial will show you how to use this code to set up your REST API, and gives example of how to interact with it from your web application.

Assumptions

  1. You have a web server set up, locally or online and known how to manage files on it.
  2. You have read a few of the CodeIgniter from Scratch tutorials.
  3. You know how to set up CodeIgniter.
  4. You know a little about RESTful services.

This tutorial is broken down into two parts. We will start by learning how to create a RESTful service, then further down we will learn how to interact with it in a few different ways.

Part 1 – Creating a RESTful API

Step 1: Setting up the Demo

Firstly you need to download the codeigniter-restserver code from GitHub and extract it and move the code to your server.

When you open the folder, you will see an entire CodeIgniter install, which is there to power the demo. This allows people to have a play with the REST demo before integrating with your existing application.

Open up “application/config/config.php” and set the base_url to get links working. This base_url will be different for everyone and depends entirely on where you uploaded your files.

Step 2: The URLs

With the files extracted and the base_url set, we are ready to load up our RESTful CodeIgniter install and have a look at the demo supplied with it. Browse the base URL, which by default is:

http://localhost/restserver

Here you will find a few example links to the example_api controller which can be found at “application/controllers/example_api.php”. Let’s disect the URL’s of these examples to see what is going on. The first URL is a very simple one.

This URL looks very much like any other CodeIgniter URL with a controller and a method, but you will notice in this diagram the method is named a “Resource”. REST is all about Resources and they are essentially a noun within your application, which are interacted with (i.e added, deleted, edited, queried) based on HTTP headers and URL query strings or HTTP arguments.

The default format for output is XML which is what we see in this basic example. The other links are slightly larger and demonstrate how to pass parameters and show how the output format can be modified in the URL:

Normally in CodeIgniter you just pass in parameter values, but a REST controller accepts any number of parameters in any order. For this to work, we need to pass in the name of the parameter followed by the value in pairs.

At the end of the URL is the “format” parameter. This is a reserved parameter that will modify the output format of the requested data like so:

By giving both the API developer and the client application the choice of data formats to use, the API is opened up to a much wider audience and can be used with more programming languages and systems. These three are not the only formats supported, out of the box your REST API can use:

  • xml – almost any programming language can read XML
  • json – useful for JavaScript and increasingly PHP apps.
  • csv – open with spreadsheet programs
  • html – a simple HTML table
  • php – Representation of PHP code that can be eval()’ed
  • serialize – Serialized data that can be unserialized in PHP

While adding the format to the URL is not technically the most RESTful way to change formats, it allows for easy browser testing and lets developers without cURL perform simple GET requests on the API. The more RESTful way is to send a Content-type HTTP header to the REST controller using cURL, but that will be explained later.

Step 3: The Code

Now if you open up application/controllers/example_api.php you will immediatley spot a few differences from normal CodeIgniter controllers.

REST_Controller

In the MVC pattern, a controller is the central point of the logic. It is called when a user makes a request and then based on the logic in the controller it fetches data and outputs views. CodeIgniter contains its own logic for how a Controller should work, but as we are doing something different we need our own REST_Controller library to contain its own REST related logic. So instead of simply using:

<?php
class Example_api extends Controller {

}

…you will need to use:

<?php
require(APPPATH'.libraries/REST_Controller.php');

class Example_api extends REST_Controller {

}

Working with Resources

Now your empty controller is set up, next are the methods or “resources”. This is prossibly the most confusing part of the tutorial if you are used to how CodeIgniter works. Basically, you take the Resource and the HTTP verb and combine them to make a method name. So the two examples we looked at before had a Resource of user and users. Because both of these were loaded in the browser, we know it was using a GET request and so the two methods below are used:

<?php
require(APPPATH'.libraries/REST_Controller.php');

class Example_api extends REST_Controller {

    function user_get()
    {
		// respond with information about a user
    }

    function users_get()
    {
		// respond with information about several users
    }
}

This may seem a little strange, but it gives you the ability to use the same URL and respond to the request depending on the HTTP verb that has been used. If somebody tries to access your API in a way that is not allowed (in this example PUT or DELETE) it will simply respond with a 404. If you aren’t sure about HTTP verbs, let me explain.

GET

Used to fetch information about an existing resource. This is used by browsers when you enter a URL and hit go, or when you click on a link, so it perfect for fetching information on one of your REST resources (like user).

POST

Used to update an existing resource with information. Browsers use this to submit most types of forms on the internet, although some use GET as well by submitting the form action with a query string containing the field data.

PUT

Less commonly used and not supported by most browsers, PUT is used to create a new resource.

DELETE

Also not used by many browsers, this HTTP verb rather obviously is used to delete a resource.

If we put that into code and allow each verb on the resource user it would look like this:

<?php
require(APPPATH'.libraries/REST_Controller.php');

class Example_api extends REST_Controller {

    function user_get()
    {
		// respond with information about a user
    }

    function user_put()
    {
		// create a new user and respond with a status/errors
    }

    function user_post()
    {
		// update an existing user and respond with a status/errors
    }

    function user_delete()
    {
		// delete a user and respond with a status/errors
    }
}

Accessing parameters and returning data

SO now the API has been given its structure by setting up the resources and defining a method for each HTTP verb we wish to support, we need parameters so we can use our CodeIgniter models and libraries. This is one of the major benefits of using CodeIgniter for our API, as we can use our existing models and libraries and not have to re-code them.

<?php
require(APPPATH'.libraries/REST_Controller.php');

class Example_api extends REST_Controller {

    function user_get()
    {
		$data = array('returned: '. $this->get('id'));
		$this->response($data);
    }

    function user_post()
    {
		$data = array('returned: '. $this->post('id'));
		$this->response($data);
    }

    function user_put()
    {
		$data = array('returned: '. $this->put('id'));
		$this->response($data;
    }

    function user_delete()
    {
		$data = array('returned: '. $this->delete('id'));
		$this->response($data);
    }
}

This example contains five new pieces of code:

$this->get()

Is used to return GET variables from either a query string like this index.php/example_api/user?id=1 or can be set in the more CodeIgniter’esque way with index.php/example_api/user/id/1.

$this->post()

Is an alias for $this->input->post() which is CodeIgniter’s method to access $_POST variables with XSS protection.

$this->put()

Reads in PUT arguments set in the HTTP headers or via cURL.

$this->delete()

You guessed it, this reads in DELETE arguments, also set in HTTP headers or via cURL.

$this->response()

Sends data to the browser in whichever data format has been requested, or defaults to XML. You can optionally pass a HTTP status code to show it has worked or failed. E.g if the ID provided was not in the database, you could use $this->response(array(‘error’ => ‘User not found.’), 404);

Step 4: Working with your Models

Until now we have been working with an example API in a clean install, so the next step is to get a REST API running from your existing codebase.

Although the download comes with a full CodeIgniter installation for the demo and to allow API’s to be built from scratch, the only two files of importance are:

  1. application/config/rest.php
  2. application/libraries/REST_Controller.php

Drop those two files into your CodeIgniter application and create a new API controller.

<?php
require(APPPATH.'/libraries/REST_Controller.php');

class Api extends REST_Controller
{
	function user_get()
    {
        if(!$this->get('id'))
        {
        	$this->response(NULL, 400);
        }

        $user = $this->user_model->get( $this->get('id') );

        if($user)
        {
            $this->response($user, 200); // 200 being the HTTP response code
        }

        else
        {
            $this->response(NULL, 404);
        }
    }

    function user_post()
    {
        $result = $this->user_model->update( $this->post('id'), array(
        	'name' => $this->post('name'),
        	'email' => $this->post('email')
        ));

        if($result === FALSE)
        {
        	$this->response(array('status' => 'failed'));
        }

        else
        {
        	$this->response(array('status' => 'success'));
        }

    }

    function users_get()
    {
        $users = $this->user_model->get_all();

        if($users)
        {
            $this->response($users, 200);
        }

        else
        {
            $this->response(NULL, 404);
        }
    }
}
?>

This shows an example API with some generic model names. In the first method we are picking up a ?id=XX and passing it to the model. If data is found we send it to the $this->response() function with a status 200. If nothing is found, return no body and a 404 to say nothing was found. You can imagine how this could be expanded to run all sorts of API activities for your web application.

Step 5: Securing the API

Now your API is built it needs securing so only users given access can interact with the API. To set the login type, usernames and passwords open up “application/config/rest.php” inside your codebase.

/*
|--------------------------------------------------------------------------
| REST Login
|--------------------------------------------------------------------------
|
| Is login required and if so, which type of login?
|
|	'' = no login required, 'basic' = relatively secure login, 'digest' = secure login
|
*/
$config['rest_auth'] = 'basic';

None

Anyone can interact with any one of of your API controllers.

Basic

A relatively insecure login method which should only be used on internal/secure networks.

Digest

A much more secure login method which encrypts usernames and password. If you wish to have a protected API which anyone could get at, use digest.

/*
|--------------------------------------------------------------------------
| REST Login usernames
|--------------------------------------------------------------------------
|
| Array of usernames and passwords for login
|
|	array('admin' => '1234')
|
*/
$config['rest_valid_logins'] = array('admin' => '1234');

Setting up the users is simple. Each login is an array item, with a key and a value. The key is the username and the value is the password. Add as many as you like to this array and dish them out to anyone who will be using the API.

Part 2 – Interacting with RESTful Services

Wether it is the API you have just buit or a public service such as Twitter, you will want to be able to interact with it somehow. Seeing as RESTful services work with basic HTTP requests it is very easy to do this in a number of different ways.

Different Methods to Interact with REST

Each of these different interaction methods will be shown with the code placed directly in Controller methods. This is purely so the demos are easier to read and would normally would be placed inside a model or a library for correct MVC separation.

file_get_contents()

Using the very simple PHP function file_get_contents(), you can perform a basic GET request. This is the most basic of all the methods but is worth mentioning for those “quick and dirty” moments.

$user = json_decode(
	file_get_contents('http://example.com/index.php/api/user/id/1/format/json')
);

echo $user->name;

Worth noting is that, while this method will not work using HTTP Digest authentication, if you are using HTTP Basic authentication you can use the following syntax to get data from your password protected RESTful API:

$user = json_decode(
	file_get_contents('http://admin:1234@example.com/index.php/api/user/id/1/format/json')
);

echo $user->name;

There are a few problems to using this method: the only way to set extra HTTP headers is to set them manually using the PHP function stream_context_create() which can be very complicated for developers new to the internal workings of HTTP requests. Another disadvantage is you only receive the body of the HTTP response in its raw format, which means you need to handle conversion from very single request.

cURL

cURL is the most flexible way to interact with a REST API as it was designed for exactly this sort of thing. You can set HTTP headers, HTTP parameters and plenty more. Here is an example of how to update a user with our example_api and cURL to make a POST request:


    function native_curl($new_name, $new_email)
    {
	    $username = 'admin';
		$password = '1234';

		// Alternative JSON version
		// $url = 'http://twitter.com/statuses/update.json';
		// Set up and execute the curl process
		$curl_handle = curl_init();
		curl_setopt($curl_handle, CURLOPT_URL, 'http://localhost/restserver/index.php/example_api/user/id/1/format/json');
		curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, 1);
		curl_setopt($curl_handle, CURLOPT_POST, 1);
		curl_setopt($curl_handle, CURLOPT_POSTFIELDS, array(
			'name' => $new_name,
			'email' => $new_email
		));

		// Optional, delete this line if your API is open
		curl_setopt($curl_handle, CURLOPT_USERPWD, $username . ':' . $password);

		$buffer = curl_exec($curl_handle);
		curl_close($curl_handle);

		$result = json_decode($buffer);

		if(isset($result->status) && $result->status == 'success')
		{
			echo 'User has been updated.';
		}

		else
		{
			echo 'Something has gone wrong';
		}
    }

Interacting with your API this way works fine but there are two problems with this method:

  1. It uses an ugly confusing syntax – imagine creating several an application based on that.
  2. cURL is not installed on all servers by default.

To solve this ugly syntax, a cURL library has been developed for CodeIgniter which simplifies things immensely.

The exact same request made with the cURL library would look like this:

    function ci_curl($new_name, $new_email)
    {
	    $username = 'admin';
		$password = '1234';

		$this->load->library('curl');

		$this->curl->create('http://localhost/restserver/index.php/example_api/user/id/1/format/json');

		// Optional, delete this line if your API is open
		$this->curl->http_login($username, $password);

		$this->curl->post(array(
			'name' => $new_name,
			'email' => $new_email
		));

		$result = json_decode($this->curl->execute());

		if(isset($result->status) && $result->status == 'success')
		{
			echo 'User has been updated.';
		}

		else
		{
			echo 'Something has gone wrong';
		}
    }

Much nicer to look at right? Well there is an even easier method to work with REST in your CodeIgniter applications that this.

REST client library

A REST client library has been developed that sits on top of this cURL library which handles format conversion, HTTP logins and several other aspects of your REST API.

    function rest_client_example($id)
    {
		$this->load->library('rest', array(
			'server' => 'http://localhost/restserver/index.php/example_api/',
			'http_user' => 'admin',
			'http_pass' => '1234',
			'http_auth' => 'basic' // or 'digest'
		));

        $user = $this->rest->get('user', array('id' => $id), 'json');

        echo $user->name;
    }

Here you can see we are making a GET request, sending id as a parameter and telling the library we want ‘json’ as the content format. This handles the setting of Content-type for you, and converts the data into a PHP object for you. You can change this value to ‘xml’, ‘json’, ’serialize’, ‘php’, ‘csv’ or any custom MIME-type you like, for example:

	$user = $this->rest->get('user', array('id' => $id), 'application/json');

As you have probably guessed as well as $this->rest->get(), the library also supports $this->rest->post(), $this->rest->put(), $this->rest->delete() to match all of your REST_Controller methods.

You will need to var_dump() results coming from the REST client library to make sure you are getting the right data format back. The conversion will somtimes be array and sometimes be an object, depending on how it is converted by PHP. If the returned MIME-type is not supported then it will simply return the format as plain-text.

Talking to Twitter

Using this REST library you can talk other RESTful services such as Twitter and Facebook. Here is a simple example of how you can get details for a specfic user based on their ID, using Twitter’s default format XML.

        $this->load->library('rest', array('server' => 'http://twitter.com/'));

        $user = $this->rest->get('users/show', array('screen_name' => 'philsturgeon'));
        $this->load->library('rest', array(
        	'server' => 'http://twitter.com/',
			'http_user' => 'username',
			'http_pass' => 'password',
			'http_auth' => 'basic'
        ));

        $user = $this->rest->post('statuses/update.json', array('status' => 'Using the REST client to do stuff'));

Looking at this, you will notice that interacting with the Twitter API is a bit different in a few ways.

  1. They support URL based format switching in the form of .json instead of /format/json. Some require an extention, some do not so it’s best to always add them on.
  2. They mostly only support GET/POST, but are starting to add more DELETE methods
  3. They dont always have just a resource in their URL, for example: users/search is one REST method, but lists is another.

Keep an eye out for these differences as they can catch you out. If you get stuck, simply echo $this->rest->debug() for a whole range of information on your REST request.

Summary

Combining what you now know about RESTful services, the CodeIgniter REST client library and the Twitter API documentation – or any other RESTful API documentation for that matter – you can create some very powerful applications that integrate with any custom or public web service using REST. You can extend your API by creating more REST_Controller’s and even make a modular API by using Matchbox or Modular Separation to create an api.php controller for each module to help keep your API as neatly organized as your application.

Write a Plus Tutorial

Did you know that you can earn up to $600 for writing a PLUS tutorial and/or screencast for us? We’re looking for in depth and well-written tutorials on HTML, CSS, PHP, and JavaScript. If you’re of the ability, please contact Jeffrey at nettuts@tutsplus.com.

Please note that actual compensation will be dependent upon the quality of the final tutorial and screencast.

Write a PLUS tutorial


December 29 2009

16:50

6 CodeIgniter Hacks for the Masters

CodeIgniter is a simple and powerful open source web application framework for PHP. Today, we’ll do some core “hacks” to this framework to change and improve its functionality. In the process, you’ll gain a better understanding of the intricacies of CodeIgniter.

Disclaimer

  1. It is not recommended to apply these hacks to an existing project. Since they change some of CodeIgniter’s core functionality, it can break the existing code.
  2. As of this writing, CodeIgniter 1.7.2 is the latest stable release. These hacks are not guaranteed to work for future (or past) releases.
  3. Even though CodeIgniter is designed to be PHP 4 compatible, some of these hacks are not. So you will need a server with PHP 5 installed.
  4. When you make any changes to the files inside the system folder, you should document it somewhere for future reference. Next time you upgrade CodeIgniter (even though they do not release updates very often), you may need to reapply those changes.

1. Autoloading Models PHP 5 Style

The Goal

On the left side, you see the regular way of loading a model in CodeIgniter, from within a Controller. After this hack, we will be able to create objects directly. The code is cleaner, and your IDE will be able to recognize the object types. This enables IDE features such as auto-complete, or previewing documentation.

There are two more side effects of this hack. First, you are no longer required to extend the Model class:

And you no longer have to add a require_once call before you do model class inheritance.

The Hack

All we need to do is add a PHP 5 style autoloader function.

Add this code to the bottom of system/application/config/config.php:

<?php
// ...

function __autoload($class) {
	if (file_exists(APPPATH."models/".strtolower($class).EXT)) {
		include_once(APPPATH."models/".strtolower($class).EXT);
	}
}
?>

If you are interested in applying this hack for controllers too, you can use this code instead:

<?php
// ...

function __autoload($class) {
	if (file_exists(APPPATH."models/".strtolower($class).EXT)) {
		include_once(APPPATH."models/".strtolower($class).EXT);
	} else if (file_exists(APPPATH."controllers/".strtolower($class).EXT)) {
		include_once(APPPATH."controllers/".strtolower($class).EXT);
	}
}
?>

Any time you try to use a class that is not defined, this __autoload function is called first. It takes care of loading the class file.

2. Prevent Model-Controller Name Collision

The Goal

Normally, you can not have the same class name for a Model and a Controller. Let’s say you created a model name Post:

class Post extends Model {

	// ...

}

Now you can not have a URL like this:

http://www.mysite.com/post/display/13

The reason is because that would require you to also have a Controller class named ‘Post.’ Creating such a class would result in a fatal PHP error.

But with this hack, it will become possible. And the Controller for that URL will look like this:

// application/controllers/post.php

class Post_controller extends Controller {

	// ...

}

Note the ‘_controller’ suffix.

The Hack

To get around this issue, normally most people add the ‘_model’ suffix to the Model class names (eg. Post_model). Model objects are created and referenced all over the application, so it might seem a bit silly to have all of these names with ‘_model’ floating around. I think it is better to add a suffix to the Controllers instead, since they are almost never referenced by their class names in your code.

First we need to extend the Router class. Create this file: “application/libraries/MY_Router.php”

class MY_Router extends CI_Router {
	var $suffix = '_controller';

	function MY_Router() {
		parent::CI_Router();
	}

	function set_class($class) {
		$this->class = $class . $this->suffix;
	}

	function controller_name() {

		if (strstr($this->class, $this->suffix)) {
			return str_replace($this->suffix, '', $this->class);
		}
		else {
			return $this->class;
		}

	}
}

Now edit “system/codeigniter/CodeIgniter.php” line 153:

if ( ! file_exists(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->controller_name().EXT))

Same file, line 158:

include(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->controller_name().EXT);

Next, edit: “system/libraries/Profiler.php”, line 323:

$output .= "
".$this->CI->router->controller_name()."/".$this->CI->router->fetch_method()."
";

That is all. Keep in mind that with this hack you are required to put the ‘_controller’ suffix on all of your controller class names. But not in the file names or the URL’s.

3. Form Validation for Unique Values

The Goal

CodeIgniter has a nice Form_validation class. It comes with several validation rules:

These are useful, but there is an important one missing from this list: to check for unique values. For example, most user registration forms need to check that the username is not already taken, or the e-mail address is not already in the system.

With this hack, you will be able add this validation rule to your form submission handler very easily:

$this->form_validation->set_rules('username', 'Username',
		'required|alpha_numeric|min_length[6]|unique[User.username]');

Note the last part that says “unique[User.username].” This new validation rule is just called “unique,” and takes a parameter inside the square brackets, which is “tablename.fieldname”. So it will check the “username” column of the “User” table to make sure the submitted value does not already exist.

Similarly, you can check for duplicate e-mails:

$this->form_validation->set_rules('email', 'E-mail',
		'required|valid_email|unique[User.email]');

And your application can respond with proper error messages:

The Hack

This might be considered more of an extension than a hack. Nevertheless, we are going to take a core CodeIgniter library and improve it.

Create: “application/libraries/MY_Form_validation.php”

class MY_Form_validation extends CI_Form_validation {

	function unique($value, $params) {

		$CI =& get_instance();
		$CI->load->database();

		$CI->form_validation->set_message('unique',
			'The %s is already being used.');

		list($table, $field) = explode(".", $params, 2);

		$query = $CI->db->select($field)->from($table)
			->where($field, $value)->limit(1)->get();

		if ($query->row()) {
			return false;
		} else {
			return true;
		}

	}
}

Now you can use the unique validation rule.

4. Running CodeIgniter from the Command Line

The Goal

Just like the title says, our goal is to be able to run CodeIgniter applications from the command line. This is necessary for building cron jobs, or running more intensive operations so you don’t have the resource limitations of a web script, such as maximum execution time.

This is what it looks like on my local Windows machine:

The above code would be like calling this URL:

http://www.mysite.com/hello/world/foo

The Hack

Create a “cli.php” file at the root of your CodeIgniter folder:

if (isset($_SERVER['REMOTE_ADDR'])) {
	die('Command Line Only!');
}

set_time_limit(0);

$_SERVER['PATH_INFO'] = $_SERVER['REQUEST_URI'] = $argv[1];

require dirname(__FILE__) . '/index.php';

If you are on a Linux environment and want to make this script self executable, you can add this as the first line in cli.php:

#!/usr/bin/php

If you want a specific controller to be command line only, you can block web calls at the controller constructor:

class Hello extends Controller {

	function __construct() {
		if (isset($_SERVER['REMOTE_ADDR'])) {
			die('Command Line Only!');
		}
		parent::Controller();
	}

	// ...

}

5. Adding Doctrine ORM to CodeIgniter

The Goal

Doctrine is a popular Object Relational Mapper for PHP. By adding it to CodeIgniter, you can have a very powerful Model layer in your framework.

The Hack

Just installing Doctrine is not very “hacky” per se, as we can just add it as a plug-in. However, once added, your Model classes will need to extend the Doctrine base classes, instead of the CodeIgniter Model class. This will completely change the way the Model layer works in the framework. The objects you create will have database persistence and also will able to have database relationships with other objects.

Follow these steps:

  1. Create folder: application/plugins
  2. Create folder: application/plugins/doctrine
  3. Download Doctrine (1.2 as of this article)
  4. Copy the “lib” folder from Doctrine to: “application/plugins/doctrine”
  5. Create “application/plugins/doctrine_pi.php”
// system/application/plugins/doctrine_pi.php

// load Doctrine library
require_once APPPATH.'/plugins/doctrine/lib/Doctrine.php';

// load database configuration from CodeIgniter
require_once APPPATH.'/config/database.php';

// this will allow Doctrine to load Model classes automatically
spl_autoload_register(array('Doctrine', 'autoload'));

// we load our database connections into Doctrine_Manager
// this loop allows us to use multiple connections later on
foreach ($db as $connection_name => $db_values) {

	// first we must convert to dsn format
	$dsn = $db[$connection_name]['dbdriver'] .
		'://' . $db[$connection_name]['username'] .
		':' . $db[$connection_name]['password'].
		'@' . $db[$connection_name]['hostname'] .
		'/' . $db[$connection_name]['database'];

	Doctrine_Manager::connection($dsn,$connection_name);
}

// CodeIgniter's Model class needs to be loaded
require_once BASEPATH.'/libraries/Model.php';

// telling Doctrine where our models are located
Doctrine::loadModels(APPPATH.'/models');

Next, edit “application/config/autoload.php” to autoload this Doctrine plugin

$autoload['plugin'] = array('doctrine');

Also make sure you have your database configuration in “application/config/database.php”.

That is all. Now you can create Doctrine Models within your CodeIgniter application. Read my tutorials on this subject for more information.

6. Running Multiple Sites

The Goal

This hack will make it possible for you to run multiple sites from a single install of CodeIgniter. Each website will have its own application folder, but they will all share the same system folder.

The Hack

Install CodeIgniter anywhere on the server. It doesn’t need to be under a website folder. Then take the application folder out of the system folder. And make additional copies of it, as seen in the image above, for every website you want to run. You can place those application folders anywhere, like under each separate website folders.

Now copy the index.php file to the root of each website folder, and edit it as follows:

At line 26, put the full path to the system folder:

	$system_folder = dirname(__FILE__) . '../codeigniter/system';

At line 43, put the full path to the application folder:

	$application_folder = dirname(__FILE__) . '../application_site1';

Now you can have independent websites using separate application folders, but sharing the same system folder.

There is a similar implementation in the CodeIgniter User Guide you can read also.

7. Allowing All File Types for Uploads

The Goal

When using the Upload library in CodeIgniter, you must specify which file types are allowed.

$this->load->library('upload');

$this->upload->set_allowed_types('jpg|jpeg|gif|png|zip');

If you do not specify any file types, you will receive an error message from CodeIgniter: “You have not specified any allowed file types.”

So, by default, there is no way to allow all file types to be uploaded. We need to do small hack to get around this limitation. After that we will be able to allow all file types by setting it to ‘*’.

$this->load->library('upload');

$this->upload->set_allowed_types('*');

The Hack

For this hack we are going to modify the Upload class behavior.

Create file: application/libraries/My_Upload.php

class MY_Upload extends CI_Upload {

	function is_allowed_filetype() {

		if (count($this->allowed_types) == 0 OR ! is_array($this->allowed_types))
		{
			$this->set_error('upload_no_file_types');
			return FALSE;
		}

		if (in_array("*", $this->allowed_types))
		{
			return TRUE;
		}

		$image_types = array('gif', 'jpg', 'jpeg', 'png', 'jpe');

		foreach ($this->allowed_types as $val)
		{
			$mime = $this->mimes_types(strtolower($val));

			// Images get some additional checks
			if (in_array($val, $image_types))
			{
				if (getimagesize($this->file_temp) === FALSE)
				{
					return FALSE;
				}
			}

			if (is_array($mime))
			{
				if (in_array($this->file_type, $mime, TRUE))
				{
					return TRUE;
				}
			}
			else
			{
				if ($mime == $this->file_type)
				{
					return TRUE;
				}
			}
		}

		return FALSE;

	}

}

Conclusion

I hope some of these are useful to you. If not, they are still interesting to know and can help you learn more about the internal workings of a framework and some of the core PHP language features.

If you know any other cool hacks or modifications, let us know in the comments. Thank you!

Ready to take your skills to the next level, and start profiting from your scripts and components? Check out our sister marketplace, CodeCanyon.

CodeCanyon


November 06 2009

10:01

Book Review: Zend Framework 1.8 Web Application Development

If you are looking into buying a book to learn about Zend Framework, chances are you are already set on using Zend Framework to build your next project. Today, we will be looking at Zend Framework 1.8 Web Application Development by Keith Pope, published by Packt Publishing.

First of all, you’ll notice that this book is based on Zend Framework version 1.8, and as of writing this review, the latest stable release of Zend Framework is 1.9.4. This is not an issue, because 1.9, even though it brings new features such as PHP 5.3 compatibility and RESTful web services, does not change its structure or any part of the system that might have impact on your learning.

Flow of the Book

The flow of this book is heavily inspired by the famous Ruby on Rails book, Agile Web Development with Rails, where the author invites you to join the process of building a demo application, which in both cases is a shopping cart system. Judging by the feedback of the Rails book, most people feel quite comfortable learning a framework this way, some don’t. I guess if you are not a fan of following a defined learning structure, this book probably isn’t for you.

Short but Sweet

It is a relatively short book, with only around 350 pages. As a result, this book expects you to be comfortable with working with PHP 5 and have a solid grasp of Object-Oriented Programming. If you aren’t already familiar with PHP, or PHP 5’s OOP features, I highly recommend you to polish up the said skills.

MVC Still Rules

The first two chapters of the book focus on the MVC (Model-View-Controller) pattern. As the author mentions at the start of the book, Zend Framework is a loosely coupled framework; it does not enforce the MVC principle. However, given the popularity of MVC within the web development community, it is definitely worth while to learn how to write an application in MVC. Chapter one explains the basics of MVC whilst chapter two explains the request/route/dispatcher/response family. These two chapters will set up the foundation nicely for you and get you to understand the basic structure of a Zend Framework powered MVC application.

Adventure of the Store-Front App

Chapter three to nine contain the actual ‘adventure’ where you as the reader will be riding along with the author on the journey of creating a store-front/shopping-cart application. During the process, the author tells you not only what to do, but also why to do them. A good example is the ‘fat controller skinny model’ vs ’skinny controller vs fat model’ comparison, the book illustrates each and tells you why you should stick with the latter.

Chapter ten wraps up the store-front application with some more common tasks such as bootstrapping modules and sharing common application elements.

Code Optimization, Caching and Testing

Chapter eleven touches on a very practical topic: code optimization and caching. This is especially beneficial if you’re to run a large volume web application or if you have limited hardware resources. Pay special attention to the Zend_Cache section as the author tells you how to integrate it effectively in your application in order to achieve the best result.

The last chapter, chapter twelve, introduces you to Zend_Test, a testing framework that utilizes PHPUnit.

Verdict

To wrap the review up, I think this is an excellent book on Zend Framework provided you:

  • already have a good understanding of PHP;
  • already have a good understanding of OOP;
  • can follow the rather forceful learning flow;
  • know how to learn with initiative (e.g. do your own research!).

This book sits well in the market, as it aims primarily at web professionals who most likely are already experienced with PHP and perhaps some other PHP frameworks, and don’t have time to read books with 1000’s of pages.

You may purchase this book via Packt Publishing’s website.



Older posts are this way If this message doesn't go away, click anywhere on the page to continue loading posts.
Could not load more posts
Maybe Soup is currently being updated? I'll try again automatically in a few seconds...
Just a second, loading more posts...
You've reached the end.
(PRO)
No Soup for you

Don't be the product, buy the product!

close
YES, I want to SOUP ●UP for ...