Build an Admin Panel with the Fuel PHP Framework

Build an Admin Panel with the Fuel PHP Framework

Tutorial Details
  • Program: Fuel PHP Framework
  • Version: 1.1.0
  • Difficulty: Easy
  • Estimated Completion Time: 2 hours

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',
		'Fuel\Core\Validation',
		'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.

Add Comment

Discussion 110 Comments

Comment Page 2 of 2 1 2
  1. Ian says:

    I had a issue with the eager loading part that turns out to be a bug in the __callStatic() method in the way it builds the query.

    If only the first post is returning an object from the query and the other posts don’t return any data it’s because of this issue explained here. http://fuelphp.com/forums/topics/view/6630#6685

    Change the find_by_slug line to this.

    $post = Model_Post::find(‘first’, array(‘where’ => array(‘slug’ => $slug), ‘related’ => array(‘user’)));

  2. Deepesh says:

    Hi,
    Thanks for this awesome post. I think I really like this framework and I have learnt a lot.
    However, I’m facing a small issue when deleting comments from the admin backend. I get the following error:

    OutOfBoundsException [ Error ]: Property “user_id” not found for Model_Comment.
    PKGPATH/orm/classes/model.php @ line 865

    Would appreciate if anyone can help me on this.

    Thanks

  3. Questionman says:

    Why is it full of static functions and global variables? These things are bad.

  4. David says:

    Very cool tutorial, thanks!

    Everything works fine but to delete comments. Renders this error:

    OutOfBoundsException [ Error ]: Property “user_id” not found for Model_Comment.

    Ideas?

    Thanks again,
    David

    {
    return $this->_data[$property];
    }
    else
    {
    throw new \OutOfBoundsException(‘Property “‘.$property.’” not found for ‘.get_called_class().’.');
    }
    }

    /**
    * Set

    • Olivier says:

      In Model_Comment change
      protected static $_belongs_to = array(‘post’,'user’);
      to
      protected static $_belongs_to = array(‘post’);

      A comment has a post_id but not user_id so it doesn’t belong to a user :)

  5. Dan says:

    After Running

    oil generate admin posts title:string slug:string summary:text body:text user_id:int

    You need to run
    oil refine migrate
    to get it workiung

  6. Muts says:

    Hi,

    Would it be possible to upload the whole finished project?
    I tried to get the oil command package to work but no use… 3 days I tried :)

    If your an expert exchange user check:
    http://www.experts-exchange.com/OS/Linux/Q_27512849.html

    But it would help me alot

    Regards

  7. thinkadoo says:

    What a GREAT framework!

    Simple Question on “Auth”

    I commented out // Response::redirect(‘admin/login’);
    in the admin controller to see the list view in step:5

    What is the Login name and pass it expects?

    Passing:
    username: admin
    password: NtgTF8q6Z9UyyUwQS7Ma5Bvj+uTrK/F66iMji9su1XY=
    DID NOT WORK

    Passing:
    email: phil@example.com
    password: NtgTF8q6Z9UyyUwQS7Ma5Bvj+uTrK/F66iMji9su1XY=
    DID NOT WORK

    I would really like to know.

    • the password should not inputed as the encrypted one. when you did the oil

      $ 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

      the password to input is the originally you have input there. when it is inserted to database it encrypted with md5 or sha

  8. thinkadoo says:

    It seems the problem is the scaffolding process created a field: “last_gin” instead of “last_login”

    Auth works fine when: “last_gin” is left in the db and “last_login” is added.

    But somewhere “last_gin” is referenced and is written to.

    Any Ideas?

  9. Cristina says:

    Cons:

    - Abuse of Static Classes (static classes can not be passed as data, and are not meant to be acted as a singleton or singleton containers)
    - No Service or Dependency Container (these should hold a singleton if you have any)
    - No use of Dependency Injection where needed (these help make classes more pure, expose dependencies, and make unit tests easier/more elegant)
    - Cascading FIle System (CFS)
    - No PSR-0/Non-standard Autoloading
    - Usage of “PHP” in vendor name. (PHP does not like this).
    - Bad Namespace Scheme
    - Doesn’t work with Composer or Pyrus.

    Pros:

    + ActiveRecord Pattern (perhaps)
    + Oil Code Generator
    + GUI to see your data
    + Clean Documentation
    + A Step Above Kohana
    + MIT License

    I would recommend FuelPHP for beginners of PHP and small projects. For serious projects I would recommend Symfony2.

    • Author

      I agree with some of these points but some are a little off:

      - Abuse of Static Classes (static classes can not be passed as data, and are not meant to be acted as a singleton or singleton containers)

      Most – if not all – classes use the multiton factory pattern. If you can show me a method of polymorphic inheritance in PHP that doesn’t use the factory pattern and doesn’t suck then I’ll be very impressed. We don’t use singletons in FuelPHP, sometimes it just looks like that. Dig deeper.

      - No use of Dependency Injection where needed (these help make classes more pure, expose dependencies, and make unit tests easier/more elegant)

      Certainly. This was a decision a while ago, but is coming in 2.0.

      - Cascading FIle System (CFS)

      How is this a con?

      - No PSR-0/Non-standard Autoloading
      - Doesn’t work with Composer or Pyrus.

      PSR-0 can be added as another autoloader if you wish to use it very easily. I will be fighting to get this supported in the core for 2.0 as the PHP world needs to get together on a single package manager and use it properly.

      - Usage of “PHP” in vendor name. (PHP does not like this).

      We initially called the project “Fuel” but that lead to problems. Whilst PHP would prefer you don’t use the term in their project, there are no legal ramifications. At the end of the day, Dan just didn’t care and I’m not sure I do either.

      - Bad Namespace Scheme

      Explain.

  10. Ha The Tung says:

    Hi there,
    In step 7, this line doesn’t work:

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

    As I inspected the generated query, WHERE should be inside FROM ( ):

    SELECT

    FROM (

    SELECT

    FROM …
    ORDER BY …
    LIMIT 1
    ) …
    LEFT JOIN …
    WHERE (

    )

    Tried this:

    $article = Model_Article::find()->where(‘slug’, $slug)->related(‘user’)->get_one();

  11. Possibly typo error in step 6 with the path?

    “We’re going to want to modify our action_create() method in fuel/app/classes/admin/posts.php”

    Surely the path should be :-

    “fuel/app/classes/controller/admin/posts.php”

    Cheers
    Dan.

  12. Paul says:

    thanks phil, i read this before and remembered the stuff you did with ORM, this one line was what i was looking for:

    comments as $comment): ?>

    :)

  13. John says:

    Wow, FuelPHP is very awesome!
    Thank you for your good article

    He speaks here http://blog.01tchat.com/fuelphp-framework-php/

  14. ambien says:

    Attractive portion of content. I just stumbled upon your blog and in accession capital to claim that I acquire in fact loved account your blog posts. Anyway I’ll be subscribing to your feeds and even I achievement you get admission to consistently rapidly.

  15. Saviour says:

    Hi thumbs up ! You guys are really doing a great job.
    Thanks.

  16. Tom says:

    I have no idea what i did wrong, i tried this tutorial over 10 times
    but right after the generation of step 5 (yes i did the additional migration) i get a redirect to public/admin/login and a blank screen ?

    any suggestions ?

  17. Christoffer Palm says:

    Great tutorial! Now i just need to understand everything i’ve done :)

Comment Page 2 of 2 1 2

Add a Comment

To add a code snippet to your comment, please wrap your code like so: <pre name="code" class="html">YOUR CODE</pre>. You can replace the class name with "js," "css," "sql," or "php." If there are any "<" or ">" within your code, please search and replace them with: &lt; and &gt; respectively.