Search icon CANCEL
Subscription
0
Cart icon
Cart
Close icon
You have no products in your basket yet
Save more on your purchases!
Savings automatically calculated. No voucher code required
Arrow left icon
All Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletters
Free Learning
Arrow right icon
$9.99 | ALL EBOOKS & VIDEOS
Save more on purchases! Buy 2 and save 10%, Buy 3 and save 15%, Buy 5 and save 20%
CakePHP 2 Application Cookbook
CakePHP 2 Application Cookbook

CakePHP 2 Application Cookbook: Over 60 useful recipes for rapid application development with the CakePHP framework.

eBook
$32.99 $9.99
Print
$54.99
Subscription
$15.99 Monthly

What do you get with Print?

Product feature icon Instant access to your digital eBook copy whilst your Print order is Shipped
Product feature icon Black & white paperback book shipped to your address
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
Buy Now
Table of content icon View table of contents Preview book icon Preview Book

CakePHP 2 Application Cookbook

Chapter 1. Lightning Introduction

In this chapter, we will cover the following recipes:

  • Listing and viewing records

  • Adding and editing records

  • Deleting records

  • Adding a login

  • Including a plugin

Introduction


CakePHP is a web framework for rapid application development (RAD), which admittedly covers a wide range of areas and possibilities. However, at its core, it provides a solid architecture for the CRUD (create/read/update/delete) interface.

This chapter is a set of quick-start recipes to dive head first into using the framework and build out a simple CRUD around product management.

If you want to try the code examples on your own, make sure that you have CakePHP 2.5.2 installed and configured to use a database—you should see something like this:

Listing and viewing records


To begin, we'll need a way to view the products available and also allow the option to select and view any one of those products.

In this recipe, we'll create a listing of products as well as a page where we can view the details of a single product.

Getting ready

To go through this recipe, we'll first need a table of data to work with. So, create a table named products using the following SQL statement:

CREATE TABLE products (
  id VARCHAR(36) NOT NULL,
  name VARCHAR(100),
  details TEXT,
  available TINYINT(1) UNSIGNED DEFAULT 1,
  created DATETIME,
  modified DATETIME,
  PRIMARY KEY(id)
);

We'll then need some sample data to test with, so now run this SQL statement to insert some products:

INSERT INTO products (id, name, details, available, created, modified)
VALUES
('535c460a-f230-4565-8378-7cae01314e03', 'Cake', 'Yummy and sweet', 1, NOW(), NOW()),
('535c4638-c708-4171-985a-743901314e03', 'Cookie', 'Browsers love cookies', 1, NOW(), NOW()),
('535c49d9-917c-4eab-854f-743801314e03', 'Helper', 'Helping you all the way', 1, NOW(), NOW());

Before we begin, we'll also need to create ProductsController. To do so, create a file named ProductsController.php in app/Controller/ and add the following content:

<?php
App::uses('AppController', 'Controller');

class ProductsController extends AppController {

  public $helpers = array('Html', 'Form');

  public $components = array('Session', 'Paginator');

}

Now, create a directory named Products/ in app/View/. Then, in this directory, create one file named index.ctp and another named view.ctp.

How to do it...

Perform the following steps:

  1. Define the pagination settings to sort the products by adding the following property to the ProductsController class:

    public $paginate = array(
      'limit' => 10
    );
  2. Add the following index() method in the ProductsController class:

    public function index() {
      $this->Product->recursive = -1;
      $this->set('products', $this->paginate());
    }
  3. Introduce the following content in the index.ctp file that we created:

    <h2><?php echo __('Products'); ?></h2>
    <table>
      <tr>
        <th><?php echo $this->Paginator->sort('id'); ?></th>
        <th><?php echo $this->Paginator->sort('name'); ?></th>
        <th><?php echo $this->Paginator->sort('created'); ?></th>
      </tr>
      <?php foreach ($products as $product): ?>
        <tr>
          <td><?php echo $product['Product']['id']; ?></td>
          <td>
            <?php
            echo $this->Html->link($product['Product']['name'], array('controller' => 'products', 'action' => 'view', $product['Product']['id']));
            ?>
          </td>
          <td><?php echo $this->Time->nice($product['Product']['created']); ?></td>
        </tr>
      <?php endforeach; ?>
    </table>
    <div>
      <?php echo $this->Paginator->counter(array('format' => __('Page {:page} of {:pages}, showing {:current} records out of {:count} total, starting on record {:start}, ending on {:end}'))); ?>
    </div>
    <div>
      <?php
      echo $this->Paginator->prev(__('< previous'), array(), null, array('class' => 'prev disabled'));
      echo $this->Paginator->numbers(array('separator' => ''));
      echo $this->Paginator->next(__('next >'), array(), null, array('class' => 'next disabled'));
      ?>
    </div>
  4. Returning to the ProductsController class, add the following view() method to it:

    public function view($id) {
      if (!($product = $this->Product->findById($id))) {
        throw new NotFoundException(__('Product not found'));
      }
      $this->set(compact('product'));
    }
  5. Introduce the following content in the view.ctp file:

    <h2><?php echo h($product['Product']['name']); ?></h2>
    <p>
      <?php echo h($product['Product']['details']); ?>
    </p>
    <dl>
      <dt><?php echo __('Available'); ?></dt>
      <dd><?php echo __((bool)$product['Product']['available'] ? 'Yes' : 'No'); ?></dd>
      <dt><?php echo __('Created'); ?></dt>
      <dd><?php echo $this->Time->nice($product['Product']['created']); ?></dd>
      <dt><?php echo __('Modified'); ?></dt>
      <dd><?php echo $this->Time->nice($product['Product']['modified']); ?></dd>
    </dl>
  6. Now, navigating to /products in your web browser will display a listing of the products, as shown in the following screenshot:

  7. Clicking on one of the product names in the listing will redirect you to a detailed view of the product, as shown in the following screenshot:

How it works...

We started by defining the pagination setting in our ProductsController class, which defines how the results are treated when returning them via the Paginator component (previously defined in the $components property of the controller). Pagination is a powerful feature of CakePHP, which extends well beyond simply defining the number of results or sort order.

We then added an index() method to our ProductsController class, which returns the listing of products. You'll first notice that we accessed a $Product property on the controller. This is the model that we are acting against to read from our table in the database. We didn't create a file or class for this model, as we're taking full advantage of the framework's ability to determine the aspects of our application through convention. Here, as our controller is called ProductsController (in plural), it automatically assumes a Product (in singular) model. Then, in turn, this Product model assumes a products table in our database. This alone is a prime example of how CakePHP can speed up development by making use of these conventions.

You'll also notice that in our ProductsController::index() method, we set the $recursive property of the Product model to -1. This is to tell our model that we're not interested in resolving any associations on it. Associations are other models that are related to this one. This is another powerful aspect of CakePHP. It allows you to determine how models are related to each other, allowing the framework to dynamically generate those links so that you can return results with the relations already mapped out for you. We then called the paginate() method to handle the resolving of the results via the Paginator component.

Tip

It's common practice to set the $recursive property of all models to -1 by default. This saves heavy queries where associations are resolved to return the related models, when it may not be necessary for the query at hand. This can be done via the AppModel class, which all models extend, or via an intermediate class that you may be using in your application.

We had also defined a view($id) method, which is used to resolve a single product and display its details. First, you probably noticed that our method receives an $id argument. By default, CakePHP treats the arguments in methods for actions as parts of the URL. So, if we have a product with an ID of 123, the URL would be /products/view/123. In this case, as our argument doesn't have a default value, in its absence from the URL, the framework would return an error page, which states that an argument was required. You will also notice that our IDs in the products table aren't sequential numbers in this case. This is because we defined our id field as VARCHAR(36). When doing this, CakePHP will use a Universally Unique Identifier (UUID) instead of an auto_increment value.

Tip

To use a UUID instead of a sequential ID, you can use either CHAR(36) or BINARY(36). Here, we used VARCHAR(36), but note that it can be less performant than BINARY(36) due to collation.

The use of UUID versus a sequential ID is usually preferred due to obfuscation, where it's harder to guess a string of 36 characters, but also more importantly, if you use database partitioning, replication, or any other means of distributing or clustering your data.

We then used the findById() method on the Product model to return a product by it's ID (the one passed to the action). This method is actually a magic method. Just as you can return a record by its ID, by changing the method to findByAvailable(). For example, you would be able to get all records that have the given value for the available field in the table. These methods are very useful to easily perform queries on the associated table without having to define the methods in question.

We also threw NotFoundException for the cases in which a product isn't found for the given ID. This exception is HTTP aware, so it results in an error page if thrown from an action.

Finally, we used the set() method to assign the result to a variable in the view. Here we're using the compact() function in PHP, which converts the given variable names into an associative array, where the key is the variable name, and the value is the variable's value. In this case, this provides a $product variable with the results array in the view. You'll find this function useful to rapidly assign variables for your views.

We also created our views using HTML, making use of the Paginator, Html, and Time helpers. You may have noticed that the usage of TimeHelper was not declared in the $helpers property of our ProductsController. This is because CakePHP is able to find and instantiate helpers from the core or the application automatically, when it's used in the view for the first time. Then, the sort() method on the Paginator helper helps you create links, which, when clicked on, toggle the sorting of the results by that field. Likewise, the counter(), prev(), numbers(), and next() methods create the paging controls for the table of products.

You will also notice the structure of the array that we assigned from our controller. This is the common structure of results returned by a model. This can vary slightly, depending on the type of find() performed (in this case, all), but the typical structure would be as follows (using the real data from our products table here):

Array
(
  [0] => Array
  (
    [Product] => Array
    (
      [id] => 535c460a-f230-4565-8378-7cae01314e03
      [name] => Cake
      [details] => Yummy and sweet
      [available] => true
      [created] => 2014-06-12 15:55:32
      [modified] => 2014-06-12 15:55:32
    )
  )
  [1] => Array
  (
    [Product] => Array
    (
      [id] => 535c4638-c708-4171-985a-743901314e03
      [name] => Cookie
      [details] => Browsers love cookies
      [available] => true
      [created] => 2014-06-12 15:55:33
      [modified] => 2014-06-12 15:55:33
    )
  )
  [2] => Array
  (
    [Product] => Array
    (
      [id] => 535c49d9-917c-4eab-854f-743801314e03
      [name] => Helper
      [details] => Helping you all the way
      [available] => true
      [created] => 2014-06-12 15:55:34
      [modified] => 2014-06-12 15:55:34
    )
  )
)

We also used the link() method on the Html helper, which provides us with the ability to perform reverse routing to generate the link to the desired controller and action, with arguments if applicable. Here, the absence of a controller assumes the current controller, in this case, products.

Finally, you may have seen that we used the __() function when writing text in our views. This function is used to handle translations and internationalization of your application. When using this function, if you were to provide your application in various languages, you would only need to handle the translation of your content and would have no need to revise and modify the code in your views.

There are other variations of this function, such as __d() and __n(), which allow you to enhance how you handle the translations. Even if you have no initial intention of providing your application in multiple languages, it's always recommended that you use these functions. You never know, using CakePHP might enable you to create a world class application, which is offered to millions of users around the globe!

See also

Adding and editing records


While listing and viewing records is handy, the ability to create and edit records allows you to build up and maintain your data.

In this recipe, we'll create actions to both add new products and edit the existing ones in our database.

Getting ready

For this recipe, we'll continue using the products table from the previous recipe. We'll also extend the ProductsController that was created.

For the views, we'll add add.ctp and edit.ctp files to our app/View/Products/ directory, and also a form.ctp file in the Products/ directory that we'll create in app/View/Elements/.

How to do it...

Perform the following steps:

  1. Add the following add() method to the ProductsController class:

    public function add() {
      if ($this->request->is('post')) {
        $this->Product->create();
        if ($this->Product->save($this->request->data)) {
          $this->Session->setFlash(__('New product created'));
          return $this->redirect(array('action' => 'index'));
        }
        $this->Session->setFlash(__('Could not create product'));
      }
    }
  2. Just below the add() method, also add an edit() method:

    public function edit($id) {
      $product = $this->Product->findById($id);
      if (!$product) {
        throw new NotFoundException(__('Product not found'));
      }
      if ($this->request->is('post')) {
        $this->Product->id = $id;
        if ($this->Product->save($this->request->data)) {
          $this->Session->setFlash(__('Product updated'));
          return $this->redirect(array('action' => 'index'));
        }
        $this->Session->setFlash(__('Could not update product'));
      } else {
        $this->request->data = $product;
      }
    }
  3. Introduce the following content in the form.ctp element file:

    <?php
    echo $this->Form->create('Product');
    echo $this->Form->inputs();
    echo $this->Form->end(__('Submit'));
  4. Introduce the following content in the add.ctp file:

    <?php echo $this->element('Products/form'); ?>
  5. The edit.ctp file will also take the same content, but the header text will be changed to the following code:

    <?php echo $this->element('Products/form'); ?>
  6. Return to the index.ctp file, and change it's content to the following:

    <h2><?php echo __('Products'); ?></h2>
    <div>
      <?php echo $this->Html->link(__('Add new product'), array('action' => 'add')); ?>
    </div>
    <table>
      <tr>
        <th><?php echo $this->Paginator->sort('id'); ?></th>
        <th><?php echo $this->Paginator->sort('name'); ?></th>
        <th><?php echo $this->Paginator->sort('created'); ?></th>
        <th><?php echo __('Actions'); ?></th>
      </tr>
      <?php foreach ($products as $product): ?>
        <tr>
          <td><?php echo $product['Product']['id']; ?></td>
          <td><?php echo $this->Html->link($product['Product']['name'], array('action' => 'view', $product['Product']['id'])); ?></td>
          <td><?php echo $this->Time->nice($product['Product']['created']); ?></td>
          <td><?php echo $this->Html->link(__('Edit'), array('action' => 'edit', $product['Product']['id'])); ?></td>
        </tr>
      <?php endforeach; ?>
    </table>
    <div>
      <?php echo $this->Paginator->counter(array('format' => __('Page {:page} of {:pages}, showing {:current} records out of {:count} total, starting on record {:start}, ending on {:end}'))); ?>
    </div>
    <div>
      <?php
      echo $this->Paginator->prev(__('< previous'), array(), null, array('class' => 'prev disabled'));
      echo $this->Paginator->numbers(array('separator' => ''));
      echo $this->Paginator->next(__('next >'), array(), null, array('class' => 'next disabled'));
      ?>
    </div>
  7. Navigate to /products in your web browser, and click one of the Edit links to modify a product. The following screenshot shows the screen that will appear:

  8. Return to /products, and click on the Add new product link to create a new product. The following screenshot shows the screen that will appear:

How it works...

Here, we extended our previous recipe by adding some extra methods to create new products and edit the existing ones. The add() method first checks whether the current request has been made using the HTTP POST method. If that's the case, we call the create() method on the Product model. This doesn't create a new record yet, but instead it prepares our model object for a new record to be created. We then call the save() method, passing the data provided in the request to it. The framework handles this internally through the Form helper, which we'll see in a moment. The condition checks whether the save is successful (here, a new record is created), and if so, it calls the setFlash() method on our Session component to register a success message to be displayed on the page that follows. We do the same in the event that the record could not be saved, and it provided a failure message. We then wrap up the method by redirecting the request to our index() action.

For the edit() method, we first check that the product for the given ID actually exists using the findById() method on the Product model. See the previous recipe, Listing and viewing records, for details on finding records. If the product doesn't exist, a NotFoundException is thrown; this is rendered as an error page. As with the add() method, we first check that the request was made via POST. However, instead of calling the create() method on our Product model, we set the $id property with the $id argument passed to our action. We then follow the same process of calling the save() method with the request data, as well as setting the result messages for the view and redirecting the request.

We finalize our edit action by populating the request data if it does not exist so that when you visit the form for editing, it's populated with the existing values from the products table.

For our views, we've taken the initiative to use an element. These are reusable sections of our views, which allow us to segment and organize our visual interface and cut down on duplicate code. Here, we've done this to avoid declaring the same form twice and reuse the same one instead. The framework is able to distinguish between the two (adding a record and editing a record) by the presence of an ID. In which case, it assumes that we're editing a record instead of creating one. In this file, we use the Form helper to generate a new form using the create() method and passing the name of the model to it, it will act against. We also called the inputs() method to create the required inputs based on the table schema and then called the end() method to complete the form.

For our add.ctp and edit.ctp view files, we included our element using the element() method, passing it the location of our form.ctp file. You'll notice that we created our element in the Products/ directory, as the form is intended for a product. We could change the contents of our element to receive the model name via an element parameter, making it more dynamic and, therefore, reusable even further. Finally, we updated the index.ctp view to include an Edit option using the link() method from the Html helper.

You'll also see that we passed the ID of each product to this method in our foreach() statement, thus generating a link for each product with its unique ID as part of the URL. We also added a link to "add a new product", which redirects you to the new add() action to create a new record in the products table.

See also

Deleting records


Just as we can list and view records, as well as edit and create new ones, you'll also want to be familiar with how to delete records using CakePHP.

In this recipe, we'll create an action that allows us to delete records from our table.

Getting ready

As with the previous recipe, we'll continue using the products table and also extend the ProductsController that we created.

How to do it...

Perform the following steps:

  1. Add the following delete() method to the ProductsController class:

    public function delete($id) {
      if (!$this->request->is('post')) {
        throw new MethodNotAllowedException();
      }
      if ($this->Product->delete($id)) {
        $this->Session->setFlash(__('Product removed: %s', $id));
        return $this->redirect(array('action' => 'index'));
      }
      $this->Session->setFlash(__('Could not remove product'));
      return $this->redirect($this->referer());
    }
  2. Change the content of the index.ctp file to the following:

    <h2><?php echo __('Products'); ?></h2>
    <div>
      <?php echo $this->Html->link(__('Add new product'), array('action' => 'add')); ?>
    </div>
    <table>
      <tr>
        <th><?php echo $this->Paginator->sort('id'); ?></th>
        <th><?php echo $this->Paginator->sort('name'); ?></th>
        <th><?php echo $this->Paginator->sort('created'); ?></th>
        <th><?php echo __('Actions'); ?></th>
      </tr>
      <?php foreach ($products as $product): ?>
        <tr>
          <td><?php echo $product['Product']['id']; ?></td>
          <td><?php echo $this->Html->link($product['Product']['name'], array('action' => 'view', $product['Product']['id'])); ?></td>
          <td><?php echo $this->Time->nice($product['Product']['created']); ?></td>
          <td>
            <?php
            echo $this->Html->link(__('Edit'), array('action' => 'edit', $product['Product']['id']));
            echo $this->Form->postLink(__('Delete'), array('action' => 'delete', $product['Product']['id']), array('confirm' => 'Delete this product?'));
            ?>
          </td>
        </tr>
      <?php endforeach; ?>
    </table>
    <div>
      <?php echo $this->Paginator->counter(array('format' => __('Page {:page} of {:pages}, showing {:current} records out of {:count} total, starting on record {:start}, ending on {:end}'))); ?>
    </div>
    <div>
      <?php
      echo $this->Paginator->prev(__('< previous'), array(), null, array('class' => 'prev disabled'));
      echo $this->Paginator->numbers(array('separator' => ''));
    echo $this->Paginator->next(__('next >'), array(), null, array('class' => 'next disabled'));
      ?>
    </div>

How it works...

In this recipe, we added a delete() method to our ProductsController class. This first checks whether the HTTP method used to call the action was POST. In the event it wasn't, we throw a MethodNotAllowedException exception, which is presented as an error page when it is thrown from an action. This is to protect our application against attempts to delete data via a simple URL. If the HTTP method is correct, we proceed to call the delete() method on the Product model, passing to it the ID of the record to delete. If it is successful, we call the setFlash() method on our Session component with a message confirming the removal or with a failure message if the delete() method fails. Finally, we redirect the request to the index() action if the record was deleted successfully—if not, then we are redirecting the user back to the URL where the delete() method was called from. It may also be worth mentioning that the delete() action itself doesn't have a view associated with it, as nothing is displayed to the user as part of the request.

We then returned to the index.ctp view to add an option to delete a product using the postLink() method of the Form helper. This creates a form with a link that allows us to use POST when clicking on the link which submits the form. You will notice that this takes a third argument, which is an array with a confirm key. This prompts the user to confirm the action before submitting the request to delete the record. This is always recommended when dealing with actions that delete records, in case of a mistake on the user's behalf.

See also

Adding a login


It's not going to be long before you need to control access to certain areas of your application.

In this recipe, we'll look at adding a basic authentication layer to our existing products section, with a login view to enter your credentials.

Getting ready

For this recipe, we'll need a table for our users. Create a table named users, using the following SQL statement:

CREATE TABLE users (
  id VARCHAR(36) NOT NULL,
  username VARCHAR(20),
  password VARCHAR(100),
  created DATETIME,
  modified DATETIME,
  PRIMARY KEY(id)
);

We'll then create a User.php file in app/Model/, which will have the following content:

<?php
App::uses('AppModel', 'Model');
App::uses('SimplePasswordHasher', 'Controller/Component/Auth');
    
class User extends AppModel {
}

We'll also need a UsersController.php file in app/Controller/ with the following content:

<?php
App::uses('AppController', 'Controller');
    
class UsersController extends AppController {
}

Finally, also create a Users directory in app/View/, and create register.ctp and login.ctp files in the new directory.

How to do it...

Perform the following steps:

  1. Add the validation rules to the User model in app/Model/User.php with the following $validate property:

    public $validate = array(
      'username' => array(
        'required' => array(
          'rule' => 'notEmpty',
          'message' => 'Please enter a username'
        )
      ),
      'password' => array(
        'required' => array(
          'rule' => 'notEmpty',
          'message' => 'Please enter a password'
        )
      )
    );
  2. In the same class, add this beforeSave() method:

    public function beforeSave($options = array()) {
      if (!parent::beforeSave($options)) {
        return false;
      }
      if (isset($this->data[$this->alias]['password'])) {
        $hasher = new SimplePasswordHasher();
        $this->data[$this->alias]['password'] = $hasher->hash($this->data[$this->alias]['password']);
      }
      return true;
    }
  3. Locate the AppController.php file in app/Controller/, and add the following $components property to the same class:

    public $components = array(
      'Session',
      'Auth' => array(
        'loginRedirect' => array('controller' => 'products'),
        'logoutRedirect' => array(
          'controller' => 'users',
          'action' => 'login'
        )
      )
    );
  4. Open the UsersController.php file and add the following methods:

    public function beforeFilter() {
      parent::beforeFilter();
      $this->Auth->allow();
    }
    
    public function register() {
      if ($this->request->is('post')) {
        $this->User->create();
        if ($this->User->save($this->request->data)) {
          $this->Session->setFlash(__('New user registered'));
          return $this->redirect(array('action' => 'login'));
        }
        $this->Session->setFlash(__('Could not register user'));
      }
    }
    
    public function login() {
      if ($this->request->is('post')) {
        if ($this->Auth->login()) {
          return $this->redirect($this->Auth->redirectUrl());
        }
        $this->Session->setFlash(__('Incorrect username or password'));
      }
    }
    
    public function logout() {
      return $this->redirect($this->Auth->logout());
    }
  5. Locate the register.ctp file in app/View/Users/ and introduce the following content:

    <h2><?php echo __('Register'); ?></h2>
    <?php
    echo $this->Form->create('User');
    echo $this->Form->inputs();
    echo $this->Form->end(__('Register'));
  6. In the same directory, open the login.ctp file, and add the following content:

    <h2><?php echo __('Login'); ?></h2>
    <?php
    echo $this->Session->flash('auth');
    echo $this->Form->create('User');
    echo $this->Form->inputs(array(
      'username',
      'password',
      'legend' => __('Login, please')
    ));
    echo $this->Form->end(__('Sign In'));

How it works...

For this recipe, we first included a $validate property in our User model. This array is used to define the validation rules applied when creating or modifying records. Here, we simply defined the username and password fields as required, not allowing an empty value and specifying some custom messages to be returned by the model if the fields fail to validate correctly. There are plenty more validation rules such as alphaNumeric, minLength, between, date, and email. You can also create your own rules for custom validation.

After setting up our validation, we also added a beforeSave() method. This is one of the callback methods available on all models, to hook into a certain point of the process. These are beforeFind(), afterFind(), beforeSave(), afterSave(), beforeDelete(), afterDelete(), beforeValidate(), afterValidate(), and onError(). In our method, we added some logic to process the password and generated a hash value before saving it to our users table. This way, we store a representation of the password, instead of the password itself. This is very important as you don't want anyone viewing the actual passwords in your database.

The SimplePasswordHasher class helps us here by providing an easy API to quickly generate hashes of any value. You will also notice our use of $this->alias. This is the recommended method of referring to the model, to allow for extensions or aliasing of a model without impacting the internal logic.

We then added the Auth component to the $components array of AppController, with some global settings. This controller acts as a base controller for your application, so behavior or functionality that needs to be propagated to your entire application should be added in this class. Here, we defined the loginRedirect and logoutRedirect settings, which define the controller and action to redirect to in each case (after login or after logout). Where the action is not defined, index will be assumed by default.

After that, we proceeded to add some methods to our UsersController. The first of these is the beforeFilter() method. This is one of the callback methods available on all controllers, which include beforeFilter(), afterFilter(), beforeRender(), and beforeRedirect(). Here, we first called parent::beforeFilter() to make sure that we included any logic that has been defined by AppController. We then called the allow() method on the Auth component to allow access to the methods in the controller. Here, you could also pass some method names to only allow certain action, or also use the deny() method to explicitly deny some actions and require login.

After that, we added a register() method, using the same logic to create a record as we did in the ProductsController from a previous recipe, in order to allow the creation of new users. Here, we've only used a simple example, without contemplating any postregistration checks, such as a token via e-mail for confirmation. You can easily include this and many more features using a plugin, such as the CakeDC Users plugin, found at https://github.com/CakeDC/users.

We also included both the login() and logout() methods, which use the API exposed by the Auth component to process the user login. As we're following convention by creating a users table with the username and password fields, we take advantage of the framework's ability to configure most of the sign-in process for us. In our login action, we first called the login() method on the Auth component, which internally checks the data passed in the request. If this is successful, we redirect the user using the redirect() method from the Auth component, which takes the target we previously defined in the loginRedirect setting. If the process were to fail, we stay in the same action but use the setFlash() method from the Session component to display a message to the user that the sign in was unsuccessful. The logout() method is even easier, simply calling the logout() method on the Auth component and redirecting the user to that location. Here, as with the login() method, we use the logoutRedirect setting we had previously defined. As you can see so far, authentication in CakePHP is really a piece of cake.

We then went on to create our views to register a new user and sign in. In our first view, register.ctp, we used the create() method of the Form helper to create a form for the User model. We then used the inputs() methods to render the required inputs and finally called end(), passing our text for the submit button. In our login.ctp view, we did almost the same; except here, we also enlisted required inputs, added custom form legend, and called the flash() method on the Session helper, passing auth as the argument to it. This renders a location to collect the flash messages sent from the Auth component, such as the message we display when the login fails. The use of the auth value allows you to easily manage the messages being collected, in case you'd like them to be displayed differently or in different locations of your view.

If you go to /products in your browser now, you will be automatically redirected to the login page, and the default message for unauthorized access will be displayed, as shown in the following screenshot:

Of course, to log in, you'll have to register a new user account first by going to the /users/register URL in your browser.

See also

Including a plugin


One of the greatest benefits of using CakePHP is its extensibility. Using plugins, you can extend and enhance the core functionality, which provides you with even more cake than you bargained for!

In this recipe, we'll look at loading a plugin using DebugKit, the official development and debugging tool for the framework, as an example.

Getting ready

We'll need to have DebugKit included in the application file structure before we begin. There are a couple of ways to do this.

Git clone or submodule

If you use Git, you can create a clone of the code by executing the following command from your app/ directory on the command line:

$ git clone https://github.com/cakephp/debug_kit.git Plugin/DebugKit

If you already have your application under version control using Git, you can also add it as a submodule. Assuming that the base of your repository is the app/ folder, you would run the following command:

$ git submodule add https://github.com/cakephp/debug_kit.git app/Plugin/DebugKit

Using Composer

If you're using Composer as your dependency manager, you can simply include DebugKit as a plugin by adding the following code to your composer.json file:

{
  "require": {
    "cakephp/debug_kit": "2.2.*"
  }
}

After updating your composer.json file, simply update your application's dependencies using Composer.

Downloading files

An alternative method is to simply download the files and include them manually in your application from the following location:

https://github.com/cakephp/debug_kit/archive/master.zip

How to do it...

Perform the following steps:

  1. Add the following code to your bootstrap.php file, found in app/Config/:

    CakePlugin::load('DebugKit');
  2. Add the following code to your AppController in app/Controller/:

    public $components = array(
      'Session',
      'Auth',
      'DebugKit.Toolbar'
    );
  3. Open your core.php file in app/Config/, and check if the following configuration value is set to a value higher than 0:

    Configure::write('debug', 2);
  4. Now, load your application in your browser, and you'll find an icon in the top right-hand corner of the screen, as shown in the following screenshot:

  5. Click on the icon to see various panels (shown in the following screenshots) with data related to the current request:

How it works...

When we call CakePlugin::load(), we request the framework to load the plugin by the given name, which also accepts an optional second argument, which is the configuration array for the plugin. We had already included the DebugKit/ directory in app/Plugin/, which is where plugins are located. You can change the location of where the plugins are loaded from using the path option in the plugin configuration array.

After loading the plugin, we included the Toolbar component in our AppController so it's available in all applications. This is a component included with the DebugKit plugin, which allows us to display a toolbar in the browser, thus providing a range of panels to introspect the application, the current request, SQL queries, and more. You'll notice that we included both the Session and Auth components here as well to propagate them in our application. The plugin itself is also extensible, allowing additional panels to be added. Once our plugin was loaded and the Toolbar component available, we made sure that the debug mode was enabled in our core.php file; we then navigated to the application in the browser to view the loaded toolbar.

Plugins may also load their own bootstrap.php and routes.php files to set up configurations and define routing settings. However, when a plugin is loaded, you must specify to load these configurations and settings. For example, if you had a Reports plugin that used these, you would load the plugin using the plugin configuration array as follows:

CakePlugin::load('Reports', array(
  'bootstrap' => true,
  'routes' => true
));

You can also load all plugins in your app/Plugin/ directory at once by calling CakePlugin::loadAll().

See also

Left arrow icon Right arrow icon

Key benefits

What you will learn

Estimated delivery fee Deliver to United States

Economy delivery 10 - 13 business days

Free $6.95

Premium delivery 6 - 9 business days

$21.95
(Includes tracking information)

Product Details

Country selected

Publication date : Aug 6, 2014
Length 346 pages
Edition : 1st Edition
Language : English
ISBN-13 : 9781782160083
Category :
Languages :

What do you get with Print?

Product feature icon Instant access to your digital eBook copy whilst your Print order is Shipped
Product feature icon Black & white paperback book shipped to your address
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
Buy Now
Estimated delivery fee Deliver to United States

Economy delivery 10 - 13 business days

Free $6.95

Premium delivery 6 - 9 business days

$21.95
(Includes tracking information)

Product Details


Publication date : Aug 6, 2014
Length 346 pages
Edition : 1st Edition
Language : English
ISBN-13 : 9781782160083
Category :
Languages :

Table of Contents

20 Chapters
CakePHP 2 Application Cookbook Chevron down icon Chevron up icon
Credits Chevron down icon Chevron up icon
Foreword Chevron down icon Chevron up icon
About the Authors Chevron down icon Chevron up icon
About the Reviewer Chevron down icon Chevron up icon
www.PacktPub.com Chevron down icon Chevron up icon
Preface Chevron down icon Chevron up icon
1. Lightning Introduction Chevron down icon Chevron up icon
2. Advanced Routing Chevron down icon Chevron up icon
3. HTTP Negotiation Chevron down icon Chevron up icon
4. API Strategies Chevron down icon Chevron up icon
5. Using Authentication Chevron down icon Chevron up icon
6. Model Layer Chevron down icon Chevron up icon
7. Search and Pagination Chevron down icon Chevron up icon
8. Events System Chevron down icon Chevron up icon
9. Creating Shells Chevron down icon Chevron up icon
10. View Templates Chevron down icon Chevron up icon
11. Unit Tests Chevron down icon Chevron up icon
12. Migrations Chevron down icon Chevron up icon
Index Chevron down icon Chevron up icon

Customer reviews

Top Reviews
Rating distribution
Empty star icon Empty star icon Empty star icon Empty star icon Empty star icon 0
(0 Ratings)
5 star 0%
4 star 0%
3 star 0%
2 star 0%
1 star 0%
Top Reviews
No reviews found
Get free access to Packt library with over 7500+ books and video courses for 7 days!
Start Free Trial

FAQs

What is the delivery time and cost of print book? Chevron down icon Chevron up icon

Shipping Details

USA:

'

Economy: Delivery to most addresses in the US within 10-15 business days

Premium: Trackable Delivery to most addresses in the US within 3-8 business days

UK:

Economy: Delivery to most addresses in the U.K. within 7-9 business days.
Shipments are not trackable

Premium: Trackable delivery to most addresses in the U.K. within 3-4 business days!
Add one extra business day for deliveries to Northern Ireland and Scottish Highlands and islands

EU:

Premium: Trackable delivery to most EU destinations within 4-9 business days.

Australia:

Economy: Can deliver to P. O. Boxes and private residences.
Trackable service with delivery to addresses in Australia only.
Delivery time ranges from 7-9 business days for VIC and 8-10 business days for Interstate metro
Delivery time is up to 15 business days for remote areas of WA, NT & QLD.

Premium: Delivery to addresses in Australia only
Trackable delivery to most P. O. Boxes and private residences in Australia within 4-5 days based on the distance to a destination following dispatch.

India:

Premium: Delivery to most Indian addresses within 5-6 business days

Rest of the World:

Premium: Countries in the American continent: Trackable delivery to most countries within 4-7 business days

Asia:

Premium: Delivery to most Asian addresses within 5-9 business days

Disclaimer:
All orders received before 5 PM U.K time would start printing from the next business day. So the estimated delivery times start from the next day as well. Orders received after 5 PM U.K time (in our internal systems) on a business day or anytime on the weekend will begin printing the second to next business day. For example, an order placed at 11 AM today will begin printing tomorrow, whereas an order placed at 9 PM tonight will begin printing the day after tomorrow.


Unfortunately, due to several restrictions, we are unable to ship to the following countries:

  1. Afghanistan
  2. American Samoa
  3. Belarus
  4. Brunei Darussalam
  5. Central African Republic
  6. The Democratic Republic of Congo
  7. Eritrea
  8. Guinea-bissau
  9. Iran
  10. Lebanon
  11. Libiya Arab Jamahriya
  12. Somalia
  13. Sudan
  14. Russian Federation
  15. Syrian Arab Republic
  16. Ukraine
  17. Venezuela
What is custom duty/charge? Chevron down icon Chevron up icon

Customs duty are charges levied on goods when they cross international borders. It is a tax that is imposed on imported goods. These duties are charged by special authorities and bodies created by local governments and are meant to protect local industries, economies, and businesses.

Do I have to pay customs charges for the print book order? Chevron down icon Chevron up icon

The orders shipped to the countries that are listed under EU27 will not bear custom charges. They are paid by Packt as part of the order.

List of EU27 countries: www.gov.uk/eu-eea:

A custom duty or localized taxes may be applicable on the shipment and would be charged by the recipient country outside of the EU27 which should be paid by the customer and these duties are not included in the shipping charges been charged on the order.

How do I know my custom duty charges? Chevron down icon Chevron up icon

The amount of duty payable varies greatly depending on the imported goods, the country of origin and several other factors like the total invoice amount or dimensions like weight, and other such criteria applicable in your country.

For example:

  • If you live in Mexico, and the declared value of your ordered items is over $ 50, for you to receive a package, you will have to pay additional import tax of 19% which will be $ 9.50 to the courier service.
  • Whereas if you live in Turkey, and the declared value of your ordered items is over € 22, for you to receive a package, you will have to pay additional import tax of 18% which will be € 3.96 to the courier service.
How can I cancel my order? Chevron down icon Chevron up icon

Cancellation Policy for Published Printed Books:

You can cancel any order within 1 hour of placing the order. Simply contact customercare@packt.com with your order details or payment transaction id. If your order has already started the shipment process, we will do our best to stop it. However, if it is already on the way to you then when you receive it, you can contact us at customercare@packt.com using the returns and refund process.

Please understand that Packt Publishing cannot provide refunds or cancel any order except for the cases described in our Return Policy (i.e. Packt Publishing agrees to replace your printed book because it arrives damaged or material defect in book), Packt Publishing will not accept returns.

What is your returns and refunds policy? Chevron down icon Chevron up icon

Return Policy:

We want you to be happy with your purchase from Packtpub.com. We will not hassle you with returning print books to us. If the print book you receive from us is incorrect, damaged, doesn't work or is unacceptably late, please contact Customer Relations Team on customercare@packt.com with the order number and issue details as explained below:

  1. If you ordered (eBook, Video or Print Book) incorrectly or accidentally, please contact Customer Relations Team on customercare@packt.com within one hour of placing the order and we will replace/refund you the item cost.
  2. Sadly, if your eBook or Video file is faulty or a fault occurs during the eBook or Video being made available to you, i.e. during download then you should contact Customer Relations Team within 14 days of purchase on customercare@packt.com who will be able to resolve this issue for you.
  3. You will have a choice of replacement or refund of the problem items.(damaged, defective or incorrect)
  4. Once Customer Care Team confirms that you will be refunded, you should receive the refund within 10 to 12 working days.
  5. If you are only requesting a refund of one book from a multiple order, then we will refund you the appropriate single item.
  6. Where the items were shipped under a free shipping offer, there will be no shipping costs to refund.

On the off chance your printed book arrives damaged, with book material defect, contact our Customer Relation Team on customercare@packt.com within 14 days of receipt of the book with appropriate evidence of damage and we will work with you to secure a replacement copy, if necessary. Please note that each printed book you order from us is individually made by Packt's professional book-printing partner which is on a print-on-demand basis.

What tax is charged? Chevron down icon Chevron up icon

Currently, no tax is charged on the purchase of any print book (subject to change based on the laws and regulations). A localized VAT fee is charged only to our European and UK customers on eBooks, Video and subscriptions that they buy. GST is charged to Indian customers for eBooks and video purchases.

What payment methods can I use? Chevron down icon Chevron up icon

You can pay with the following card types:

  1. Visa Debit
  2. Visa Credit
  3. MasterCard
  4. PayPal
What is the delivery time and cost of print books? Chevron down icon Chevron up icon

Shipping Details

USA:

'

Economy: Delivery to most addresses in the US within 10-15 business days

Premium: Trackable Delivery to most addresses in the US within 3-8 business days

UK:

Economy: Delivery to most addresses in the U.K. within 7-9 business days.
Shipments are not trackable

Premium: Trackable delivery to most addresses in the U.K. within 3-4 business days!
Add one extra business day for deliveries to Northern Ireland and Scottish Highlands and islands

EU:

Premium: Trackable delivery to most EU destinations within 4-9 business days.

Australia:

Economy: Can deliver to P. O. Boxes and private residences.
Trackable service with delivery to addresses in Australia only.
Delivery time ranges from 7-9 business days for VIC and 8-10 business days for Interstate metro
Delivery time is up to 15 business days for remote areas of WA, NT & QLD.

Premium: Delivery to addresses in Australia only
Trackable delivery to most P. O. Boxes and private residences in Australia within 4-5 days based on the distance to a destination following dispatch.

India:

Premium: Delivery to most Indian addresses within 5-6 business days

Rest of the World:

Premium: Countries in the American continent: Trackable delivery to most countries within 4-7 business days

Asia:

Premium: Delivery to most Asian addresses within 5-9 business days

Disclaimer:
All orders received before 5 PM U.K time would start printing from the next business day. So the estimated delivery times start from the next day as well. Orders received after 5 PM U.K time (in our internal systems) on a business day or anytime on the weekend will begin printing the second to next business day. For example, an order placed at 11 AM today will begin printing tomorrow, whereas an order placed at 9 PM tonight will begin printing the day after tomorrow.


Unfortunately, due to several restrictions, we are unable to ship to the following countries:

  1. Afghanistan
  2. American Samoa
  3. Belarus
  4. Brunei Darussalam
  5. Central African Republic
  6. The Democratic Republic of Congo
  7. Eritrea
  8. Guinea-bissau
  9. Iran
  10. Lebanon
  11. Libiya Arab Jamahriya
  12. Somalia
  13. Sudan
  14. Russian Federation
  15. Syrian Arab Republic
  16. Ukraine
  17. Venezuela