gignus.com

Web development

Welcome to gignus.com, this are my latests post.

HtmlHelper url/link hack for using your main layout in a plugin

Created: 2008-06-27 09:02:02, last updated: 2008-06-27 09:03:32

When we include our main layout from a plugin, a prefix is added to all $html->link() method calls, breaking our links. A solution is to set plugin = null on each call but it isn't very DRY. Talking with one of my friends, Walter, we arrive to the following code.

At the top and bottom of your layout file add:

$_this =& Router::getInstance();
$params = $_this->__params;
$_this->__params[0]['plugin'] = null;

//  your
//  layout
//  code ...

$_this->__params = $params;
unset($params);

This is an ugly hack, but we remove the plugin option in the global parameters that cake uses to add the plugin prefix, so all calls to Router::url() here will not be seen as in the context of a plugin. At the end of the layout we restore the global parameters.

5 comments

Auth Component: Using hashPasswords

Created: 2008-02-24 19:32:43, last updated: 2008-02-24 19:54:45

If you have used Cake's Auth Component, you should know that it hashes your password field from your Auth's model name automatically. So, when you make some sort of confirmation password in your accounts creation, you have to make something like this to validate your password:

Extracted from Chris Hartjes blog: Simple User Registration in CakePHP 1.2, Part II

if ($data['password'] == Security::hash(Configure::read('Security.salt') . $this->data['User']['password_confirm'])) {
        $valid = true;
}

With my friend mozart_ar, we consider that there should be lesser weird way of doing it. We thought password hashing should be either in the model or the controller, but not in both. Hopefully, Cake's Auth Component give us the possibility of doing this in a clean way: using the authenticate attribute of the Auth Component.

The authenticate attribute accepts an object reference. Then, it checks if a hashPasswords function exists in that reference. If exists, calls our custom hashPasswords function. For example, in your User Model you can create:


class User extends AppModel {
    // Your code
	
    function hashPasswords($data) { 
        if( isset($data[$this->name]['password']) ) {
            $data[$this->name]['password'] = $this->hashString( $data[$this->name]['password'] );
        }
        if( isset($data[$this->name]['password_confirmation']) ) {
            $data[$this->name]['password_confirmation'] = $this->hashString( $data[$this->name]['password_confirmation'] );
        }
        return $data;   	    	
    }
    
    function hashString( $string ) {
        return Security::hash(Configure::read('Security.salt') . $string );  
    }
}

Now for this to work, you must pass a User's model reference to the Auth Component. You can put this in the app_controller.php, but you should need to create an object reference of the User Model in every action of your controller. As you generally will use this functionality in your users/register or users/add action, you can put this in your UserController:


class UsersController extends AppController {
	// Your code...
	
	function beforeFilter() {
		$this->Auth->allow('login', 'logout', 'register');
		$this->Auth->authenticate = $this->Account;
		
		parent::beforeFilter();
	}
	
	// Rest of your actions
}

So now when checking passwords equality you just do:

if ($data['password'] == $this->data['User']['password_confirm']) {
        $valid = true;
}

Other useful resources:

I hope you like this lovely use of this Auth Component feature as much as I do.

4 comments

Cake Unit Testing: Dropping and recreating tables on each test takes too much time

Created: 2008-02-07 14:25:17, last updated: 2008-02-07 22:48:47

In my last post, I extended the CakeWebTestCase class to recreate the tables instead of truncating them on each test. Now I think this might not be a good idea.

At first I liked it, but when the quantity of tests grew up, recreating the tables took too much time and my tests began to fail. I have to add a sleep of two 2 seconds to make them work, and that's a lot of time to spent on something that should be immediate.

So here is what I do now:

  1. Add a new property: $methods to the class, with startcase and endcase (following CakeTestCase convention), and override Simpletest's getTests() to make it call them on the start and end of each test.
  2. Call the fixtures loader start method on startCase and the end method on endCase.

class CustomCakeWebTestCase extends  CakeWebTestCase  {
	public $methods = array('startcase', 'endcase');
	
	function startCase() {
		$this->fixturesLoader = new FixturesLoader( am($this->fixtures, $this->defaultFixtues) );
		$this->fixturesLoader->start();		
	}
	
	function endCase() {
		$this->fixturesLoader->end();
	}
	
/**
 * Gets a list of test names. Normally that will be all internal methods that start with the
 * name "test". This method should be overridden if you want a different rule.
 *
 * @return array	List of test names.
 *
 * @access public
 */
	function getTests() {
		$methods = parent::getTests();
		$methods = am(am(array('startCase'), $methods), array('endCase'));
		return $methods;
	}
	
}

Now, the tables are generated on the beginning and end of each test case. It's not as safe as before, but we can live with the risk. What risk? Well I can only think of one right now: when using transactions if you start one and you don't close it, the truncating of the table may fail, but that's easily fixable by checking for an open transaction on the tearDown() method and closing it.

15 comments

Copyright © 2007 Matias Lespiau - Powered by CakePHP