It does all sorts of cool shit, including generating form fields. They have a really helpful manual and IRC chat that is really all you need to learn. Check it out. BTW it's CakePHP not PHPCake.
I think what you will find most helpful is the Bake application, which is a command line app that can generate model files and scaffolding (which is like a rough draft of all the forms you need). Check out their blog video tutorial to see how easy it is.
Here's some sample code from one of my apps. It's the Restaurants controller.
[high=php]<?php
class RestaurantsController extends AppController {
// CakePHP vars
var $name = 'Restaurants';
var $helpers = array('Html');
// custom vars
var $r_name;
var $clear = false; // DELETE IN PRODUCTION.......
function beforeRender() {
/*
* Cache Handling - saves 9+ MySQL queries per load
* Careful not to confuse cache between restaurants
*/
// The user is on a new website ( DELETE FIRST CHECK IN PRODUCTION )
if(($this->clear) || ($this->r_name != Cache::read('restaurant_name'))) {
Cache::clear();
Cache::write('restaurant_name', $this->r_name);
}
// if the restaurant information is cached, use that. otherwise execute all model operations
if($cached_restaurant = Cache::read('restaurant')) {
$restaurant = $cached_restaurant;
} else {
/*
* fetch basic restaurant information and layout information
*/
$restaurant = $this->Restaurant->find('first', array(
'contain' => array(
'Layout' => array(
'Imagetype' => array(
'ImagetypesLayout'
)
)
),
'conditions' => array(
'Restaurant.name' => $this->r_name
)
));
/*
* "WORKAROUND" NOTE:
* For some strange reason, CakePHP does not recognize the relationship
* ImagetypesLayout HABTM Image unless it is defined on the fly.
* Further, the find() query above does not return this relationship even
* when explicitly requested. Therefore it is broken into the find() query
* above and this one below.
(!) * It would be nice to fix this, because it adds an extra query for every Imagetype.
*/
/*
* iterate through each Imagetype and find the associated image
*/
for($i = 0; $i < count($restaurant['Layout']['Imagetype']); $i++) {
/* fetch the image data, append it to ['Imagetype'][$i]['Image'] */
// see "WORKAROUND" NOTE above for reason for binding
$this->Restaurant->Layout->Imagetype->ImagetypesLayout->bindModel(
array('hasAndBelongsToMany' => array(
'Image' => array(
'className' => 'Image',
'joinTable' => 'images_imagetypes_layouts',
'foreignKey' => 'imagetypes_layout_id',
'associationForeignKey' => 'image_id',
'unique' => true,
)
)
));
// find the image associated with this Imagetype (if any), add to [...]['Imagetype'][$i]['Image']
$image = $this->Restaurant->Layout->Imagetype->ImagetypesLayout->find('first', array(
'contain' => array('Image'),
'conditions' => array(
'ImagetypesLayout.id' => $restaurant['Layout']['Imagetype'][$i]['ImagetypesLayout']['id']
)
));
// if there is one image found (max), there will be one key [0] in $image['Image'].
// if there are no images (min), count($image['Image']) will be 0.
// so if possible, move $image['Image'][0] up one level to [...]['Image']
$restaurant['Layout']['Imagetype'][$i]['Image'] = (count($image['Image']) == 0) ? $image['Image'] : $image['Image'][0];
// get Imagetype width and height info, which is specific to each layout. so fetch from ImagetypesLayout
$restaurant['Layout']['Imagetype'][$i]['width'] = $restaurant['Layout']['Imagetype'][$i]['ImagetypesLayout']['width'];
$restaurant['Layout']['Imagetype'][$i]['height'] = $restaurant['Layout']['Imagetype'][$i]['ImagetypesLayout']['height'];
/* clean up array */
// rename the key from $i to $i['title'] for readability, and destroy anything unnecessary
$imagetype_title = $restaurant['Layout']['Imagetype'][$i]['title'];
$restaurant['Layout']['Imagetype'][$imagetype_title] = $restaurant['Layout']['Imagetype'][$i];
unset($restaurant['Layout']['Imagetype'][$i]);
unset($restaurant['Layout']['Imagetype'][$imagetype_title]['title']);
unset($restaurant['Layout']['Imagetype'][$imagetype_title]['ImagetypesLayout']);
}
Cache::write('restaurant', $restaurant);
}
/*
* get everything ready for the layout/view
*/
// set variables
$this->set('restaurant', $restaurant['Restaurant']);
$this->set('r_name_for_layout', $restaurant['Restaurant']['name']);
$this->set('r_info_for_layout', $restaurant['Restaurant']['info']);
$this->set('data_for_layout', $restaurant['Layout']);
// turn on Themed mode and set the theme
$this->view = 'Theme';
$this->theme = 'layout' . $restaurant['Layout']['id'];
}
function page($r_name, $page, $clear = false) {
$this->r_name = $r_name;
if($clear) $this->clear = true;
$this->Session->setFlash($page);
}
function contact($r_name) {
$this->helpers = array('Map');
$this->r_name = $r_name;
$contact = $this->Restaurant->ContactInfo->find('first', array('contain' => array('State')));
$contact['ContactInfo']['state_full'] = $contact['State']['full'];
$contact['ContactInfo']['state_abbr'] = $contact['State']['abbreviation'];
unset($contact['State']);
$this->set('contact', $contact['ContactInfo']);
}
function menu($r_name) {
$this->r_name = $r_name;
}
function edit($r_name) {
$this->r_name = $r_name;
// this is the action that will open two frames
}
}
?>[/high]
Here is the view file for one of the actions there. It's the view file for contact, and outputs a google map using a custom helper.
[high=php]<!-- File: /app/views/restaurants/contact.ctp -->
<?php
$addr_html = $contact['addr_1'] . '<br />';
$addr_html .= (!empty($contact['addr_2'])) ? $contact['addr_2'] . '<br />' : '';
$addr_html .= $contact['city'] . ', ' . $contact['state_abbr'] . ' ' . $contact['zip'];
$phone = $contact['phone_area'] . '-' . $contact['phone_one'] . '-' . $contact['phone_two'];
$addr_for_map = $contact['addr_1'] . ' ' . $contact['addr_2'] . ', ' . $contact['city'] . ', ' . $contact['state_abbr'];
?>
<h2><?php echo $restaurant['name']; ?></h2>
<?php echo $addr_html; ?><br />
<?php echo $phone; ?>
<?php
// map code
$locations = array();
$locations[1]['address'] = $addr_for_map;
$locations[1]['title'] = $restaurant['name'];
echo $map->displaymap($locations, 500,500);
?>
<script type="text/javascript">onLoad();</script>[/high]
You can get a lot more complicated with view files when it comes to forms and everything. They're very powerful.
Here is the model file for the restaurants table. It is almost entirely auto generated by the bake application, and defines the relationships it has with other models to making querying the database very easy.
[high=php]<?php
class Restaurant extends AppModel {
var $actsAs = array('Containable');
var $name = 'Restaurant';
var $validate = array(
'name' => array('notempty'),
'layout_id' => array('numeric')
);
//The Associations below have been created with all possible keys, those that are not needed can be removed
var $belongsTo = array(
'Layout' => array(
'className' => 'Layout',
'foreignKey' => 'layout_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
var $hasOne = 'ContactInfo';
var $hasMany = array(
'Image' => array(
'className' => 'Image',
'foreignKey' => 'restaurant_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
)
);
var $hasAndBelongsToMany = array(
'Page' => array(
'className' => 'Page',
'joinTable' => 'pages_restaurants',
'foreignKey' => 'restaurant_id',
'associationForeignKey' => 'page_id',
'unique' => true,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
)
);
}
?>[/high]