Sources:
Structure of the main Cake folder
/cake/
./app (User application and files here ...)
./lib (CakePHP Core files are stored here)
./plugins ()
./vendors (Third party PHP Libraries)
Structure of /cake/app
folder. This is is where most of user development takes place. Cake emphasizes on convention over configuration.
/cake/app/
./Config - Config files used by Cake php
- database connection details, bootstrapping, etc
./Console - Console tasks for your application
./Controller - Application controllers and components
./Lib - Internally developed libraries only
./Locale - Internationalisation files
./Model - Application models
./Plugin - Plugin packages
./Tests - Application test cases
./tmp - Cake temporary files (sessions, logs, etc)
./Vendor - Third party classes / libraries. Use App Import function.
./View - Presentation files. Elements, error pages, helpers and layouts
./webroot - Point DocumentRoot for the application here.
- Also place CSS, Javascript and images here.
Security.salt
in /app/Config/core.php
Security.cipherSeed
in /app/Config/core.php
to a numeric (digits only) seed value/app/tmp
directory must be writable (chmod 777)/app/Config/database.php
Navigate to your project root folder and run the cake serve
command as below
cd /cakephp-folder/
## Start Cake Http Server at port 8080
$ ./lib/Cake/Console/cake serve -p 8080
Dispatcher -> Controller -> Model -> View -> html
When a user makes a request, the request goes to the dispatcher which makes decisions about which controller to call. The controllers then calls a model (if required), then the controller sends the results of the request to the view
CakePHP favours conventions over configurations.
By following a convention, developers avoid maintaining a log of configuration files.
index()
method is called.www.example.com/apples/
calls ApplesController
index() methodwww.example.com/apples/view
calls ApplesController
view() methodApplesController
will be defined in the file ApplesController.php
red_apples/go_pick/
is mapped to RedApplesController
's go_pick() actionclass NewsController {
protected function _findNewArticles () {}
public function latest() {
$this->_findNewArticles();
}
}
// www.example.com/news/latest # will be successful
// www.example.com/news/_findNewArticles # will fail as action name starts with an underscore.
category_types
the foreign key will be category_type_id
recipes
table and ingredients
table will be named ingredients_recipes
. Database table fields (ingredients_recipes.id, ingredients_recipes.ingredient_id, ingredients_recipes.recipe_id
), HABTMPeopleController->getReady()
will be /app/View/People/get_ready.cpt
Looking at all conventions at once
Database table: people
Model class: Person, "/app/Model/Person.php"
Controller class: PeopleController, "/app/Controller/PeopleController.php"
View Template: /app/Views/People/index.cpt
/app/View/Layouts
with file extension .cpt
default.cpt
. Pages use the default layout used by /app/View/Pages/home.cpt
page. Edit contents of the home.cpt file to see the effects.// app/Controller/AppController.php
// ...
class AppController extends Controller {
// beforeFilter is executed first in Cake classes before
// other functions in the class are called.
public function beforeFilter()
{
# Change default layout to Main.ctp
$this->layout = 'main';
}
}
Controllers contain the business logic. Methods to retrieve data, filter data, and other user related actions must be placed in Controllers.
Create a controller called UsersController populated with users in an array
<?php
// app/Controller/UsersController.php
class UsersController extends AppController
{
public function index () {
$this->set('page_title', 'Fun with Users');
$users = [
['id' => '01', 'first_name' => 'Sue', 'last_name' => 'Ja'],
['id' => '02', 'first_name' => 'Pat', 'last_name' => 'Lo'],
['id' => '03', 'first_name' => 'Cra', 'last_name' => 'Ha'],
['id' => '04', 'first_name' => 'Gie', 'last_name' => 'Cu']
];
// make users available to view
$this->set('users', $users);
}
}
Each time you are duplicating code in a single Controller or across controllers, you should consider extracting the duplicate code out to a Component.
Sample component
<?php
# File: /app/Controller/Component/MathComponent.php
App::uses('Component', 'Controller');
class MathComponent extends Component
{
// Given an array of data, extract a column, and sum numbers in the column
// Assume data contains sales figures
public function sum($data, $column) {
$total = Hash::extract($data, "{n}.Sale.$column");
return array_sum($total);
}
}
Using MathComponent in a controller
<?php
App::uses('AppController', 'Controller');
class UsersController extends AppController
{
// Use the Math custom component
public $components = [
'Math',
];
public function index() {
// Use Maths component
$sales = $this->Sale->find('all');
$total = $this->Math->sum($sales, 'price');
$this->set('sales', $sales);
}
}
Views must be used to present data to the user. Views must not process data. That is the job of a controller.
Create a view for UsersController above to display list of users in a table.
<?php # /app/View/Users/index.ctp ?>
<h1 style="font-size: 2em;"> <?php echo $page_title ?> </h1>
<table>
<tr>
<th>ID</th> <th>First name</th> <th>Last name</th>
</tr>
<?php
foreach($users as $user) {
$htm = "<td>{$user['id']}</td>";
$htm .= "<td>{$user['first_name']}</td>";
$htm .= "<td>{$user['last_name']}</td>";
echo "<tr> $htm </tr>";
}
?>
</table>
Navigate to http://cake.local/users to view users.
Models contain database related methods like Create, Read, Delete, Update, and Validation.
CREATE TABLE `cakedb`.`users` (
`id` INT NOT NULL,
`username` VARCHAR(45) NOT NULL,
`password` VARCHAR(45) NOT NULL,
`name` VARCHAR(45) NULL,
PRIMARY KEY (`id`, `username`, `password`));
Insert some data into the new database table
INSERT INTO cakedb.users
VALUES
(1, 'maria', 'pass', 'Mary Katana'),
(2, 'lucas', '1234', 'Luke Jambo'),
(3, 'maloi', 'pass', 'Lidan Bamai');
Create a CakePHP User Model
<?php
// app/Model/User.php
class User extends AppModel
{
//
}
Some controllers do not have a view. They may simply send back a JSON response to the calling API.
class UsersController extends AppController
{
public $autoRender = false;
public function index() {
$users = [
['id' => '01', 'first_name' => 'Sue', 'last_name' => 'Ja'],
['id' => '02', 'first_name' => 'Pat', 'last_name' => 'Lo'],
['id' => '03', 'first_name' => 'Cra', 'last_name' => 'Ha'],
['id' => '04', 'first_name' => 'Gie', 'last_name' => 'Cu']
];
// Response as JSON, no view
$this->response->type('json');
$this->response->body(json_encode($users));
// $this->response->body(var_dump($books)); // dump raw data to response
}
}
Modify the UsersController to retrieve data from the database
<?php
// app/Controller/UsersController.php
class UsersController extends AppController
{
public function index () {
$this->set('page_title', 'Users from database');
// Get users from database
$users = $this->User->find('all');
// // make users available to view
$this->set('users', $users);
}
}
Modify the index view, the return data structure has changed. Debug statements allow us to inspect the users variable coming from the controller.
<?php # /app/View/Users/index.ctp ?>
<?php
# debug users
// debug($users);
?>
<h1 style="font-size: 2em;"> <?php echo $page_title ?> </h1>
<table>
<tr>
<th>ID</th> <th>User name/th> <th>Full name</th>
</tr>
<?php
foreach($users as $key => $value) {
// debug($value);
// break;
$user = $value['User'];
$htm = "<td>{$user['id']}</td>";
$htm .= "<td>{$user['username']}</td>";
$htm .= "<td>{$user['name']}</td>";
echo "<tr> $htm </tr>";
}
// alt syntax, does the same thing only cleaner
foreach ($users as $user) {
$htm = "<td> {$user['User']['id']} </td>";
$htm .= "<td> {$user['User']['username']} </td>";
$htm .= "<td> {$user['User']['name']} </td>";
echo "<tr> $htm </tr>";
}
?>
</table>
To retrieve details of one User if a user navigates to '/Users/view/1' where 1 is an id in the database
<?php
// app/Controller/UsersController.php
class UsersController extends AppController
{
// ...
public function view ($id) {
$this->set('page_title', 'Selected user');
$users = $this->User->find(
"first",
array('conditions' => ['id' => $id])
);
# alternative use findById() magic method
# $users = $this->User->findById($id);
# using read methods
# $users = $this->User->read(null, $id); # null returns all fields
$this->set('user', $users);
}
}
Create view.ctp file
<?php # app/View/Users/view.ctp ?>
<h1 style="font-size: 2em;"> <?php echo $page_title ?> </h1>
<table>
<tr>
<th>ID</th>
<th>User name </th>
<th>Full name</th>
<th>Password</th>
</tr>
<?php
$htm = "<td> {$user['User']['id']} </td>";
$htm .= "<td> {$user['User']['username']} </td>";
$htm .= "<td> {$user['User']['name']} </td>";
$htm .= "<td> {$user['User']['password']} </td>";
echo "<tr> $htm </tr>";
?>
</table>
Modify UsersController.php and create an add() method.
<?php
// app/Controller/UsersController.php
class UsersController extends AppController
{
// ....
// save user data to database
public function add () {
$this->set('page_title', 'Create user');
if ($this->request->isPost()) {
if ($this->User->save($this->request->data)) {
// successfully saved
$this->Session->setFlash(__("New user created successfully."));
$this->redirect(['action' => 'index']);
} else {
// failed
$this->Session->setFlash(__("Failed to create user"));
}
}
}
}
Create an add.ctp view
<?php
// app/View/Users/add.ctp
// use Form helpers to create a form
echo $this->Form->create('User');
echo $this->Form->input('id');
echo $this->Form->input('username');
echo $this->Form->input('password');
echo $this->Form->input('name');
echo $this->Form->end('Submit');
Use Model::set()
to change values of a model before save
<?php
// Change one field at a time
// Change user's title to doctor and save
$this->User->set('title', 'Doctor');
$this->User->save();
// Set multiple fields at a time and save
$this->User->set([
'firstname' => 'Lucas',
'title' => 'Doctor',
'dob' => '1980-01-01'
]);
$this->User->save();
// get ID of newly saved record
$userId = $this->User->id;
Validation rules are added to the Model within the constructor.
Example below showing how to validates our User model
<?php
// app/Model/User.php
class User extends AppModel
{
public function __construct($id = null, $table = null, $ds = null)
{
parent::__construct($id, $table, $ds);
$this->validate = array (
'username' => [
'rule' => 'alphaNumeric',
'allowEmpty' => false,
'message' => __('must be alpha numeric')
],
'password' => [
'rule' => ['minLength' => '5'],
'allowEmpty' => false,
'message' => __('Try a longer password')
],
'name' => [
'rule' => 'alphaNumeric',
'allowEmpty' => false,
'message' => __('must be alpha numeric')
]
);
}
}
Behaviours in CakePHP refers to helpers (or logic) than can be used across multiple models. If you are duplicating code in a model or across Models, extract the duplicate code to a Behaviour class.
// Create a link to a model action.
// this code must go in a view template
<tr><td>
<?php // view link
// e.g. http://cake.local/topics/view/1
echo $this->HTML->link(
$data['Topic']['title], array(
'controller' => 'topics',
'action' => 'view',
$data['Topic']['id']
)
);
?>
</td></tr>
<tr><td>
<?php // Delete link
// e.g. http://cake.local/topics/delete/1
echo $this->Form->postLink(
'Delete',
array(
'controller' => 'topics',
'action' => 'delete',
$data['Topic']['id']
),
array(
'confirm' => 'Are you sure?'
)
);
?>
</td></tr>
/// Delete method in TopicsController
public function delete($id)
{
$this->Topic->id = $id;
if ($this->request->is(['post', 'put']) {
$this->Topic->delete();
// $this->Session->setFlash('Topi deleted successfully');
// $this->redirect('index');
return true;
}
return false;
}
Delete a user given an ID
<?php
App::uses('AppModel', 'Model');
App::uses('SimplePasswordHasher', 'Controller/Component/Auth');
class User extends AppModel {
public $validate = array(
'username' => array(
'required' => array(
'rule' => 'notEmpty',
'message' => 'Please enter a username'
)
),
);
//
public function beforeSave($options = []) {
if (!parent::beforeSave($options)) {
return false;
}
$pwd = $this->data[$this->alias]['password'];
if (isset($pwd)) {
$this->data[$this->alias]['password'] = (new SimplePasswordHasher())->hash($pwd);
}
return true;
}
// Delete user
// Source: CakePHP 2 Essential Training
public function delete($id = null) {
$this->User->id = $id;
if (!$this->User->exists()) {
throw new NotFoundException(__('Invalid user'));
}
// important! Prevents users scrapping your site to delete data
// if they accidentally hit this endpoint
$this->request->allowMethod('post', 'delete');
if ($this->User->delete()) {
$this->Flash->success(__('User has been deleted'));
} else {
$this->Flash->error(__('Could not delete user'));
}
return $this->redirect(
['action' => 'index'];
)
}
// create a virtual field called full_name
// concatenate firstname and lastname using SQL commands
public $virtualFields = [
'full_name' => '`firstname` + ` ` + `lastname`',
];
}
Source: CakePHP 2 Essential Training Add this method to a controller.
<?php
App::uses('AppController', 'Controller');
class UsersController extends AppController
{
// Components that must be available to very controller your app must be defined in AppController.php
// Use the Paginator component
public $components = [
'Paginator',
];
public function index() {
// Paginate
$this->Paginator->settings = [
'limit' => 25,
'order' => array('created' => 'DESC');
];
$this->set('users', $this->Paginator->paginate());
}
}