http://grahambird.co.uk/cake/tutorials/ajax.php
Ajax task list
A tutorial for the Cake PHP framework
This tutorial is similar to the Ta Da List application (www.tadalist.com) in Ruby on Rails.
Note: Updated on 3 May 2006 10:07pm GMT to work with Cake version 1.0.1.2708
What you will learn
How to create a basic task list using Ajax and Cake.
What you will need
An installation of Cake on your server, a working database and the script.aculo.us JavaScript library (which can be downloaded from script.aculo.us).
Demonstration
http://demo.grahambird.co.uk/tasks
Download files
If you don't want to copy and paste the code, download all the application files in a handy Zip file. Please note that this file does not include Cake itself.
1. Upload script.aculo.us files
Place the script.aculo.us files in the /app/webroot/js/
folder. Don’t forget to put the prototype.js
file (that comes with the script.aculo.us library) in the same folder.
To make the JavaScript libraries available to Cake, you also need to add the following inside the <head> tag of your Layout file (/app/views/layouts/default.thtml
):
<?php print $html->charsetTag('UTF-8') ?> <?php print $javascript->link('prototype') ?> <?php print $javascript->link('scriptaculous.js?load=effects') ?>
Note: The Ajax functions only work with the UTF-8 character set declaration.
If you have not created a layout file (ie the /app/views/layouts/ folder is empty), you can add these lines to the default Cake layout (in /cake/libs/views/templates/layouts/).
2. Create the database table
Create the database table structure.
CREATE TABLE tasks ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, title VARCHAR(255) NOT NULL, done TINYINT(1), created DATETIME, modified DATETIME, PRIMARY KEY (id) );
3. Create a Model file
Create a file called task.php
and put it in the /app/models
folder. Copy and paste the code below into your file.
<?php //File: /app/models/task.php class Task extends AppModel { var $name = 'Task'; } ?>
4. Create a Controller file
Create a file called tasks_controller.php
and put it in the /app/controllers
folder. Copy and paste the code below into your file.
The key things to note here are the render actions. Rather than displaying a new view (normal behaviour) they instead send the rendered view back to a div in the view that originally called the controller. This is done by specifying the ajax layout in the second argument.
<? php
// File: /app/controllers/tasks_controller.php
class TasksController extends AppController
{
var $name = ' Tasks ' ;
var $helpers = array ( ' Html ' , ' Javascript ' , ' Ajax ' );
function index()
{
// initial view
$this -> set( ' data ' , $this -> Task -> findAll());
}
function add()
{
// adds new task to database
if ( ! empty ( $this -> params[ ' data ' ]))
{
if ( $this -> Task -> save( $this -> params[ ' data ' ]))
{
$this -> set( ' data ' , $this -> Task -> findAll());
$this -> render( ' todo ' , ' ajax ' );
}
else
{
// do nothing
}
}
}
function done ( $id = null )
{
// move task from todo to done
$this -> Task -> id = $id ;
$this -> params[ ' data ' ][ ' Task ' ][ ' done ' ] = 1 ;
if ( $this -> Task -> save( $this -> params[ ' data ' ]))
{
$this -> set( ' data ' , $this -> Task -> findAll());
$this -> render( ' done ' , ' ajax ' );
}
}
function undo( $id )
{
// moves task from done to todo
$this -> Task -> id = $id ;
$this -> params[ ' data ' ][ ' Task ' ][ ' done ' ] = 0 ;
if ( $this -> Task -> save( $this -> params[ ' data ' ]))
{
$this -> set( ' data ' , $this -> Task -> findAll());
$this -> render( ' todo ' , ' ajax ' );
}
}
function delete( $id )
{
// deletes task from database
$this -> Task -> del( $id );
$this -> set( ' data ' , $this -> Task -> findAll());
$this -> render( ' done ' , ' ajax ' );
}
}
?>
5. Create the Views
Create three view files, index.thtml
, todo.thtml
and done.thtml
and put them in the /app/views/tasks
folder. Copy and paste the code below into your files.
The index.thtml
file includes the form that adds new tasks and two divs (tasks_todo and tasks_done) which show the tasks. The first time the page loads, these divs are populated with tasks. From then on they are dynamically updated by the ajax functions.
By specifying which div to update in the call to Ajax.Updater (in the onClick actions on the checkboxes), we tell Cake where to display the update coming from the $this->render statement in the Controller.
<!-- File : / app / views / tasks / index . thtml -->
< div id = " add_task " >
< form onSubmit = " return false; " >
<? php
print $html -> input( ' Task/title ' , array ( ' id ' => ' add_input ' ));
print $ajax -> submit( ' Add task ' , array ( ' url ' => ' add ' , ' update ' => ' tasks_todo ' ));
?>
</ form >
</ div >
< h3 > To do </ h3 >
< div id = " tasks_todo " >
<? php
foreach ( $data as $row )
{
$item = $row [ ' Task ' ];
if ( $item [ ' done ' ] == 0 )
{
print ' <div class="task" id="todo_ ' . $item [ ' id ' ] . ' "> ' ;
print ' <input id="todo_check_ ' . $item [ ' id ' ] . ' " type="checkbox" onClick="new Ajax.Updater('tasks_done','/tasks/done/ ' . $item [ ' id ' ] . ' ', {asynchronous:true, evalScripts:true});new Effect.Fade('todo_ ' . $item [ ' id ' ] . ' ');" /> ' ;
print ' <label for="todo_check_ ' . $item [ ' id ' ] . ' "> ' . $item [ ' title ' ] . ' </label> ' ;
print ' </div> ' ;
}
}
?>
</ div >
< h3 > Done </ h3 >
< div id = " tasks_done " >
<? php
foreach ( $data as $row )
{
$item = $row [ ' Task ' ];
if ( $item [ ' done ' ] == 1 )
{
print ' <div class="task" id="done_ ' . $item [ ' id ' ] . ' "> ' ;
print ' <input id="done_check_ ' . $item [ ' id ' ] . ' " type="checkbox" checked="true" onClick="new Ajax.Updater('tasks_todo','/tasks/undo/ ' . $item [ ' id ' ] . ' ', {asynchronous:true, evalScripts:true});new Effect.Fade('done_ ' . $item [ ' id ' ] . ' ');" /> ' ;
print ' <label for="done_check_ ' . $item [ ' id ' ] . ' "> ' . $item [ ' title ' ] . ' </label> ' ;
print $ajax -> link( ' Delete ' , ' delete/ ' . $item [ ' id ' ] , array ( ' update ' => ' tasks_done ' ));
print ' </div> ' ;
}
}
?>
</ div >
The file todo.thtml
is loaded into the tasks_todo div by the add or undo methods in the controller. This happens when a task is created or unticked. This View simply loops through all tasks in the database and displays those where the done field equals 0.
If Cake is installed in a subfolder (ie not your server's web root) then you will need to use $html->url("/tasks/undo/" . $item["id"] ); in the second Ajax.Updater argument. This means the URL is generated by Cake rather than being hard-coded.
<? php
// File: /app/views/tasks/todo.thtml
foreach ( $data as $row )
{
$item = $row [ ' Task ' ];
if ( $item [ ' done ' ] == 0 )
{
print ' <div class="task" id="todo_ ' . $item [ ' id ' ] . ' "> ' ;
print ' <input id="todo_check ' . $item [ ' id ' ] . ' " type="checkbox" onClick="new Ajax.Updater('tasks_done','/tasks/done/ ' . $item [ ' id ' ] . ' ', {asynchronous:true, evalScripts:true});new Effect.Fade('todo_ ' . $item [ ' id ' ] . ' ');" /> ' ;
print ' <label for="todo_check_ ' . $item [ ' id ' ] . ' "> ' . $item [ ' title ' ] . ' </label> ' ;
print ' </div> ' ;
}
}
?>
The file done.thtml
is loaded into the tasks_done div when a task is ticked (triggering the done method in the controller). This View loops through all tasks in the database and displays all those where the done field equals 1.
<? php
// File: /app/views/tasks/done.thtml
foreach ( $data as $row )
{
$item = $row [ ' Task ' ];
if ( $item [ ' done ' ] == 1 )
{
print ' <div class="task" id="done_ ' . $item [ ' id ' ] . ' "> ' ;
print ' <input id="done_check_ ' . $item [ ' id ' ] . ' " type="checkbox" checked="true" onClick="new Ajax.Updater('tasks_todo','/tasks/undo/ ' . $item [ ' id ' ] . ' ', {asynchronous:true, evalScripts:true});new Effect.Fade('done_ ' . $item [ ' id ' ] . ' ');" /> ' ;
print ' <label for="done_check_ ' . $item [ ' id ' ] . ' "> ' . $item [ ' title ' ] . ' </label> ' ;
print $ajax -> link( ' Delete ' , ' delete/ ' . $item [ ' id ' ] , array ( ' update ' => ' tasks_done ' ));
print ' </div> ' ;
}
}
?>
6. Try it out
Visit your task list at www.example.com/tasks
. You should have a working task manager.
Comments and feedback
Please e-mail graham at grahambird dot co dot uk