Category Archives: CakePHP 2.0

CakePHP’s tips

Hello, this post will be few tips about CakePHP, about random things such as set a favicon, use the cache, use no database, …

I’ll make it evolve from time to time, feel free to give tips too in the comments 😉

  • How to set a favicon in your layout?

Well that’s pretty easy, open your layout which should be in app/View/Layouts/xxx.ctp and add this line in the head markup :

<head>
    <?php
        echo $this->Html->meta('icon', $this->Html->url('/favicon.png'));
    ?>
    ...
<head>

Of course here, the favicon is called favicon.png and is at the root of the webroot folder.

  • How to use the cache? (short explanation)
  • How to use CakePHP without database?

You have to create a new datasource file in app/Model/Datasource, call it for example Nosource.php, and fill it with this

<?php
    class NoSource extends DataSource {
        function isConnected() {
            return true;
    }
}

Then in your database.php file in app/Config just configure the default database this way

<?php
    public $default = array(
        'datasource' => 'NoSource'
    );

This would do the trick.

  • How to use a model without a table?

Just add the $useTable = false; line into your model.

<?php
App::uses('AppModel', 'Model');

class NoTableModel extends AppModel {
    public $name = 'NoTableModel';
    public $useTable = false;
}

  • How to have coloration in Eclipse for .ctp files?

In Eclipse just go to Preference > General > Content Types > Php, and then add *.ctp, you were blind, now you see.

  • How to deal quickly with erros in your application? (dirty but quick way)

Put this function in your appController, this example will catch all errors and redirect to the root of your website. It’s a pretty bad example, but it’s up to you to do whatever you want in the function (set a message, do actions, …). I used this for a small local application, it didn’t need to show errors.

<?php
    public function appError($error)
    {
        $this->redirect('/');
    }
  • How to get only your model data and not its associated data?

The recursive attribute allows you to set how deep you go when you get (with the methods find, read, etc … ) the data your model. For example, let’s say you have a model Post, if you want to grab only the data of the post and not the tags, the categories, etc … do as follows

<?php
    $myPost = $this->Post->find('first', array('recursive' => -1));

Set to -1 it only grabs the data of the model. You can also set it in your appModel directly (it’s wiser) and change its value whenever you need just like I showed before with the find method. More explanations about it there.

  • How to make your own validation rule?

Probably you came to the need of some homemade validation rules. It’s kinda easy to do. Let’s say you have a Post model, so it’s in App/Model/Post.php, in there just write down the following code.

<?php
    public function notEqualTo($field, $forbidden) 
    {
	if($field['title'] === $forbidden))
	{
		return false;
	}

	return true;
    }

This method is really quite easy and just forbids to use a value. So we just created a new validation rule, called notEqualtTo. Its first parameter is the value of the field on which you’ll put the validation rule, the second is a parameter we set here for a forbidden value, it can be whatever you need/want. Stick in your Post model, and in the $validate, just add this.

<?php
    public $validate = array(	
	'title' => array(
		'rule1' => array(
			'rule' => array("notEqualTo", "my awesome title"),
			'message' => 'This title is forbidden.'		
		),
                .....
	),
    );

So here we just said that we want the rule notEqualTo applied on our title field, as a second parameter we gave the string “my awesome title”, which means that when you’ll try to validate, if your title is “my awesome title”, the validation will fail. It’s now up to you to do more itneresting validation rules.
P.S : you can also define your validation rules in the AppModel, it means that you could use them in all your models.

  • How to get rid of CakePHP’s default routes?

Other easy thing, open the app/Config/routes.php file and comment or erase this line (should be at the bottom of the file)

<?php
// 	require CAKE . 'Config' . DS . 'routes.php';

Don’t forget to define your routes else this won’t work well.

  • How to debug?

Two interesting methods :

    – Debugger::log($this->request->data); which saves into a file in /app/tmp/logs/debug.txt

    – debug($this->request->data); which displays its result directly (it’s an “upgraded” var_dump)

The documentation is quite good, so have a look at it there.

  • How to override validation message?

Let’s say you have a custom validation rule on a field in your $validate. But your validation function can return different kind of errors according to what went wrong?  The important word was return. To do so you just have to return the error message from your validation function if it failed and true if it went well. Example !

// your validation rules
public $validate = array(
    'name' => array(
	'rule1' => array(
         	'rule' => array('myIsValid'),
	        'message' => 'My useless message.'
	),
);

// the homemade validation
public function myIsValid($field)
{
    $return = true;

    if($field['name'] != 'bla')
    {
        $return = 'wooops ! it should be equal to bla !';
    }
    else if(other stupid condition)
    {
        ...
    }

    return $return;
}

The point here was to show that you can return a string has the error message, it will be in the validationErrors. Enjoy !

Use tinymce in CakePHP with a behavior

Hey !
I went through the problem of using WYSIWYG to have more nicer news, comments … well giving a new possibility to the users to personalize what they want to write.

I tried two possibilities : making my own BBCODE or using a WYSIWYG editor. I will talk a “bit” about these two options, if you only want the explanations about how to set it for your CakePHP, skip it and go to this section Tinymce, HtmlPurifier and CakePHP behavior.

BBCODE
It is a very good way to be protected from xss or any other malicious content because you convert directly the bbcode into (x)html. Though, it’s a bit annoying to use/do and limits the possibilities. Also, one good point against is the fact that you have to “decode” (i.e to convert it in (x)html) each time you want to display it, if you do it once when you save the content, you can’t reverse the process, and you get (x)html when you want to edit your content. To be true, you could reverse it, but it’s not the wisest way.

WYSIWYG
Well, it is pretty much better here. We directly work with (x)html which is easier. We save directly (x)html in the database when we create our news, comment, … and we just display the content when we want, no process. BUT ! it’s plain (x)html we have and it means the users can write what they want, your layout can be broken, or worst they can write some javascript and mess your website.

Obviously I ended up by using a WYSIWYG editor, tinymce but as I said previously, you have some good advantages, the good looking, the ease of use but a vulnerability : xss attack. So the goal is to prevent it and secure the content the users will write. There we won’t use htmlentities because the goal is to “execute” the html by the browser, so we have to get some tags, and get rid of the others. We could use strip_tags, but let’s see it’s not the safer way.

Let’s admit we allow the tag <p>, here is what we could do.

    $data = "<p onmouseover=alert("xss")>blablabla</p>";
    $data = strip_tags($data, '<p>');
    echo $data;

Put your mouseover the <p> tag (i.e blablabla) and you will see that “xss” pop up. Well it’s logic because we allow the <p> tag, would be the same with <span> and so on. It means we can allow some tags but not be totally safe.

HtmlPurifier

So let’s use HtmlPurifier, it’s a filtering PHP library which “purifies” your html, it even deals with malformed tags and follows the specifications ! It’s THE massive weapon ! You can do a whitelist of the acceptable tags and even decide which attributes of these tags are allowed. Well I discovered it few days ago, so I’m no expert but we’ll get down to some configuration.

Tinymce, HtmlPurifier and CakePHP behavior

I’ll explain here how to set tinymce and htmlpurifier with CakePHP using a behavior. First we need a copy of tinymce, you can find it on their website : tinymce, take the latest production version. Then you can put it in your webroot/js or you create your own folder “lib” in the webroot, do as you wish it doesn’t matter as long as it is in the webroot. Next step is to configure the tinymce editor to fit our needs. Create a file “config.js” in your tinymce folder and put what follows into.

 
tinyMCE.init
(
    {
        // General options
        mode : "textareas",
        theme : "advanced",
        plugins : "paste",
        width : "100%",
        height : "100%",

       // Theme options
       theme_advanced_buttons1 : "bold,italic,underline,strikethrough,forecolor,|,justifyleft,justifycenter,justifyright,justifyfull,|,bullist,numlist,|,undo,redo",
       theme_advanced_toolbar_location : "top",
       theme_advanced_toolbar_align : "left",
       theme_advanced_statusbar_location : "bottom",
       theme_advanced_resizing : false,

      // Paste plugins configuration
      paste_auto_cleanup_on_paste : true,
      paste_remove_styles: true,
      paste_remove_styles_if_webkit: true,
      paste_strip_class_attributes: true,
      paste_retain_style_properties : "none",
      theme_advanced_disable : "styleselect",
      convert_fonts_to_spans: false,

      // No css
      content_css : false
    }
);

Here what’s important is the field “theme_advanced_buttons1” which is the list of the tags we display in our editor, so the ones we let our users use. It has to fit with the configuration of HtmlPurifier we will do just after. What we accept is basic things such as list, text position, underline, bold, etc …

P.S : I added the plugin “paste”, very useful when u copy/paste stuff in the text editor, there it will clean the content, or keep what’s allowed.

P.S2 : Tinymce has a lot of parameters, you can really set a lot of things. There I show a “little” example.

Now HtmlPurifier ! First, we need the latest copy that we can download here. I decided to wrap it in a CakePHP behavior.  Why? Because I think it’s “smarter”, when we add/edit a comment, post, … we purify the content before to save it in the database. If you read well, you saw before and save. And yes it’s a callback method of the CakePHP model. Consequently, we will create a behavior that we will link to all our models that need it and through the beforeSave callback, we will purify.

Thus, let’s put our HtmlPurifier folder in the vendors. Rename your folder “HtmlPurifier-blabla” to only “htmlpurifier”, it’s just more convenient.

We can create a behavior in app/Model/Behavior, under the name of PurifyBehavior.php, and it should look like this.

 
class PurifyBehavior extends ModelBehavior
{
    function setup(Model $model, $settings = array())
    {
        //field is the field we purify by default it is called content
        $field = (isset($settings['field']))?$settings['field']:'content';
        $this->settings[$model->alias] = array('field' => $field);
    }

    /*
        cleaning before saving
    */
    function beforeSave(Model $model)
    {
        //convenient to get the name of the field to clean
        $field = $this->settings[$model->alias]['field'];

        //check if we are working on the field
        if(isset($model->data[$model->alias][$field]))
        {
            //get htmlpurifier => http://htmlpurifier.org/
            App::import('Vendor', null, array
            (
                'file' => 'htmlpurifier'.DS.'library'.DS.'HTMLPurifier.auto.php'
             ));

            //set its configuration
            $config = HTMLPurifier_Config::createDefault();
            $config->set('Core.Encoding', 'UTF-8');
            $config->set('AutoFormat.RemoveEmpty', true);
            $config->set('Core.EscapeNonASCIICharacters', false);

            /*
            allowed HTML elements according to tinymce's configuration => p[align] to allow p and the attribute align
            */
            $allowedHTML = "em,strong,p[style],ul,ol,li,span[style]";
            $config->set('HTML.Allowed', $allowedHTML);

            //cleaning
            $purifier = new HTMLPurifier($config);
            $model->data[$model->alias][$field] = $purifier->purify($model->data[$model->alias][$field]);
        }

        return true;
    }
}

We use the setup method to tell the behavior which field we want to purify, let’s say you do a news, you call the content field “body” and if you do a media, you could call it “description”, it’s why I did it this way. We will see how to set it in the model below. By default, the field value would be “content”. Then we use the callback beforeSave to purify. First we put in a variable the name of the field, it’s “more readable”. We check that the field exists. Why? For one good reason. The callback is fired each time you save something. Let’s say you want to save one field only with saveField, the behavior will fail because the field of the PurifyBehavior will not be set. We import the htmlpurifier library from the vendors.  Then create a default configuration. We set few parameters, encoding, removeEmpty (let’s say you have a span looking this <span></span>, htmlpurifier will erase it), etc … check the documentation for more parameters. Important there is the allowedHTML where we write all the tags and theirs attributes according to tinymce’s previous configuration ! It’s pretty easy to understand and to change, right? Finally we purify our field of the current model. Ensure of returning true or the save will fail !

P.S : You can of course add some conditions to check and fail the save by returning false. You may use $model->invalidate(‘name of the field’, ‘message to display’) to invalidate the field in your form and display an error message.

Last but not least, we have to do a small modification of all our models using the behavior. To tell them they have to behave with the purifybehavior. Let’s see how to do this, it’s the easiest and smallest part.

    public $actsAs = array('Purify' => array('field' => 'description'));

Pretty easy ! We just tell our model to use the PurifyBehavior and we set the field to “description” here. If your field’s name is “content” you don’t even have to set the array ! It would give something like this.

    public $actsAs = array('Purify');

Et voilà, you have proper html saved in your database, your users are less limited and your website is safer.

P.S : I didn’t say totally safe !

Using pass parameters with the Paginator component

CakePHP’s paginator is a great component allowing you to paginate what you want.

Though, I went through a “problem”. I wanted to make custom routes and I didn’t find how to set the paginator to use the pass parameters (example : /posts/index/1) instead of the named params (example : /posts/index/page:1) or the query string (example : /posts/index/?page=1). The problem was occuring to get the links using the methods next and prev of the paginator in the view, always giving me named parameters or query string.

My aim was to write a route like this /news-page-1 where 1 is the page number, and a pass parameter !

I did a small hack providing a custom route with pass parameters. This problem is maybe due to my lack of knowledge concerning this component, but I couldn’t find anything to use pass parameters for it.

We will see how to do it through an example with Post.

PostsController

 
   //using the Paginator component
   public $components = array("Paginator");

   //index method
   public function index($page = 1)
   {
        $this->Paginator->settings = array
        (
            'Post' => array
            (
                'limit' => 2,
                'page' => $page,
                'conditions' => array
                (
                    'published' => 1
                ),
                'order' => array
                (
                    'created' => 'DESC'
                ),
            )
        );

        $posts = $this->paginate();           
	$this->set('posts', $posts);
    }

It’s pretty much easy to see what we are doing here. We limit the number of Posts on each page to 2, we want only the published posts and we want them ordered by created desc. I added the $page variable which is there to say which page we are looking for.

Let’s deal with the custom route we wanted, remember? We want /news-page-1 (feel free to do the route you want :p)

app/Config/routes.php

    //setting the route with the pass parameter page
    Router::connect
    (
        '/news-page-:page', 
        array('controller' => 'posts'), 
        array
        (
            'pass' => array('page'), 
            'page' => '[0-9]+'
        )
    );

Let’s see how to generate the link for the view.
View/Posts/index.ctp

    //put this where you want the previous link to be            
    if($this->Paginator->hasPrev()):

        //grab the current page number           
        $page = $this->params['paging']['Post']['page'];
        
        //decrement to get the previous page
        $page--;

        //generating the link
        //giving $page as a pass parameter
        echo $this->Html->link
        (
            '← Previous', 
            array
            (
                'controller' => 'posts', 
                'action' => 'index', 
                'page' => $page
            ),
            array('escape' => false)
        ); 

    endif;

You can notice we don’t use the prev method of the paginator to generate the link. You can also write the next link, the same way than for the previous, but use the hasNext method to check if there is a next page and increment $page. Also change the name of the link 😉

Et voilà we have a nice url using pass parameters with the paginator component.

P.S : feel free to give feedback about the paginator, if you found a way to get it working with pass parameters, thanks.

Use your own style for a form

When you create a new form with CakePHP, it sets automatically some style around your inputs. We will see here how to get rid of it and use our own style.

echo $this->Form->create('Post', array('inputDefaults' => 
array('div' => false, 'label' => false, 'error' => false)));

We create a form, the first parameter is the name of your model, the second is an array of parameters but I will talk only of the key “inputDefaults” which deals with all of your inputs in this file.

  • div => (true or false), sets or not the div wrapper
  • label => (true or false), sets or not the label
  • error => (true or false), display errors or not

And then for one input, the title input for example.

echo $this->Form->input('title', array('class' => 'span3'));
echo $this->Form->error('title', null, 
array('class' => 'help-block alert alert-error', 'wrap' => 'p'));

The first line we set the input “title” with the class “span3”.

The second line is there to print the error if one occurs after the validation process, and of course, we set some classes and a wrapper element, here a paragraph, but you can put a span, a div. And voilà !

How to set HABTM

I saw few documentation about the HABTM relation, so I thought of writing some few things I have found out after my researches. Here I will show a HABTM with two tables, Posts and Tags. A post can have many tags and of course a tag can belong to several posts.

Database (posts table taken from CakePHP blog tutorial)

CREATE TABLE posts 
(
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(50),
    body TEXT,
    created DATETIME DEFAULT NULL,
    modified DATETIME DEFAULT NULL
);

CREATE TABLE tags 
(
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50) NOT NULL
);

CREATE TABLE posts_tags
(
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    post_id INT NOT NULL,
    tag_id INT NOT NULL
);

This is just example tables, you may add your engine, encoding and put the fields you want.
Just remember that the associated table name must by convention have the name of the two tables you link, and in alphabetical order. It is posts_tags and not tags_posts ! ! ! But you are not forced to use conventions, they are just convenient, and there I will use them.

After the database, here comes the time of setting the models. I will only show the important part about HABTM in them.

Post Model

public $hasAndBelongsToMany = array
(
	'Tag' => array
	(
		'className' => 'Tag',
		'joinTable' => 'posts_tags',
		'foreign_key' => 'post_id',
		'associationForeignKey' => 'tag_id',
		'unique' => false
	)
);

Tag Model

public $hasAndBelongsToMany = array
(
	'Post' => array
	(
		'className' => 'Post',
		'joinTable' => 'posts_tags',
		'foreign_key' => 'tag_id',
		'associationForeignKey' => 'post_id',
		'unique' => false
	)
);

You can also create a model called PostsTag, if you need. Again with the convention, alphabetical and plural for the first model, not the second, it is PostsTag and not TagsPost.

Now let’s go to the controller Post. For what I saw, you can’t save your new post and its tags in one shot. It comes from the fact that you need the id’s post to save the association.

So first we save the post, nothing new here. You may just send the Post content, test if it was successful, I just go to the essential.

	$this->Post->save($this->request->data);

Now we have the id of the post in $this->Post->id. We just have to save our tags and add the id in its structure. Here we assume $tags is an array of tag you get from your view.

	foreach($tags as &$tag)
	      $tag['Post']['id'] = $this->Post->id;

        $this->Post->Tag->saveAll($tags);

There we had the id to all our tags and save them all in one shot. What’s magic here is that cakePHP directly save the tags but also the associations between the tags and the post.

Now let’s say you want to erase all the associated tags of one specific post, you can do like this.

	$this->Post->PostsTag->deleteAll(array
        ('PostsTag.post_id' => $this->Post->id), 
        false);

And if you just want to erase one association, just specify the id of the post and the id of the tag you don’t want anymore. And voilà !

HABTM are tricky, I may not be very clear to explain this point, and I surely don’t know the whole about it, don’t hesitate on correcting me if needed 😉