CHAPTER 11

Components and Utilities

Cake uses helpers to extend the functionality of views. By placing several view functions in helpers, not only do your views remain light but the application is better organized. There is no precise method for how to organize functions in a helper other than keeping order and maintaining consistency in the application. Helpers are to the view what components and utilities are to the controller. In a way, you could think of components as "controller helpers."

Why Use Components?

But you may ask, why have components when there's an App controller? Shouldn't the App controller hold all the functions used by multiple controllers? Yes and no. The App controller does hold more generalized actions, or actions that more than one controller will use. Rather than employ the requestAction() function to allow a controller to access a function in another controller, you could just place the function in the App controller and scale it to be brought across the application into multiple controllers.

Consider the actions in the App controller like you consider display elements in the layout; parts of the view that you want to be visible across all views. Multiple views should be placed in a layout, and in a similar way, actions that will be used across controllers should be placed in the App controller. However, there are still helpers for the views that are used for specific display output tasks. The $html->link() helper function, for example, can be used only in the view because it generates HTML output, but being repeated throughout multiple views, it makes sense to house this function in a helper file rather than in the layout or in the controller. Components serve a similar purpose in Cake applications in the sense that they house specific functions that the controller or multiple controllers can use.

For example, consider the built-in components in Cake. Some of these are the Auth, Session, and RequestHandler components. Their functions all serve to specifically help the controller perform actions relating to their specific method; actions in the authentication component all have to do with helping the controller manage user authentication processes, and so forth. In a sense, helpers are portable view functions in that they can be distributed and pulled into any Cake application and work in a specific way to enhance and simplify the processes the views may need. Similarly, components are portable controller functions that serve a specific purpose and can be distributed to any controller in any Cake application.

Using Components

All components are stored in the app/controllers/components directory and are pulled into the controller like helpers, with the $components array. Like other parameters, components must be camel-cased and separated in the array by commas. Built-in components as well as custom ones must be included in the $components array to work in the controller, and like helpers, the App controller can include components for use in any or all controllers. See Listing 11-1.

Listing 11-1. Including Components in the Controller with the $components Array

var $components = array('Session','RequestHandler'),

Once a component is included in the controller, the controller references its functions by using the $this identifier. Remember to specify the component like you do with models: $this followed by the camel-cased name of the component and the function in use. Listing 11-2.

Listing 11-2. Running a Component Function in the Controller

$user = $this->Session->read('User'),

Components are not intended to work directly with the database, so the component does not do much more than return a variable, a string, or an array for the controller to use. If database calls are necessary, the controller using the component ought to run these calls through the model and provide the component with the necessary data. This does not mean that the components are limited to logic tests or reformatting forwarded data; like the Session and Cookie components, functions here can write to outside files or process images, and so forth.

Besides allowing for custom components, Cake comes with several built-in components and utilities. Before building your own components, let's first examine what Cake has to offer and explore how these component functions might improve an application.

Using Built-in Components

Like the helpers, Cake has a wealth of component functions that make running processes in the controller much easier. In fact, some components are intended to work hand in hand with helpers like the Session component. Running some functions in the controller makes it easier to organize and format the data forwarded on to the view for use by the Session helper. Knowing what built-in components can do for your controllers will save you from "reinventing the wheel" and help you exploit some of the snazzy functions that make Cake such an attractive framework.

In each of the following descriptions, I'll give a basic overview of what the component offers and list some of its functions. In the future, more functions may be added to these components, and sometimes third-party components will extend them too. Cake's core components are stored in the cake/libs/controller/components folder; if you'd like to take a look at the code itself, refer to the component files there.

Authentication

The Auth component helps with managing user authentication of the application. Many times you may want an area of the site to be protected from ordinary users. Common processes such as logging in to view an area of the site are made easier by the Auth component. Like other components, it needs to be included in the $components array to be available in the controller:

var $components = array('Auth'),

With the Auth component, you can require authentication for miscellaneous areas of the application such as models, controllers, or other objects. Auth also provides methods for automating logging in and automatically redirecting the user to accessible areas of the site. A database table containing stored user information is necessary for the Auth component to work correctly.

Setting Up the Users Table

By default, Auth will look for a table named users with fields named username and password. Of course, these default parameters can be changed by entering custom parameters into the component before runtime. Whatever the case, a table somewhere in the database will need to be present to store all the users and their passwords. The users table doesn't need to store only username and password fields; it can also include roles, privileges, e-mail, and more. You'll have to tell Auth how to use these types of metadata.

The users table is already built into your blog application. Because the Auth component hashes all passwords, you'll need to make sure the password field has enough space to store the hashes. Remember that Security.salt value in the core configuration? The Auth component will use this salt value to generate its hashes, so putting a more complicated string in this preference will improve the security of your passwords.

With the users table ready to store usernames and passwords, you can implement the Auth component in your blog application. First let's initialize some parameters in the Posts controller.

Initializing Variables

Several parameters need to be set for Auth to work correctly. You specify these parameters by setting variables in the beforeFilter() controller action for the controller that will require authorization before letting the user access it. In this case, you will want to block out certain editing and deleting actions in the blog except for logged-in users. In the Posts controller, you need to tell the Auth component which actions are allowed and where to redirect the user upon a successful login. Listing 11-3 contains the beforeFilter() action for the Posts controller.

Listing 11-3. Some Auth Parameters in the beforeFilter() Action

1    function beforeFilter() {
2        $this->Auth->loginAction = array('controller'=>'users','action'=>'login'),
3        $this->Auth->allow('index','view'),
4        $this->Auth->redirectLogin = array('controller'=>'posts','action'=>'add'),
5    }

Notice in Listing 11-3 that each class variable is set like any typical PHP variable. On line 3, I used the allow function to tell the Auth component which actions don't require authentication to be accessed. You do not need to specify the login and logout actions; otherwise, this will trip up the Auth component; it automatically assumes these functions are accessible to everyone.

Now that the Auth component has been called in the controller and the beforeFilter() action sets some parameters for Auth to use; Auth will check each action against what you've specified. If a request is made for an action that is not allowed, Auth will require the user to log in for access, which is specified in line 2 of Listing 11-3. In this case, the user will be redirected to the Login action in the Users controller (which hasn't been created yet). Auth will not be run if the user requests the Index and View actions in the Posts controller.

The next step is to create the Login action in the Users controller. Because the Auth component does almost all the work, you will need only to create the view with the username and password fields.

Logging In

As long as the view that renders the login form is named appropriately (in other words, its names correspond to the parameters set in the beforeFilter() function), the Auth component will automatically check the $this->data array for the username and password and check it using the userModel and fields settings. Listing 11-4 shows the view file for the Users Login action.

Listing 11-4. The app/views/users/login.ctp File

1    <h1>Log In</h1>
2    <? if ($session->check('Message.auth')) $session->flash('auth'),?>
3    <?=$form->create('User',array('action'=>'login'));?>
4    <?=$form->input('username'),?>
5    <?=$form->input('password',array('type'=>'password'));?>
6    <?=$form->end('Login'),?>

Simple enough—the Login view contains a form to render input fields for the username and password and submits it to the Login action. If there are any errors, like an incorrect match, they will be displayed in line 2 with the flash function.

To complete the login process, you need only to prepare the Users controller. Listing 11-5 shows the contents of the Users controller with the login function.

Listing 11-5. The Auth Component in the Users Controller

1    <?
2    class UsersController extends AppController {
3        var $name = 'Users';
4        var $components = array('Auth'),
5
6        function beforeFilter() {
7            $this->Auth->allow('*'),
8        }
9
10        function login() {
11
12        }

You can see that the Login action is empty. This is because the Auth component takes care of everything for you; you only need to specify the action so that Cake appropriately renders the Login view. Line 7 shows the use of the asterisk in the Auth->allow() function. This tells the Auth component to allow all actions in the current controller.

With the Users controller working correctly with the Auth component and the Login action and view provided, Auth can now manage authentication for the Posts controller. Try editing a post, and you'll notice that the Auth component requires you to log in.

Logging Out

Logging a user out is even easier than logging one in. Simply add the Auth->logout() function to the Users controller, as shown in Listing 11-6.

Listing 11-6. The Logout Action in the Users Controller

14    function logout() {
15        $this->redirect($this->Auth->logout());
16    }

Now, whenever the user requests http://localhost/blog/users/logout, they will be logged out by the Auth component, and the session will be deleted. The user will need to log back in to access blocked actions.

Checking a User's Role

The Auth component can check for a user's role to see whether the user is an administrator. To do so, you will need to add one more field to the users table: role. See Listing 11-7.

Listing 11-7. The SQL Query for Adding the Role Field in the users Table

ALTER TABLE `users` ADD `role` varchar(50) DEFAULT 'www'

Because the Auth component does not check user roles by default, you will need to run your own login process instead. First, in the Posts controller, you must tell the Auth component not to perform its default logic:

$this->Auth->authorize = 'controller';

This line essentially tells Auth to look in the active controller for an action named isAuthorized(). In this function, you'll run your own authorization logic; if it returns true, then the user is authorized, and so forth. Now let's create the isAuthorized() function to check the user's role. See Listing 11-8.

Listing 11-8. The isAuthorized() Function in the Posts Controller

1    function isAuthorized() {
2        if ($this->Auth->user('role') == 'admin') {
3            return true;
4        } else {
5            return false;
6        }
7    }

Line 2 performs the test. Using the Auth->user() function and passing the value role, you're telling Auth to look for a field named role and see whether it contains admin. If the user is not given the admin role in the users table, then he will not be authorized to view anything other than the Index and View actions.

This same check can be accomplished in the model rather than the controller. Rather than set Auth->authorize to controller, set it to model instead. Then run the isAuthorized() function in the model rather than the controller. The function will need to include the $user, $controller, and $action parameters to work in the model (see Listing 11-9).

Listing 11-9. The isAuthorized() Function in the Post Model

1    function isAuthorized($user, $controller, $action) {
2        if ($user['User']['role'] == 'admin') {
3            return true;
4        } else {
5            return false;
6        }
7    }

The $user array will contain the Auth component's retrieval of the user record where the username and password match, so line 2 uses this array rather than the Auth->user() function. Both methods for checking a user's role will work; following the convention of having larger models than controllers will mean that most of the time you'll check user roles in the model.

Using Other Auth Parameters and Functions

The following is a list of other parameters that can be used with the Auth component. They are used to customize the way in which Auth manages user logins, sessions, and authentication.

deny( actions )

actions: The actions, separated by commas, which should be denied access without authentication

hashPasswords( data[array] )

data: Takes values assigned to keys named password in an array and returns their hashed equivalents

$userModel = 'User';

Used to customize the table name where users are stored. By default this is set to User. Be sure to use the model name, not the table name (for example, the bloggers table would need to be set to Blogger here).

$fields = array('username'=>'username','password'=>'password'),

This array can be changed to reflect customized field names for the username and password.

$loginAction = array('admin'=>false,'controller'=>'users','action'=>'login'),

Setting the keys in this array to other controllers or actions tells Auth where to run the Login action. Admin routing (see Chapter 10) and subsequent actions can be used by setting admin to true.

$loginRedirect = array('controller'=>'users','action'=>'index'),

This parameter tells Auth where to redirect the user after a successful login.

$loginError = 'Login failed. Invalid username or password.';

The error message displayed in the event of a login error can be customized here.

$autoRedirect = true;

If you set this parameter to false, the Auth component will not automatically redirect the user after authenticating. Other checks can be run in the Login action after a successful authentication occurs, such as writing information to or checking the contents of a cookie or checking other session variables.

$ajaxLogin = null;

When doing Ajax-based logins, set this preference to the view the file that should be displayed in the event of an unsuccessful login.

Session

The Session component handles various methods for reading, writing, starting, and destroying sessions.

Some settings may be manipulated to change how Cake saves sessions in the core configuration. The Session.save parameter in the core configuration file contains one of three possibilities: php, cake, and database. By default, this value is set to php, which means that sessions will be saved depending on the current PHP settings on the host. Setting this to cake will tell Cake to write session information in temporary files in the app/tmp folder. The third option, database, requires that a table be present in the database that has corresponding fields where Cake can save session information. The necessary SQL statements to generate this table are found in the app/config/sql/session.sql file. Simply run this file through your database, and a default table named cake_sessions should appear with all the necessary fields to store session data. Other session settings that the Session component will use follow the Session.save preference and are fairly self-explanatory.

Reading and Writing Session Variables

Reading and writing variables in a session are easy with the Session component. In the controller, after instantiating the Session component, of course, you can create a new session by specifying a session name with the create() function:

$this->Session->create('User'),

Now, anywhere in the application, this session can be read by using the read() function:

$user = $this->Session->read('User'),

This function will produce an array that follows the typical Cake array structure, allowing you to pull anything you want to store in the session by calling the key:

$password = $user['password'];

Writing to the session is just as simple. With the write() function, supply the session name and the value:

$this->Session->write('User',$locale);

Deleting and Destroying Sessions

The delete() and destroy() functions allow you to stop storing session information or kill a session entirely. With delete(), supply the name of the session key you want to remove. This will not completely destroy the whole session—only the data associated with the provided key. The destroy() function, on the other hand, removes all sessions for the current user; it's the equivalent of PHP's session_destroy() function.

Cookie

When writing cookies, the Cookie component simplifies the process. Much like the Session component, the Cookie component creates new cookies on the user's system, checks for cookies, and reads and writes to those cookies as well. For the Cookie component to work correctly, be sure to provide the necessary parameters when setting up the component.

Setting Up the Cookie Component

Three variables are required for this component to run: cookieName, cookieKey, and cookieDomain. These tell the Cookie component, respectively:

  • The name of the cookie
  • The string used to encrypt information written to the cookie
  • The domain with access to the cookie

Other Cookie component preferences include the following:

  • cookiePath: The path where the cookie is stored on the server; the default is set to the entire domain
  • cookieSecure: A boolean value set to false by default; tells the component whether to save cookies over a secure connection
  • cookieTime: The number of seconds before the cookie will expire; the default value is 0, meaning that when the browser is closed, the cookie will expire

These settings are called in the controller as class properties, like so:

var $cookieName = 'super_duper_cookie';
var $cookieKey = '$%sdkj29KA9Ne@@uxlqW';
var $cookieDomain = 'www.domain_name.com';
var $cookieTime = 5400;

Writing to a Cookie

Storing information in the cookie is simple with the write() function and is similar to the Session->write() function. The first value in the function is the cookie's key, and the second contains the value to be written. Remember that this value will be encrypted using the cookieKey preference.

$this->Cookie->write('User',$user_agent);

Reading a Cookie

Just like the Session component, reading a cookie is done with the read() function:

$user = $this->Cookie->read('User'),

As long as the cookie hasn't expired, the information should be available.

Deleting Cookies

Delete cookies using the del() function. Supply the name of the cookie to be deleted, and the component will eliminate the cookie for you:

$this->Cookie->del('User'),

Email

One of the most common tasks of web sites is to send automated e-mails to users. Whether it is an e-commerce web site or a marketing tool for spreading the word, automated e-mail find wide use on the Web. But they can be frustrating to implement, especially when using HTTP headers in the PHP mail() function. The Email component provides a method for sending e-mails in your Cake application using layouts and views rather than coding long strings of e-mail text in the script or controller.

Setting Up the E-mail Layouts

The Email component distinguishes between HTML and plain-text e-mail. To create a layout for each, you must create the folders in the app/views/layouts/email directory. The HTML layouts are stored in a folder named html, and the plain-text layouts are stored in the text folder. In each folder, create a default.ctp file with the necessary layout markup surrounding the $content_for_layout variable. For example, the HTML layout might include the following:

<!DOCTYPE XHTML PUBLIC "-//W3C//DTD XHTML 1.0 strict//EN">
<html>
    <body>
        <?=$content_for_layout; ?>
    </body>
</html>

The plain-text layout will probably not need much more than the $content_for_layout variable itself.

Sending an E-mail

In the controller action that sends the e-mail, provide some settings for the Email component and then run the send() function to send the e-mail. Available settings include the following:

  • $to
  • $cc
  • $bcc
  • $replyTo
  • $from
  • $sendAs
  • $subject

As you will notice, these parameters are self-explanatory and correspond to typical e-mail settings. The sendAs setting tells the Email component how to send the e-mail, either as html or text.

Say you have a basic message you would like to send in the email() action in one of your controllers. It may go something like this:

function email() {
    $this->Email->to = '[email protected]';
    $this->Email->cc = '[email protected]';
    $this->Email->replyTo = '[email protected]';
    $this->Email->from = '[email protected]';
    $this->Email->subject = 'Basic Email Greeting';
    $this->Email->send('Glad to see you'),
}

The text passed in the send() function is the body of the e-mail to be sent.

Using a Template for E-mail Messages

Creating e-mail templates and sending those rather than providing the content in the controller is simple. Make elements that are stored in keyed folders to create e-mail templates. For example, a basic plain-text template would be stored in the app/views/elements/email/text/hello.ctp file and could contain the following:

Hello! This message is from <?=$sender;?>.

Then, in the email() function, you could set the $sender variable if you want:

function email() {
    $this->Email->to = '[email protected]';
    $this->Email->cc = '[email protected]';
    $this->Email->replyTo = '[email protected]';
    $this->Email->from = '[email protected]';
    $this->Email->subject = 'Basic Email Greeting';
    $this->set('sender','Me, Myself, and I'),
    $this->Email->template = 'hello';
    $this->Email->send();
}

Notice that by using the set() function, you were able to pass a value to the $sender variable in the template file. You also told the Email component which template to use when sending the message; in this case, you'll need to name the template file hello.ctp.

HTML templates can behave just like other views in Cake and may draw upon images stored on the web site; remember to use absolute URLs for any graphics because the user's e-mail client won't be able to pull relative paths when opening the e-mail.

Other Components

Cake's built-in components certainly cut down on the amount of code needed to run common web application methods. Not only does it include the components discussed earlier, but it also includes others that are more advanced: the ACL (which stands for "access control list"), RequestHandler, and Security components.

ACL

One of the most complicated methods for building web applications is access control for multiple users. The Auth component takes care of simple authentication, but what if you wanted to have multiple layers for users and their access levels? Or, better yet, what about dynamically altering a user's privileges across the site? This more complex system for assigning areas for the user requires access control lists that outline a tree or matrix of who gets access to what areas in the site. The ACL component simplifies this type of user management with an assortment of functions and methods.

RequestHandler

This component helps with managing HTTP requests and obtaining more client-side information. Features such as SSL detection, reading the type of HTTP request, and responding with a unique MIME type give the RequestHandler a lot of power for making your web application accessible to more than one type of user. For instance, web services can be managed with the RequestHandler, which allows for other web sites to access information and talk to your application. The RequestHandler can also output information as a MIME type other than text/html, which allows your application to even work in nonbrowser client applications.

Security

The Security component provides ways for tightening up the security of the web application and avoiding vulnerabilities. Together with the Auth component, Security can provide extra mechanisms for checking user authentications and also display more complex error messages depending on how the user interacts with the application. You can require that certain actions run only under an SSL-secured request or hash strings with this component. By sticking to secure methods for building your applications and by running the Security component to protect the site, you more effectively prohibit unwanted intrusions or other attacks.

Utility Classes

As noted earlier, components serve a specific purpose or function that the controller may need depending on its own actions and processes. Other class functions that are used by the components themselves, or that are used throughout Cake's core libraries, perform more functions that can be used in a controller or a custom component. These functions are housed in utility classes and are accessible anywhere in the application through the App::import() function. Several built-in utility classes are available in Cake, but it is assumed that no third-party utilities will be distributed because these classes are contained deep inside the core libraries and as a rule should not be manipulated for applications. To create your own utilities, you would need to create third-party vendor files and bring them into your application using App::import() as well (see Chapter 12 for more information on what Cake's vendors are and how to install them in your Cake application). You would usually want to program your own utility class only if you're trying to incorporate third-party software into your application; otherwise, the solution will generally be found in building one of Cake's resource files like a helper or component.

Configure

The Configure utility is responsible for storing global variables. Such variables are generally stored in the core configuration file and are accessible throughout the whole Cake application.

Reading and Writing Global Variables

Just as you see in the core configuration file, constants or global variables are set using the Configure::write() function. The first parameter is the name of the variable, and the second is the assigned value.

Configure::write('Site.title','My Favorite Web Site'),

To read these globals, simply use the Configure::read() function like so:

$title = Configure::read('Site.title'),

The Configure::read() function can be called in the controller, model, or view.

File and Folder

The File and Folder utilities are especially helpful when writing and reading files on the server or listing the file names of a folder's contents. They can also be used to set folder and file permissions or delete files and folders. Unlike the Configure utility, the File and Folder utilities must be imported into the controller or model where they are used with the App::import() function by instantiating a new class object.

For the File and Folder utilities to work correctly, the path to the file or folder on the server must be supplied. As in Listing 11-10, you can perform several operations on this object, which represents the file. Some of these operations are listed in the following sections.

Listing 11-10. Importing a Utility and Instantiating the Class Object

App::import('Core','File'),
$file =& new File('path/to/file'),

Reading Files

The read() function extracts the contents of the file:

$contents = $file->read();

Creating New Files

If you instantiate a File object for a file that doesn't actually exist, you can use the create() function to create a new, blank file on the server:

$file->create();

Writing to Files

The write() function writes content to a file:

$file->write($filedata);

By default, the method the write() function uses is the PHP w type. The w type overwrites content previously in the file. Other methods used in PHP's fwrite() function can be provided as a second parameter. For example, the a parameter allows contents to be appended to the file:

$file->write($filedata,'a'),

Changing File and Folder Permissions

Using the Folder utility, you can run chmod commands on files and folders. The Folder utility is instantiated like the File utility:

App::import('Core','Folder'),
$folder =& new Folder();

The new $folder object can have its permissions changed with the chmod() function:

$folder->chmod('/path/to/folder',0777);

Other permissions values can be used as well. The chmod() function can also apply permissions recursively by using the recursive parameter. One last feature of this function is the exceptions parameter; if there are any files or folders that should not receive the permissions, they can be skipped by specifying their names in an array.

$folder->chmod('/path/to/folder',0777,true,array('nested_1','nested_3'));

Reading Folder Contents

When the Folder utility is instantiated, a path can be provided:

$folder =& new Folder('/path/to/folder'),

This utility can return an array containing the contents of this folder by using the read() function:

$folder->read();

Reading Folder Trees

To get an array of nested directories or trees with the files in each directory, just use the tree() function like so:

$folder->tree();

Copying, Creating, and Deleting Folders

You can copy a folder's contents to another area on the server with the Folder::copy() function. Notice that this function requires an array with some parameters included:

$folder->copy(array('to'=>'/path/to/new/folder'));

Of course, the $folder object represents the current folder.

Creating new folders is done just like the File utility, with the Folder::create() function. The second parameter sets the permissions value for the new folder:

$folder->create('/path/for/new/folder',0755);

Deleting folders is also easy. Just run the delete() function, and Cake will remove the directory, if its permissions allow this:

$folder->delete('/folder/to/be/deleted'),

Calculating the Folder Size

To find the current folder's file size in bytes, just run the dirsize() function:

$folder->dirsize();

Finding Files in a Folder

Searching for files by their file name can be tricky, but not with the Folder utility. To search in a folder, just provide the find() function with a regular expression, and it will return an array of all matching files in the current directory:

$results = $folder->find('title(.*).pdf'),

HTTP Socket

When running HTTP requests or constructing headers, the HTTP Socket utility comes in handy. Like the other utilities (getting the hang of this?), it's imported in the controller or model with App::import().

App::import('Core','HttpSocket'),
$http =& new HttpSocket();

Making HTTP Requests

One of the most useful features of the HTTP Socket utility is its request handling. Not to be confused with the RequestHandler component, HTTP Socket can perform the nitty-gritty of HTTP requests such as GET and POST.

To run a POST with the HTTP Socket utility, first create an array that contains the post variables. The keys in the array correspond with the name attribute in HTML input elements, and the values match the value attribute.

$post = array('username'=>'cakeuser','password'=>'somehashedpassword'),

Then, using the HttpSocket::post() function, pass the array to a specific URL:

$result = $http->post('http://domain.com/',$post);

A successful POST will return true; an unsuccessful one will return false.

GET methods are also run with the HTTP Socket utility. The serialized set of GET names and values is generally appended to the URL string, so with the HttpSocket::get() function you can provide a GET query as the second parameter. The contents of the GET response are returned to the variable, so if the server returns some HTML or an error message or HTTP headers, it will appear as the returned string.

$response = $http->get('http://domain.com/'),

To run a page scrape of another web site or to perform other HTTP requests, use the HttpSocket::request() function. This function will perform an HTTP request and return the raw server response, be it a web page or some other text.

$response = $http->request('http://domain.com/web_page.html'),

Using Other HTTP Socket Functions

Other functions in the HTTP Socket utility include buildHeader(), buildRequestLine(), buildUri(), decodeBody(), delete(), parseHeader(), parseUri(), put(), and more. When considering how to perform HTTP requests or socket methods for your site, consult this utility, and it will likely contain useful functions to minimize the amount of coding you will have to write.

Localization and Internationalization

For web sites that will have an international audience, translating site content can be difficult. In recent years some important localization (L10n) and internationalization (I18n) standards have been implemented for the Web and other software systems. Cake makes use of L10n and I18n standards and methods with their respective utilities.


Note The "10" and "18" in L10n and I18n represent the number of letters between the first and last in the words localization and internationalization. (Developers use these terms so often that spelling them out was taking too long.) Cake's utility classes are named using these abbreviated versions, not the full titles, so the Localization utility class is named in the core L10n, not Localization.


Working with Locale Files

To localize a web site, or in other words, to make the site content fit the language, number system, currency, and other standards of visitors, use the L10n utility. For example, if a user in Spain were to need the application, you might want to translate some important titles, buttons, links, and other strings into Spanish. To do so, you must first set up Spanish locales or locale files for L10n to use in translation.

Locale files are stored in the app/locale directory and are arranged in subfolders named for the language or locale. The names and strings that L10n uses conform to the ISO 639–2 standard (for more information, consult the Library of Congress, which is the official registration authority for the standard: http://www.loc.gov/standards/iso639-2/). Locale files use the .po file extension and are arranged by locale.

By default, Cake includes the English locale folder, app/locale/eng. In it you'll see another subfolder, which contains locale files for translating strings and messages, LC_MESSAGES.

For each language you'd like to localize, create a default.po file and store this in its locale folder. For Spanish, for example, you would need to create not only the default.po file for the app/locale/eng/LC_MESSAGES directory but also one for the app/locale/spa/LC_MESSAGES folder. The next step is to write in these locale files the string you want to be translated.

Translating Strings

Translating strings is made easy by using two keys: msgid and msgstr. In the default.po locale file, for each line provide one of these keys with an associated value. For example, in the English locale file, you could include the contents of Listing 11-11.

Listing 11-11. An Example of Content in the English Locale File

msgid    "home"
msgstr    "Welcome to Our Site"
msgid    "contact"
msgstr    "Contact Us"
msgid    "login"
msgstr    "Log In"
msgid    "logout"
msgstr    "Log Out"

As long as the other locales contain the same msgid values, the L10n utility can fetch the msgstr values depending on the locale. The default locale is set in the core configuration under Config.language. This value is set by default to English but may be changed to any locale. When L10n looks up msgid keys, it will first visit the locale specified in the Config.language setting.

A corresponding Spanish locale would look something like this:

msgid    "home"
msgstr    "Bienvenidos a Nuestro Sitio"
msgid    "contact"
msgstr    "Contactarnos"
msgid    "login"
msgstr    "Ingresar"
msgid    "logout"
msgstr    "Salir"

Localizing Content

Once the locale files are in place, localizing content is simple. First, instantiate the L10n utility class like other utilities:

App::import('Core','L10n'),
$l10n =& new L10n();

Then, run the L10n::get() function to tell the controller which locale to use for localizing. Now, everything contained in the convenience function, __(), will be looked up in the locales.

$l10n->get('spa'),
__('home'),

The previous lines will output the msgstr following the msgid with home as its value. If you want returned localized strings to be passed along as raw data and not echoed, set the escape parameter to true:

$form->error('Post.title',__('postTitleError',true));

Sanitize

The Sanitize utility contains functions designed for cleaning up data and text. Stripping whitespace, HTML tags, and references to scripts and style sheets, as well as escaping text for SQL, can be accomplished with the Sanitize utility.

Remember to instantiate the Sanitize utility first with App::import() and assign the utility as a class object for use in the controller or model:

App::import('Core','Sanitize'),
$sanitize =& new Sanitize();

Sanitize can strip out HTML tags from a block of text by using the stripTags() function:

$this->set('output',$sanitize->stripTags($output,array('<p>','<em>','<div>')));

Other functions include clean(), escape(), html(), stripImages(), stripScripts(), and stripWhitespace(). If you need to clean data, text, or SQL queries, consult the Sanitize utility.

Third-Party Components

Cake's built-in components and utilities are extremely helpful in extending the functionality of the controller and model. Even more components are available online through Cake's open source service CakeForge (%www.cakeforge.org) and the Bakery (http://bakery.cakephp.org). These third-party components cover all sorts of functions that can reduce the amount of code you need to write. When running components, remember to follow the instructions supplied by the developer, which usually assume you know how to install a component (which was covered in "Using Components" earlier). Third-party components available at the Bakery include Twitter, Image Resizer, LastRSS, SMS Text, Authorize.net AIM Integration, and Yahoo! Weather. Searching for components first before trying to write a long controller process yourself can yield excellent results.

Creating Custom Components

Creating your own custom components is simple and follows the pattern shown in earlier chapters. All components are stored in the app/controllers/components directory and follow Cake's naming scheme. If you want to install your own utilities, you will need to install them as vendor files, since utilities are stored in the Cake core.

The basic component file has a file name that matches its class name. Like controller classes, these are camel-cased and have the .php extension. See Listing 11-12.

Listing 11-12. The Basic Component File, Named blog.php, Stored in the components Directory

<?
class BlogComponent extends Object {
}
?>

The component contains functions like the controller. Because these functions are called by noncomponents, or in other words, a controller, they will run the return() function to send back its output. These functions can also have parameters.

Using the Initialize and Startup Functions

To run logic before the controller's functions are called (like the beforeFilter() function in the controller), use the initialize() function. This function can contain any logic to be run before the component's functions are processed.

Sometimes a component function will need access to its parent controller, meaning the controller that is currently calling it. Properties like $this->data and $this->params, as well as any other properties currently in use by the controller can be pulled, into the component. By using the startup() function, you can provide the component with the controller object and thus give any component function access to its parent controller. See Listing 11-13.

Listing 11-13. The startup() Function in the Component

function startup(&$controller) {
    $this->data = $controller->data;
}

Anything in the controller that appears under the $this object is made available to the component as $controller, as shown in Listing 11-13. You can set a component class variable to contain the controller object:

var $controller = null;

and then use the startup() function to instantiate the controller object. Then all functions in the controller can use $this->controller to access anything in the parent controller.

function startup(&$controller) {
    $this->controller = $controller;
}

Now in a placeholder function, you can pull the parameters from the parent controller and use them in the component:

function test() {
    $params = $this->controller->params;
    $data = $params['data'];
    return $data;
}

Writing Vendor Files Instead of Components

Remember that the idea behind components is to share code for cross-controller functions that are portable. In theory, you should be able to distribute any component, and it should work for any Cake application regardless of how the application is designed. If the code does not fit in the CakePHP framework, or an MVC model, then you ought to create vendor files and not components. By adhering to this standard, you ensure that other Cake developers can benefit from your work, and vice versa.

Like helpers, components are designed to cut down on superfluous code in the controller. When the controller begins to look more bloated than the model, you will generally want to find ways to move more code to the model. When code is shared across multiple controllers, you may want to consider placing that logic in a component and scaling the code to be more portable than in one specific controller or Cake application. In the long run, your site will be better organized, cleaner, and lighter—always good reasons for taking the extra step.

Summary

Components extend the controller like helpers extend the view. They provide a set of functions and methods that are specific to a certain overall scheme to the controller and are designed with portability in mind. In other words, if you've built your components right, you should be able to distribute them for use in any Cake application. In this chapter, I discussed how to include a component class in the controller and how to put its functions to work. You wrote some user authentication methods into your blog application using the Auth component to better secure areas of the site and limit access to the general user. This chapter also explained how to work Cake's utility classes into your application. Many of these classes condense the time and code involved to perform some typical web-related tasks such as reading and writing files and folders. I outlined some other utility classes, and I discussed some important differences between vendor files, utilities, and components. In Chapter 12, I'll go into more detail about vendors and how to use them in your Cake project.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.142.237.172