Routing Practice, Event Handlers and Naming Convention in CakePHP

Hej, dude. Have you tested your cake. In this tutorial, we will discuss some advance concept of CakePHP. Routing, event handling and naming convention are very important to learn CakePHP. These are made to make the life easier, for developer. We will see lots of interesting features using these features. Before starting, I hope you already read previous tutorial, otherwise you may find it boring. So, let’s start.

Cake logo

Routing
Routing is a process of setting URL. It was added to CakePHP to make pretty URLs more configurable and flexible.

1 routing_engine

Routes Configuration
We need to configure the routes file from app/Config/routes.php to set our routing. This file contains parameters for default routing of the application.

Default Routing
Before we learn about configuring of our own routes, we should know that CakePHP comes with a default set of configured routes. You can access an action directly via the URL by putting its name in the request. You can also pass parameters to your controller actions using the URL.:

URL pattern default routes:
http://example.com/controller/action/param1/param2/param3

The URL /posts/view maps to the view() action of the PostsController, and /products/view_clearance maps to the view_clearance() action of the ProductsController. If no action is specified in the URL, the index() method is assumed.

The default routing setup also allows you to pass parameters to your actions using the URL. A request for /posts/view/25 would be equivalent to calling view(25) on the PostsController, for example.

Events System
If you have used JavaScript, there is a good chance that you are already familiar with event driven programming.

CakePHP emulates several aspects of how events are triggered and managed in popular JavaScript libraries such as jQuery. In the CakePHP implementation, an event object is dispatched to all listeners. The event object holds information about the event, and provides the ability to stop event propagation at any point. Listeners can register themselves or can delegate this task to other objects and have the chance to alter the state and the event itself for the rest of the callbacks.

event-system

The event subsystem is at the heart of Model, Behavior, Controller, View and Helper callbacks. If you’ve ever used any of them, you are already somewhat familiar with events in CakePHP.

Example event usage
Let’s suppose you are building a Cart plugin, and you’d like to focus on just handling order logic. You don’t really want to include shipping logic, emailing the user or decrementing the item from the stock, but these are important tasks to the people using your plugin. If you were not using events, you may try to implement this by attaching behaviors to models, or adding components to your controllers. Doing so represents a challenge most of the time, since you would have to come up with the code for externally loading those behaviors or attaching hooks to your plugin controllers.

Instead, you can use events to allow you to cleanly separate the concerns of your code and allow additional concerns to hook into your plugin using events. For example in your Cart plugin you have an Order model that deals with creating orders. You’d like to notify the rest of the application that an order has been created. To keep your Order model clean you could use events:

// Cart/Model/Order.php
App::uses('CakeEvent', 'Event');
class Order extends AppModel {

    public function place($order) {
        if ($this->save($order)) {
            $this->Cart->remove($order);
            $event = new CakeEvent('Model.Order.afterPlace', $this, array(
                'order' => $order
            ));
            $this->getEventManager()->dispatch($event);
            return true;
        }
        return false;
    }
}

The above code allows you to easily notify the other parts of the application that an order has been created. You can then do tasks like send email notifications, update stock, log relevant statistics and other tasks in separate objects that focus on those concerns.

Accessing event managers
In CakePHP events are triggered against event managers. Event managers are available in every Model, View and Controller using getEventManager():

$events = $this->getEventManager();

Each model has a separate event manager, while the View and Controller share one. This allows model events to be self contained, and allow components or controllers to act upon events created in the view if necessary.

Global event manager
In addition to instance level event managers, CakePHP provides a global event manager that allows you to listen to any event fired in an application. This is useful when attaching listeners to a specific instance might be difficult. The global manager is a singleton instance of CakeEventManager that receives every event before the instance managers do. In addition to receiving events first, the global manager also maintains a separate priority stack for listeners. Once an event has been dispatched to the global manager, it will be dispatched to the instance level manager. You can access the global manager using a static method:

// In any configuration file or piece of code that executes before the event
App::uses('CakeEventManager', 'Event');
CakeEventManager::instance()->attach(
    $aCallback,
    'Model.Order.afterPlace'
);

Dispatching events
Once you have obtained an instance of an event manager you can dispatch events using dispatch(). This method takes an instance of the CakeEvent class.

dispatch

Let’s look at dispatching an event:

// Create a new event and dispatch it.
$event = new CakeEvent('Model.Order.afterPlace', $this, array(
    'order' => $order
));
$this->getEventManager()->dispatch($event);

CakeEvent accepts 3 arguments in its constructor. The first one is the event name, you should try to keep this name as unique as possible, while making it readable. We suggest a convention as follows: Layer.eventName for general events happening at a layer level (e.g. Controller.startup, View.beforeRender) and Layer.Class.eventName for events happening in specific classes on a layer, for example Model.User.afterRegister or Controller.Courses.invalidAccess.

The second argument is the subject, meaning the object associated to the event, usually when it is the same class triggering events about itself, using $this will be the most common case. Although a Component could trigger controller events too. The subject class is important because listeners will get immediate access to the object properties and have the chance to inspect or change them on the fly.

Finally, the third argument is any additional event data. This can be any data you consider useful to pass around so listeners can act upon it. While this can be an argument of any type, we recommend passing an associative array. The dispatch() method accepts an event object as an argument and notifies all subscribed listeners.

Registering listeners
Listeners are the preferred way to register callbacks for an event. This is done by implementing the CakeEventListener interface in any class you wish to register some callbacks. Classes implementing it need to provide the implementedEvents() method. This method must return an associative array with all event names that the class will handle.

3-good-listener

To continue our previous example, let’s imagine we have a UserStatistic class responsible for calculating a user’s purchasing history, and compiling into global site statistics. This is a great place to use a listener class. Doing so allows you concentrate the statistics logic in one place and react to events as necessary. Our UserStatistics listener might start out like:

App::uses('CakeEventListener', 'Event');
class UserStatistic implements CakeEventListener {

    public function implementedEvents() {
        return array(
            'Model.Order.afterPlace' => 'updateBuyStatistic',
        );
    }

    public function updateBuyStatistic($event) {
        // Code to update statistics
    }
}

// Attach the UserStatistic object to the Order's event manager
$statistics = new UserStatistic();
$this->Order->getEventManager()->attach($statistics);

As you can see in the above code, the attach function will accept instances of the CakeEventListener interface. Internally, the event manager will use implementedEvents to attach the correct callbacks.

CakePHP Naming Conventions
CakePHP is a big fan of convention. If you follow the convention, everything will be piece a cake. It may takes a bit of time to learn CakePHP’s conventions. CakePHP’s conventions have come out from years of web development experience and best practices. So, let’s learn some CakePHP conventions and make our life easy.

convention

Controller Conventions
Controller class names are plural, CamelCased, and end in Controller. PeopleController and LatestArticlesController are both examples of conventional controller names.
The first method you write for a controller is index() method. When a request specifies a controller but not an action, the default CakePHP behavior is to execute the index() method of that controller. For example, a request for http://www.example.com/apples/ maps to a call on the index() method of the ApplesController, whereas http://www.example.com/apples/add/ maps to a call on the add() method of the ApplesController.

This is also possible to change the visibility of controller methods in CakePHP by prefixing controller method names with underscores. If a controller method has been prefixed with an underscore, the method will not be accessible directly from the web but is available for internal use. For example: if we create a method _findNewArticles() inside a controller class it will not be accessible from the web. But, you can use it inside that class.

URL Considerations for Controller Names
As we have already seen, single word controllers map easily to a simple lower case URL path. For example, ApplesController (which would be defined in the file name ‘ApplesController.php’) is accessed from http://example.com/apples.

The convention is that your URLs are lowercase and underscored, therefore /red_apples/go_pick is the correct form to access the RedApplesController::go_pick action.

File and Class Name Conventions
The general idea of files name, filenames and the class names will be the same, which are CamelCased. So if you planning to make a class for user then, the class name will be UsersController, then in CakePHP, the file should be named UsersController.php.

Model and Database Conventions
Model class names are singular and CamelCased. Person, BigPerson, and ReallyBigPerson are all examples of conventional model names.

Table names corresponding to CakePHP models are plural and underscored. The underlying tables for the above mentioned models would be people, big_people, and really_big_people, respectively.
Field names with two or more words are underscored like, first_name.

Foreign keys in hasMany, belongsTo or hasOne relationships are recognized by default as the (singular) name of the related table followed by _id. (If you don’t know with is, hasMany, belongsTo or hasOne, then keep in mind, we will learn these from a whole tutorial, in future.) So if a Baker hasMany Cake, the cakes table will refer to the bakers table via a baker_id foreign key. For a multiple worded table like category_types, the foreign key would be category_type_id.

Rather than using an auto-increment key as the primary key, you may also use char(36). CakePHP will then use a unique 36 character UUID (String::uuid) whenever you save a new record using the Model::save method.

View Conventions
View template files are named after the controller functions they display, in an underscored form. The getReady() function of the PeopleController class will look for a view template in /app/View/People/get_ready.ctp.

The basic pattern is /app/View/Controller/underscored_function_name.ctp.
By naming the pieces of your application using CakePHP conventions, you gain functionality without the hassle and maintenance tethers of configuration. Here’s a final example that ties the conventions

  • Database table: “people”
  • Model class: “Person”, found at /app/Model/Person.php
  • Controller class: “PeopleController”, found at /app/Controller/PeopleController.php
  • View template, found at /app/View/People/index.ctp

Using these conventions, CakePHP knows that a request to http://example.com/people/ maps to a call on the index() function of the PeopleController, where the Person model is automatically available (and automatically tied to the ‘people’ table in the database), and renders to a file.

Cake Code
Are you not tired of reading all of these texts? I think so lets write some code. Here, we will write a very small application, which is useful to all of our reading. Ok.

  1. Create a project with a blank database. Don’t forget to configure it properly. Otherwise, you may see some red and yellow boxes. Let’s say our database is cakedb. So, according to convention our table will be posts. It is plural form of post.
    CREATE TABLE posts (     
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,     
    title VARCHAR(50),     
    body TEXT, 
    funnypost TINYINT(1),
    seriouspost TINYINT(1),
    created DATETIME DEFAULT NULL,     
    modified DATETIME DEFAULT NULL );
    
    
  2. Now open your editor and create a model in called Post.php in app/Model folder. This is also according to convention; the model name is always singular. Write the following simple code.
    class Post extends AppModel{
    
    }
    
    

    Code Explanation:
    In line 1, we have extends one CakePHP model class called AppModel. It is a built in model class provided by CakePHP.

  3. Now create a controller called PostsController.php inside app/Controller folder. According to the convention controller name will be plural and with Controller word. Write the following code.
    class PostsController extends AppController{
        
    
        public function index(){
    
        }
    
        public function view(){
    
        }
    
        public function edit(){
    
        }
    }
    
    

    Code explanation:
    In line 1, we have extended AppController class. AppController class is a built in CakePHP controller class which helps to work our PostsController class.

    In line 4, 8, 11, we have created three methods. In cakePHP these are called action. Every method look for their corresponding .ctp file for views.

  4. Now create a Posts folder inside app/View folder. This is also a convention. Controllers should have their views in the corresponding views folder. It means, if we have 5 controllers then we will need 5 views folder with their corresponding folders inside app/View.

    Create index.ctp inside Posts folder and write –

    <h1>This is the page called from index</h1>
    

    Create view.ctp inside Posts folder and write –

    <h1>This is the page called from view</h1>
    

    Create edit.ctp inside Posts folder and write –

    <h1>This is the page called from edit</h1>
    

Now open your browser and go to –

http://localhost/cakephp24/posts/

By default it will call the index action as well as index.ctp. But we can change this default behavior. We can do it by changing our routing.

Now If you go to –
http://localhost/cakephp24/posts/view

http://localhost/cakephp24/posts/edit

Both of them will their corresponding action and views.

Now let’s change the default routing. So, open app/Config/router.php and find –

Router::connect('/', array('controller' => 'pages', 'action' => 'display', 'home'));

Router::connect('/pages/*', array('controller' => 'pages', 'action' => 'display'));

Change it to –

Router::connect('/', array('controller' => 'posts', 'action' => 'view'));
	
Router::connect('/posts/*', array('controller' => 'posts', 'action' => 'view'));

Now from your browser, go to –

http://localhost/cakephp24/

and then –
http://localhost/cakephp24/posts/

What are you seeing? Your default route has changed from index to view.

Conclusion
Ohhh.. I am really tired. It is a long tutorial. I have picked these important texts from the documentation to make your life easier with CakePHP. Because these are very important to know. Without knowing cake conventions you could be puzzled. Now it is time to give a big hug.

Happy coding… 🙂

Routing Practice, Event Handlers and Naming Convention in CakePHP

Hej, I’m from Bangladesh. Learning programming is one of the freaking decisions I have taken in my life. Because, it makes me and my life crazy. I have great weakness on open source technologies. Perhaps, that’s why I do not know any closed source language. I fall in love with programming, when I started my undergraduate in East West University. Till now, I can not live without it.
Print Friendly

Leave a Reply

Your email address will not be published. Required fields are marked *


*