Warning: file_exists() [function.file-exists]: open_basedir restriction in effect. File(/usr/lib/php5/pear/cake/libs/controller/components/session.php) is not within the allowed path(s): (/var/www/vhosts/rd11.com/subdomains/rdos/httpdocs:/tmp) in /var/www/vhosts/rd11.com/subdomains/rdos/httpdocs/cake/basics.php on line 939

Warning: session_start() [function.session-start]: Cannot send session cookie - headers already sent by (output started at /var/www/vhosts/rd11.com/subdomains/rdos/httpdocs/cake/basics.php:939) in /var/www/vhosts/rd11.com/subdomains/rdos/httpdocs/cake/libs/session.php on line 140

Warning: session_start() [function.session-start]: Cannot send session cache limiter - headers already sent (output started at /var/www/vhosts/rd11.com/subdomains/rdos/httpdocs/cake/basics.php:939) in /var/www/vhosts/rd11.com/subdomains/rdos/httpdocs/cake/libs/session.php on line 140

Warning: file_exists() [function.file-exists]: open_basedir restriction in effect. File(/usr/lib/php5/pear/cake/libs/model/dbo/dbo_mysql.php) is not within the allowed path(s): (/var/www/vhosts/rd11.com/subdomains/rdos/httpdocs:/tmp) in /var/www/vhosts/rd11.com/subdomains/rdos/httpdocs/cake/basics.php on line 939

Warning: file_exists() [function.file-exists]: open_basedir restriction in effect. File(/usr/lib/php5/pear/cake/config/tags.ini.php) is not within the allowed path(s): (/var/www/vhosts/rd11.com/subdomains/rdos/httpdocs:/tmp) in /var/www/vhosts/rd11.com/subdomains/rdos/httpdocs/cake/basics.php on line 939

Warning: file_exists() [function.file-exists]: open_basedir restriction in effect. File(/usr/lib/php5/pear/cake/libs/view/helpers/html.php) is not within the allowed path(s): (/var/www/vhosts/rd11.com/subdomains/rdos/httpdocs:/tmp) in /var/www/vhosts/rd11.com/subdomains/rdos/httpdocs/cake/basics.php on line 939

Warning: file_exists() [function.file-exists]: open_basedir restriction in effect. File(/usr/lib/php5/pear/cake/libs/view/helpers/javascript.php) is not within the allowed path(s): (/var/www/vhosts/rd11.com/subdomains/rdos/httpdocs:/tmp) in /var/www/vhosts/rd11.com/subdomains/rdos/httpdocs/cake/basics.php on line 939
CakePHP : A Rapid Development Framework :: Bulletin Boards

Part 2: The Cake Code

this content element requires the Flash 7.0 player. You can download it here

written by: gwoo aka Garrett Woodworth

The database is now setup so lets begin creating the code to connect the bulletin board.

The Model

First we create the Models so we can use Cake methods to access the db.

in /app/models/bulletin_board.php
<?php
class BulletinBoard extends AppModel {
    
    var $name ='BulletinBoard';
    
    var $belongsTo = 'User';
}
?>
in /app/models/user.php
<?php
class User extends AppModel {
    
    var $name ='User';

}
?>

From these models we see that BulletinBoard is associated with User. You may have noticed the user_id field in the bullentin_boards table. This is the foreignKey for the association and makes it easier to access the User data from the BulletinBoard.

The Controller

Now we create the BulletinBoardsController to handle all the fun stuff.

in /app/controllers/bulletin_boards_controller.php
<?php
class BulletinBoardsController extends AppController {
    
    var $name ='BulletinBoards';
	var $scaffold;

}
?>

The above code is the basic CakePHP class with scaffolding enabled. There is more example Cake code on the Cake Wiki

Check your Scaffolding

Debugging CakeBrowser

Now that Scaffolding is working, we can add some code to the Controller and tell AMFPHP about our Cake Controller class. This code is sectioned into parts to describe each function. If you would like the whole class, scroll to the bottom.

<?php
class BulletinBoardsController extends AppController {
    
    var $name ='BulletinBoards';
	var $scaffold;
	var $methodTable;

	function BulletinBoardsController() {
		//Define the methodTable
		$this->methodTable = array(
			"amfCreate" => array(
				"description" => "Inserts data from flash into DB",
				"access" => "remote"
	         ),
			"amfRead" => array(
				"description" => "Read data from DB and pass back recordset",
				"access" => "remote",
			)
		);
		parent::__construct();
	}

}
?>

We just added a constructor used by amfphp to determine the methods it has available.
*Make sure to call Cake's parent constructor.

You see that we defined two methods, amfCreate and amfRead. We provided some nice descriptions to know what they are going to do.

We will look at the amfCreate method first. This method looks a bit muddied because we wanted to show how associations in CakeAMFPHP can work. The first things to notice are *$this->autoRender = false;* and the *$this->constructClasses();*. You can see the code comments, but basically, these two things should be done for every method that will connect to via amf. So, in this method I want to add the user to the users table and the message to the bulletin_boards table. So, first we check if the user exists by doing a *findByName*. *findBy* is one of the best Cake methods for checking any value against any field. Ok, so if the user exists we can get the user_id and use that for the association with the message. Otherwise, we save a new user and get the last insert id. Then we can insert the message. We could check if the fields validate against the cake model then return errors, but we leave that for another day. Instead, we just return success or failure.

function amfCreate($message, $user) 
{
	$this->autoRender = false;//make sure cake does not want to render a view, cause this is a amf method
	$this->constructClasses();//this will create the models needed for this method
	
	$this->params['data']['BulletinBoard'] = $message;//set the cake data for the bulletin board
	
	//see if the user name exists
	$oldUser = $this->BulletinBoard->User->findByName($user['name']);//User is associated with BulletinBoard
	//if not then insert a new user
	if(empty($oldUser))
	{	
		$this->params['data']['User'] = $user;
		if(!$this->BulletinBoard->User->save($this->params['data']['User']))
		{
			return array("status" => "fail");
		}
		$this->params['data']['BulletinBoard']['user_id'] = $this->BulletinBoard->User->getLastInsertId();
	}
	else//use the old user
	{
		$this->params['data']['BulletinBoard']['user_id'] = $oldUser['User']['id'];
	}
	//save the message
	if($this->BulletinBoard->save($this->params['data']['BulletinBoard']))
	{
		return array("status" => "success");
	} 
	else 
	{
		return array("status" => "fail");
	}
}

Now that we can insert data, we need a way to read it back. We want to use recordsets so that large chunks of data will get to flash quickly. In reality, I think any time we can use recordsets, we should. So, below is the amfRead method add to the bottom of our BulletinBoardsController.

	
function amfRead($offset = '0', $limit='10') 
{
	$this->autoRender = false;
	$this->constructClasses();
	
	$page = intval($offset / $limit) + 1;//cake uses pages not offsets
 	$this->BulletinBoard->findAll(null,null,'BulletinBoard.created DESC',$limit,$page);
	$db = &ConnectionManager::getDataSource($this->BulletinBoard->useDbConfig);//get the db object so amfphp can pack it and send it
	return $db;
}	

Looking up at the amfRead method, we see two params coming in from flash. The offset or starting point for getting the records and the limit to the number that will be returned. As, the code comment tells us Cake uses pages instead of offsets, so we need to convert the data coming from flash. Of course, we could have change our flash code, but we didn't. So, we do the findAll, get the newest records. Then we return the *db* object. You could of course return the full array, but that would be too slow. In some instances just returning the find directly will be suitable. But here we want amfphp via the CakeMySqlAdapter to pick up the db object and pack things nicely into a recordset.

Wow, we got amf creating and reading pretty fast. Now we just need to setup a simple template for the view. First, we will add the helpers that we are going to use. You can see the var $helpers in the properties of the class. In this array we put Html, which is used a the layout along with Javascript, which is used to include the UFO.js file, and lastly we have the Cakeamfphp helper, which is a wrapper for the UFO.js. Then, we add the *view* method to the bottom of the BulletinBoardsController and the class will be complete.

<?php
class BulletinBoardsController extends AppController {
    
    var $name ='BulletinBoards';
	var $scaffold;
	var $methodTable;
	
	var $helpers = array('Html','Javascript','Cakeamfphp');

	function BulletinBoardsController() {
		//Define the methodTable
		$this->methodTable = array(
			"amfCreate" => array(
				"description" => "Inserts data from flash into DB",
				"access" => "remote"
	         ),
			"amfRead" => array(
				"description" => "Read data from DB and pass back recordset",
				"access" => "remote",
			)
		);
		parent::__construct();
	}
	
	function amfCreate($message, $user) 
	{
		$this->autoRender = false;//mae sure cake does not want to render a view, cause this is a amf method
		$this->constructClasses();//build the models here since we are not going through cake's dispather
		
		$this->params['data']['BulletinBoard'] = $message;//set the cake data for the bulletin board
		
		//see if the user name exists
		$oldUser = $this->BulletinBoard->User->findByName($user['name']);
		//if not then insert a new user
		if(empty($oldUser))
		{	
			$this->params['data']['User'] = $user;
			if(!$this->BulletinBoard->User->save($this->params['data']['User']))
			{
				return array("status" => "fail");
			}
			$this->params['data']['BulletinBoard']['user_id'] = $this->BulletinBoard->User->getLastInsertId();
		}
		else//use the old user
		{
			$this->params['data']['BulletinBoard']['user_id'] = $oldUser['User']['id'];
		}
		//save the message
		if($this->BulletinBoard->save($this->params['data']['BulletinBoard']))
		{
			return array("status" => "success");
		} 
		else 
		{
			return array("status" => "fail");
		}
	}
	
	function amfRead($offset = '0', $limit='10') 
	{
		$this->autoRender = false;
		$this->constructClasses();
		
		$page = intval($offset / $limit) + 1;//cake uses pages not offsets
	 	$this->BulletinBoard->findAll(null,null,'BulletinBoard.created DESC',$limit,$page);
    	$db = &ConnectionManager::getDataSource($this->BulletinBoard->useDbConfig);//get the db object so amfphp can pack it and send it
    	return $db;
	}
	
	function view(){}
}
?>
Yeah, its cool, only one line: function view(){} in the controller. Not much more to display the movie using the cakeamfphp helper.

Layout

Before getting into the view, we want to make sure we have the proper layout.

Create a default.thtml
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>CakePHP : A Rapid Development Framework :: <?php echo $title_for_layout?></title>
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
<?php echo $html->charset('UTF-8')?>
<?php echo $html->css('cake.default')?>
<!--[if lt IE 7]>
<?php echo $html->css('cake.ie');?>
<![endif]-->

<?php 
	if(isset($javascript))
	echo $javascript->link('ufo');
?>
</head>
<body>
<div id="wrapper">
<div id="header">
<?php echo $html->image('cake.logo.png', array('alt'=>'CakePHP : Rapid Development Framework', 'border'=>"0"))?>
</div>
<div id="content">
      <?php if (isset($this->controller->Session)) $this->controller->Session->flash(); ?>
      <?php echo $content_for_layout?>
</div>
<?php echo $cakeDebug;?>
<div id="footer">
          <p>CakePHP ::
          <a href="http://www.cakefoundation.org/pages/copyright/">© 2006 Cake Software Foundation, Inc.</a>
          </p>
          <br />
          <p>
<!--PLEASE USE ONE OF THE POWERED BY CAKEPHP LOGO-->
    <a href="http://www.cakephp.org/" target="_new">
        <?php echo $html->image('cake.power.png', array('alt'=>'CakePHP : Rapid Development Framework',
                                                        'height' => "15",
                                                        'width' => "80"))?>
       </a>
       </p>
</div>
</div>
</body>
</html>  

The above layout is basically a copy of the default cake layout. But we added the javascript call so the ufo.js will be included. This allows us to use the cakeamfphp helper to display our flash movies. Check out the CakePHP manual for more info.

The View

After we add the method to the controller, we need to create the corresponding view template

Create a view.thtml

In the view.thtml, we add one line.

<?php
echo $cakeamfphp->placeFlash('/swf/BulletinBoard-flash7.swf', '540' , '460' , '7');
?>

we are calling the location, setting the width, then height and the minimum version of the flash player.

Done.

That's all we need on the PHP side of things, so move on to step three if you are ready.

 
2 queries took 3 ms
NrQueryErrorAffectedNum. rowsTook (ms)
1DESC `bulletin_boards`772
2DESC `users`221