Welcome!

And don't forget to edit your signature & profile.

 

Icon

Statistics

  • Total posts 23318
  • Total topics 4048
  • Total members 5574
  • Our newest member
    ALF5583

TOP POSTERS

CakePHP - Stop Reading About It And Start Loving It

All problems and developments related to PHP, Ruby on Rails & Co. are discussed and resolved here.
   
A lot of people are posting questions about MVC frameworks and which one they should choose. As a CakePHP (or Cake for short) fan I've decided to write a tutorial about it.

This tutorial is meant for people who are already developing using PHP but want to improve their style and embrace new practices - like the MVC pattern.

I'll break the tutorial into multiple posts, as it get's bots hard to write and read the tutorial if it's long.

We'll build a Movie Trailer App - or Movee for short (love the web2.0 name, BTW) where people will be able to browse the latest movie trailers (we'll just embed YouTube vids), rate them, comment etc.

So, off we go. Download the latest stable version, extract it to your local machine and we're ready to go. For this purpose, I've extracted my into localhost/movee directory. When you extract the archive, you should get something like this

Code: Select all
[localhost root]
/movee
   /app
   /cake
   /vendors
   .htaccess
   index.php


Next, we need a database. Create a new database, call it movee (or whatever you like) and add a table called trailers that looks something like this

Code: Select all
id - int, primary key
title - varchar(200)
description - varchar(255)
youtube_url - varchar(255)
created - datetime


:!: OK, we have our database and a table for storing the trailers. From now on we'll be working in the /app folder.

Open /config and rename the database.php.default to database.php. Open it as we need to do a few tweaks to tell Cake to use the database we just created. You can just copy-paste the code, just make sure you modify the parameters to your system. The code it really self-explanatory. :oops:

Code: Select all

<?php
class DATABASE_CONFIG {

    var 
$default = array(
        
'driver' => 'mysql',
        
'persistent' => false,
        
'host' => 'localhost',
        
'login' => 'root',
        
'password' => '',
        
'database' => 'movee',
    );
}
?>


Hit http://localhost/movee and we're ready to write our first lines of code.

First, the model. Models are the one that are responsible for communicating with the database, fetching, saving and deleting data.

:!: Create a file in the /models folder called trailer.php

Code: Select all

<?php
    
class Trailer extends AppModel {    
        var 
$name 'Trailer';
    }
?>


This is all it takes for Cake to know you want your model to "talk" to the trailers table in your database. The code is PHP4 style, this is kind of a downside to Cake, but the future versions will be pure PHP5.

Next we need a controller. Controllers are like glue. They process the client requests, take the corresponding model, load the data from it and pass it to the view which serve only to display the data.

:!: Our controller will reside in the /controllers folder. So create a file trailers_controller.php and paste the following code.

Code: Select all

<?php
    
class TrailersController extends AppController {        
    }    
?>


What you can do now is go to http://localhost/movee/trailers and Cake will automatically map that URL to the controller.

BTW, you'll get an error saying there is no index action.

When your URL's point to something like http://localhost/movee/trailers, Cake assumes you want the "index" action. So let's create one. The following code goes inside the trailers_controller.php

Code: Select all
function index() {
    
$trailers $this->Trailer->find('all');
    
$this->set('trailers'$trailers);    
}
 


What this does it uses the Trailer model's built in function called find and with the parameter 'all' finds all the trailers in the trailers table. (You might want to add a few rows first using your GUI/phpMyAdmin/shell tool)

The set function sends the data to the view file which we'll create now.

In the /views folder create a new folder /trailers and in it a file index.ctp

:!: Notice all Cake template files have the .ctp extension

Code: Select all

<h1>Latest trailers</h1>

<?php foreach($trailers as $trailer):?>
<b><?php echo $trailer['Trailer']['id'];?></b> <br />
<?php endforeach;?>


We're basically looping through the list of trailers here and just echoing the title. Notice you have to use the ['Trailer'] index first. I'll get to explaining this in one of the next posts.

Next up we'll create a way of adding new trailers, editing and deleting. We'll also be able to VIEW the trailer :D Sorting movies in categories (genres?), adding comments, ratings.

This was really quick and you will surely have a ton of questions. Shout them here or PM me any time. 8-)

Before you ask, here are a few resources that are a must see:


Hope you want more :thumbsup:
Last edited by ibernat on Fri Mar 27, 2009 12:49 pm, edited 1 time in total.
Freelance PHP and CSS developer in love with jQuery and CakePHP. Playing with Django and AppEngine.

Portfolio -PSD to HTML slicing -Twitter
User avatar
ibernat
Smashing <hr />
 
Posts: 54
Joined: Mon Feb 09, 2009 4:07 pm
Location: Zagreb, Croatia / London, UK
   

   
OK, off the top of my head, a few things I forgot to mention.

1) WHERE DID ALL THE SQL GO??

The $this->Trailer->find('all'); actually generates all the SQL necessary to select all the rows from the Trailers table. You can add conditions to fetch only the rows you need, I'll get to this soon.

2) WHAT DATABASE ARE YOU USING??

Cake can use any database, the configuration I posted was for MySQL. PM me for other databases.

3) CAN I WRITE http://localhost/movee/trailers/index ??

Yes, it's the same as leaving http://localhost/movee/trailers (without the /index). Cake can read your mind. Sort of like Ruby on Rails. 8-)
Freelance PHP and CSS developer in love with jQuery and CakePHP. Playing with Django and AppEngine.

Portfolio -PSD to HTML slicing -Twitter
User avatar
ibernat
Smashing <hr />
 
Posts: 54
Joined: Mon Feb 09, 2009 4:07 pm
Location: Zagreb, Croatia / London, UK
   

   
Okay, let's create a way of adding new trailers into the DB.

In the trailers controller add a new method called add

Code: Select all
function add() {
    if (!empty(
$this->data)) {
        
$this->Trailer->create();
        if (
$this->Trailer->save($this->data)) {
            
$this->Session->setFlash('New trailer saved');
            
$this->redirect(array('action'=>'index'));
        } else {
            
$this->Session->setFlash('Something went wrong. Give it another go');
        }
    }
}
 


So, instead of using $_POST from now on you'll use $this->data. And if it's not empty (meaning if the form had been submitted) we can save the data. Notice there are no insert statements. Using $this->Trailer->save(); Cake generates all the SQL necessary.

After saving we'll redirect back to the index action.

Now, the view file (/views/trailers/add.ctp)

Code: Select all

<h1>Add a New Trailer</h1>
<?php 
    
echo $form->create('Trailer');
    echo 
$form->input('title');
    echo 
$form->input('description', array('type'=>'textarea'));
    echo 
$form->input('youtube_url');

    echo 
$form->end('Submit');
?>


This will use Cake's form helper which saves you the trouble in hand-coding <form>, <input /> etc. Notice the form elements names correspond to the table fields.

You can access this page via http://localhost/movee/trailers/add

After you hit Submit, check the DB. You'll notice Cake automatically added the date in the Created field :thumbsup:

This is just the start of all the things Cake can do to save your time
Last edited by ibernat on Fri Mar 27, 2009 12:49 pm, edited 1 time in total.
Freelance PHP and CSS developer in love with jQuery and CakePHP. Playing with Django and AppEngine.

Portfolio -PSD to HTML slicing -Twitter
User avatar
ibernat
Smashing <hr />
 
Posts: 54
Joined: Mon Feb 09, 2009 4:07 pm
Location: Zagreb, Croatia / London, UK
   

   
OK, time to edit and delete the trailers, after that it's fun stuff all the way (I promise!) :thumbsup:

So, in the trailers_controller.php add a new method called edit which will be accessible via http://localhost/movee/trailers/edit/123 where 123 is the ID of the trailer we want to edit

Code: Select all
function edit($id null) {
    
// make sure the user didn't try to access http://localhost/movee/edit
    // without the ID of the trailer
    
if (!$id && empty($this->data)) {
        
$this->flashError('Please choose a Trailer to edit');
    }
    
// if the form had been submitted
    
if (!empty($this->data)) {
        if (
$this->Trailer->save($this->data)) {
            
$this->flashSuccess('Trailer edited and saved. Weee!');
        } else {
            
$this->Session->setFlash('Something went wrong. Ooops!');
        }
    }
    
// OK, find the trailer with this ID and load it into the data array
    // so we can display the data in the form fields
    
if (empty($this->data)) {
        
$this->data $this->Trailer->read(null$id);
    }
}
 


And now the view file (/views/trailers/edit.ctp) which is the same as the add.ctp,
but we added the ID field as well, so Cake knows which record to update

Code: Select all

<h1>Add a New Trailer</h1>
<?php
   
echo $form->create('Trailer');
   echo 
$form->input('title');
   echo 
$form->input('description', array('type'=>'textarea'));
   echo 
$form->input('youtube_url');

   
// notice this, we add the ID field so Cake knows which record to update
   
echo $form->input('id', array('type'=>'hidden'));

   echo 
$form->end('Submit');
?>


Deleting stuff is plain easy. Just pass in the ID and use the built in del method which takes care of things. No view files necessary.

Code: Select all
function delete($id null) {
    if (!
$id) {
        
$this->Session->setFlash('Please choose a REAL trailer to delete.');
        
$this->redirect(array('action'=>'index'));
    }
    if (
$this->Trailer->del($id)) {
        
$this->Session->setFlash('Trailer deleted..');
        
$this->redirect(array('action'=>'index'));
    }
}
 


As promised, only fun stuff from now on. Next up will be adding movie genres, comments, user registration, protecting the admin area, layouts and maybe AJAX.

BTW, I know the view option is missing. I think I'll explain that first, and then move on :oops:
Last edited by ibernat on Fri Mar 27, 2009 12:50 pm, edited 1 time in total.
Freelance PHP and CSS developer in love with jQuery and CakePHP. Playing with Django and AppEngine.

Portfolio -PSD to HTML slicing -Twitter
User avatar
ibernat
Smashing <hr />
 
Posts: 54
Joined: Mon Feb 09, 2009 4:07 pm
Location: Zagreb, Croatia / London, UK
   

   
Okay, lets wrap the first part up by adding the view action. Each trailer has a numeric ID and can be viewed by going to /trailers/view/123 where 123 is the ID. It's also easy to do stuff like using slugs instead of numbers (like /trailers/view/mission-impossible-iv) but I'll leave that for some other time.

Create a view method in the Trailers controller with the following code

Code: Select all

function view
($id = null) {
    if (!$id) {
        $this->Session->setFlash('Cant find this trailer.');
        $this->redirect(array('action'=>'index'));
    }
    // find the trailer based on it's ID
    $trailer = $this->Trailer->findById($id);
    // parse the URL to get the video key
    $trailer['Trailer']['youtube_url'] = $this->parseVideoCode($trailer['Trailer']['youtube_url']);
    // send it to the view
    $this->set('trailer', $trailer);
}
 


As you can see, we're using a built in method findById which looks up a record with that ID. If you wanted to search by, e.g. username, the method name would be findByUsername().

We also need to create the parseVideoCode method that will take a YouTube URL (e.g. http://www.youtube.com/watch?v=yLjvnTY4huk) and just return the "yLjvnTY4huk" part (the "video key").

Code: Select all

function parseVideoCode
($url){
    $vp = parse_url($url);
    parse_str($vidparser[query], $query);
    return $query['v'];
}
 


And the view that will show the actual video is located in /views/trailers/view.ctp

Code: Select all

<h1><?php echo $trailer['Trailer']['id'];?></h1>

<?php echo "<object width=\"490\" height=\"335\">
<param name=\"movie\" value=\"http://www.youtube.com/v/"
.$trailer['Trailer']['youtube_url']."\"></param>
<param name=\"allowFullScreen\" value=\"true\"></param>
<embed src=\"http://www.youtube.com/v/"
.$trailer['Trailer']['youtube_url']."\" 
type=\"application/x-shockwave-flash\" allowfullscreen=\"true\" width=\"490\" height=\"335\"></embed>
</object>"
; ?>

<p><?php echo $trailer['Trailer']['description'];?></p>


Hope you like this :thumbsup:
Freelance PHP and CSS developer in love with jQuery and CakePHP. Playing with Django and AppEngine.

Portfolio -PSD to HTML slicing -Twitter
User avatar
ibernat
Smashing <hr />
 
Posts: 54
Joined: Mon Feb 09, 2009 4:07 pm
Location: Zagreb, Croatia / London, UK
   

   
Okay, time to move on 8-)

Now we'll add the Genres into the mix. I'll only show you the important stuff, you can build the rest off the code from the previous posts.

Create a table genres in your movees database

Code: Select all
CREATE TABLE `genres` (                                       
          `id` int(11) NOT NULL AUTO_INCREMENT,                       
          `title` varchar(200) COLLATE utf8_unicode_ci DEFAULT NULL, 
          `slug` varchar(200) COLLATE utf8_unicode_ci DEFAULT NULL,   
          PRIMARY KEY (`id`)                                         
        ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci


Alter the trailers table and add another column

Code: Select all
genre_id - int


Now, we need to tell Cake that a Trailer resides in a certain category (a Genre), and also tell it that a Genre will have many Trailers in it.

Open /models/trailer.php and change it to the following

Code: Select all
class Trailer extends AppModel {
    var 
$name 'Trailer';

    var 
$belongsTo = array(
        
'Genre' => array(
            
'className' => 'Genre',
            
'foreignKey' => 'genre_id'
        
)
    );
}
 


What will this bring? Well, when you're looking for a certain Trailer, you get an array $trailer['Trailer]. Now you'll also get another index $trailer['Genre'] which will contain the Genre data for a certain trailer.

E.g. if a Trailer "Mad Dogs" belongs to "Thriller" Genre, you would get something like this

Code: Select all
$trailer
    ['Trailer']
        ['id']=>213, ['title']=>'Mad Dogs', ['youtube_url'] ...
    ['Genre']
        ['id']=>1, ['title']=>'Thriller' ...


We also need to add the Genre Model in /models/genre.php

Code: Select all
class Genre extends AppModel {
    var 
$name 'Genre';

    var 
$hasMany = array(
        
'Trailer' => array(
            
'className' => 'Trailer',
            
'foreignKey' => 'genre_id'
        
)
    );
}
 


So we just tell Cake it can expect this Genre to have more Trailers.

Now you're probably like :shock: or like :? But rest assured, this will all seem dead simple after you do it a few times.

For now create the controller actions for the GenresController (add/edit/delete/index/view), the view files and give it a go.

Later on I'll explain how to add a Trailer to a Genre. 8-)
Freelance PHP and CSS developer in love with jQuery and CakePHP. Playing with Django and AppEngine.

Portfolio -PSD to HTML slicing -Twitter
User avatar
ibernat
Smashing <hr />
 
Posts: 54
Joined: Mon Feb 09, 2009 4:07 pm
Location: Zagreb, Croatia / London, UK
   

   
BTW, obviously nobody's reading this, the parseVideoCode has a few errors :thumbdown:

This is the fixed version :P

Code: Select all
function parseVideoCode($url){
   $vp = parse_url($url);
   parse_str($vp['query'], $query);
   return $query['v'];
}
Freelance PHP and CSS developer in love with jQuery and CakePHP. Playing with Django and AppEngine.

Portfolio -PSD to HTML slicing -Twitter
User avatar
ibernat
Smashing <hr />
 
Posts: 54
Joined: Mon Feb 09, 2009 4:07 pm
Location: Zagreb, Croatia / London, UK
   

   
Thanks, i subscribe to this post. Very interessting!
"A day without a Table is a good day."
User avatar
breastfed
Smashing <table>
 
Posts: 46
Joined: Tue Apr 07, 2009 1:17 pm
Location: Dülmen, Germany
   

   
@breastfed Thanks! I'm working on a site dedicated to Cake tutorials, I'll PM you when it's done :thumbsup:
Freelance PHP and CSS developer in love with jQuery and CakePHP. Playing with Django and AppEngine.

Portfolio -PSD to HTML slicing -Twitter
User avatar
ibernat
Smashing <hr />
 
Posts: 54
Joined: Mon Feb 09, 2009 4:07 pm
Location: Zagreb, Croatia / London, UK
   

   
Hello!

This is amazing thread ! Thanx for that ! Is your cakephp dedicated site finished ?

Thank you !

btw: I am from Slovenia ! Lep pozdrav ! ;)
zaay
 
Posts: 2
Joined: Thu Sep 17, 2009 2:15 pm
   

   
Never got the chance to develop it, too much *real* work got in the way :D

(lang=hr) Pozdrav iz kišnog Zagreba :D
Freelance PHP and CSS developer in love with jQuery and CakePHP. Playing with Django and AppEngine.

Portfolio -PSD to HTML slicing -Twitter
User avatar
ibernat
Smashing <hr />
 
Posts: 54
Joined: Mon Feb 09, 2009 4:07 pm
Location: Zagreb, Croatia / London, UK
   

   
Hello!


I have one question. I am building news system helping myself with that tutorial. I think it's weird that i build edit function and i have news i want to edit and i get blank inputs,.. How can i fill inputs with data from database and then edit them ? See my point ?

Thanx!
zaay
 
Posts: 2
Joined: Thu Sep 17, 2009 2:15 pm
   

   
Code: Select all
$this->data = $this->News->read(null, $id);
Freelance PHP and CSS developer in love with jQuery and CakePHP. Playing with Django and AppEngine.

Portfolio -PSD to HTML slicing -Twitter
User avatar
ibernat
Smashing <hr />
 
Posts: 54
Joined: Mon Feb 09, 2009 4:07 pm
Location: Zagreb, Croatia / London, UK
   

   
Great little introduction to Cake! Been using it for a few months and the hardest thing to get my head around initially is the MVC and how different a PHP framework operates to raw PHP. Much better but still different!

Going to start looking at CodeIgniter more now though due to EE 2.0 running on it!
Tom Sinclair
Tom
 
Posts: 5
Joined: Sun Sep 28, 2008 10:56 am
Location: Manchester, UK
   

   
Tom wrote:Going to start looking at CodeIgniter more now though due to EE 2.0 running on it!


I think EE ran on CI from version 1? Still, why leave Cake? :(
Freelance PHP and CSS developer in love with jQuery and CakePHP. Playing with Django and AppEngine.

Portfolio -PSD to HTML slicing -Twitter
User avatar
ibernat
Smashing <hr />
 
Posts: 54
Joined: Mon Feb 09, 2009 4:07 pm
Location: Zagreb, Croatia / London, UK
   


Return to Server-side Scripting



Who is online

Users browsing this forum: No registered users and 1 guest