NULL Life Is Cake - Posts

Flashers

By: Andrew Posted on 24/8/08

I have flashed a lot of people. Let me qualify that, I have used $this->Session->setFlash a lot of times in my time with CakePHP. In all those thousands of flashes I can break them into just 4 categories: auth, good, bad, and notices

The categories are fairly simple to understand their purposes so I am not really going to go into that but each one in my mind should have a different presentation and because of that I put a little trick into my app_controllers to allow me to simplify the process of defining the flash type. I make 3 functions in my app_controllers, infoFlash, goodFlash and badFlash which all just call the session component through it's setFlash method

Set flash is a part of the session component and is defined as

setFlash($message, $layout = 'default', $params = array(), $key = 'flash')

to call it from a controller normaly, call $this->Session->setFlash and put the other half of the session component (the session helper) in your layout.

 
<?php
	$session->flash('flash');
	$session->flash('auth');
	$session->flash('good'); 
	$session->flash('bad'); 
?>
 

However if you just add this to your app_controller, you'll be able to type $this->(info|good|bad)Flash('Your message in here'); and be able to flash a message quickly and in a very nice looking manner. This is how I definied my flash functions, you may certainly do it in any way you want but here is a sample.

 
	function infoFlash($message, $layout = 'default', $params = array()) {
		return $this->Session->setFlash($message, $layout, $params);
	}
 
	function goodFlash($message, $layout = 'default', $params = array()) {
		return $this->Session->setFlash($message, $layout, $params, 'good');
	}
 
	function badFlash($message, $layout = 'default', $params = array()) {
		return $this->Session->setFlash($message, $layout, $params, 'bad');
	}
 

And now the associated css. I keep a small good/info/bad png image that is 16px by 16px in my img folder so I will use those. Change as necessary.

 
#goodMessage {
	background:#9CED60 url('/img/good.png') no-repeat scroll 8px 11px;
	border:2px solid #6CCC26;
	padding:2px 0px 0px 30px;
}
#flashMessage {
	background:#E6E6FF url('/img/info.png') no-repeat scroll 8px 11px;;
	border: 2px solid blue;
	padding: 2px 0px 0px 30px;
}
#authMessage, #badMessage {
	border: 2px solid #f00;
	background: #fcc url('/img/bad.png') no-repeat scroll 8px 11px;
	padding: 2px 0px 0px 30px;
}
 

Sincerely,
~Andrew Allen

By: Andrew Posted on 24/8/08

I have flashed a lot of people. Let me qualify that, I have used $this->Session->setFlash a lot of times in my time with CakePHP. In all those thousands of flashes I can break them into just 4 categories: auth, good, bad, and notices

The categories are fairly simple to understand their purposes so I am not really going to go into that but each one in my mind should have a different presentation and because of that I put a little trick into my app_controllers to allow me to simplify the process of defining the flash type. I make 3 functions in my app_controllers, infoFlash, goodFlash and badFlash which all just call the session component through it's setFlash method

Set flash is a part of the session component and is defined as

setFlash($message, $layout = 'default', $params = array(), $key = 'flash')

to call it from a controller normaly, call $this->Session->setFlash and put the other half of the session component (the session helper) in your layout.

 
	$session->flash('flash');
	$session->flash('auth');
	$session->flash('good'); 
	$session->flash('bad'); 
 

However if you just add this to your app_controller, you'll be able to type $this->(info|good|bad)Flash('Your message in here'); and be able to flash a message quickly and in a very nice looking manner. This is how I definied my flash functions, you may certainly do it in any way you want but here is a sample.

 
	function infoFlash($message, $layout = 'default', $params = array()) {
		return $this->Session->setFlash($message, $layout, $params);
	}
 
	function goodFlash($message, $layout = 'default', $params = array()) {
		return $this->Session->setFlash($message, $layout, $params, 'good');
	}
 
	function badFlash($message, $layout = 'default', $params = array()) {
		return $this->Session->setFlash($message, $layout, $params, 'bad');
	}
 

And now the associated css. I keep a small good/info/bad png image that is 16px by 16px in my img folder so I will use those. Change as necessary.

 
#goodMessage {
	background:#9CED60 url('/img/good.png') no-repeat scroll 8px 11px;
	border:2px solid #6CCC26;
	padding:2px 0px 0px 30px;
}
#flashMessage {
	background:#E6E6FF url('/img/info.png') no-repeat scroll 8px 11px;;
	border: 2px solid blue;
	padding: 2px 0px 0px 30px;
}
#authMessage, #badMessage {
	border: 2px solid #f00;
	background: #fcc url('/img/bad.png') no-repeat scroll 8px 11px;
	padding: 2px 0px 0px 30px;
}
 

Sincerely,
~Andrew Allen

Revised model links

By: Andrew Posted on 23/8/08

In yesterday's post I discussed creating a global app_model (usgin late static binding) function to create a url and with that rolling around in my head I realized that I myself hadn't taken it far enough. I present to you my revised addition to model (and app_model when 5.3 comes out,) the Model::link function.

One of the things I disliked about Felix's manner of creating the url was that when you were creating a slug you were requerying the database. That is a nominal hit when you only have a few hits a day (like my site currently does) but when you get to a larger volume of trafic, running that 50 time a page can grind things to a halt quickly. To make matters worse you already had pulled the data from the database, you were just neglecting to use it. This is my revised solution, passing the whole db result into the link function and setting options to override what you need to change.

Lets look at an example (pulled from this blog's code since it is where I origionally wrote this.) This is from /app/views/posts/view.ctp so assume that $post is the current post returned from a $this->Post->find('first', array('conditions' => array( 'id' => $id ) ) );

 
	<h2><?php 
		echo Post::link($post);
		if ($session->read('Auth.user.type') == 'admin') echo Post::link($post, array('action' => 'edit', 'title' => '(edit)'));
	?></h2>
	<div class="author"><?php 
		echo __('By', true).': ';
		echo User::link($post).' '; //The model is smart enough to make this a user link
		echo __('Posted', true).' ';
		echo $time->timeAgoInWords($post['Post']['created']); 
	?></div>
 

Now look at that without static methods

 
	<h2><?php 
		echo $html->link($post['Post']['title'], array('controller' => 'posts', 'action' => 'view', $post['Post']['id']);
		if ($session->read('Auth.user.type') == 'admin') echo $html->link('(edit)', array('controller' => 'posts', 'action' => 'edit', $post['Post']['id']);
 
	?></h2>
	<div class="author"><?php 
		echo __('By', true).': ';
		echo $html->link($post['User']['name'], array('controller' => 'users', 'action' => 'view', $post['User']['id'])).' ';
		echo __('Posted', true).' ';
		echo $time->timeAgoInWords($post['Post']['created']); 
	?></div>
 

I for one, prefer the first method. There are a few extra benifits from this that I didn't forsee, like inline keywords in content. For me I just added a regexp replacement that replaces in my posts [post::guid] with the evaluated form of [Post::guid] and [Post::url::guid], I will never again have my links go out of date and fail due to a changed slug. Put this in the model you're interested in allowing the static link function on.

 
	/**
	 * Link function
	 * 
	 * Create a link either from an object or a passed ID
	 */
	Public Static Function link ($object = null, $options = array()) {
		$model = 'Post';
		$nameField = 'title';
 
		if (!is_array($object)) {
			$object = array(
				'id' => $object,
			);
		}
 
		//This means we have been given this through a belongs to / hasOne (or cascade from a passed id)
		if (!isSet($object[$model])) {
			$object = array(
				$model => $object
			);
		}
 
		if (isSet($options['title'])) { 
			$title = $options['title'];
			unset($options['title']);
		}
		if (isSet($options['class'])) {
			$class = "class=\"{$options['class']}\"";
			unset($options['class']);
		} else {
			$class = '';
		}
 
		$options = array_merge(array(
			'controller' => inflector::underscore(inflector::pluralize($model)),
			'action' => 'view',
			$object[$model]['id']
		), $options);
 
		$url = Router::url($options);
 
		if (!isSet($title)) {
			//If it is post it will be come $post['Post']['title']
			if (isSet($object[$model][$nameField])) {
				$title = $object["$model"][$nameField];
			} else {
				$title = ClassRegistry::init($model)->field($nameField, array('Post.id' => $object[$model['id']));
			}
		}
		return "<a {$class} href=\"$url\">$title</a>";
	}
 

Sincerely,
~Andrew Allen

Static URL's

By: Andrew Posted on 22/8/08

One of the neat features of PHP 5.3 (the lowest accepted version of PHP in the 2.0 release of CakePHP) is called late static binding. Late static binding is one of those ideas that is a little hard to grasp but if you play enough with singletons you will inevitably run into a good use for it.

First I will briefly explain what static binding is. Static binding (a.k.a. "early binding") is when during compilation the location of the jump in code (ASM JMP) can be determined with ease and because of this, the compiler simply can give it a location in memory to run. Late static binding allows that resolution to be done when the interpretor hits the line of code during execution. The best example that requires this style is when you need to do inheritance backwards. Sometimes it's useful to write a method in a base class that pulls data from the class that is abstracted from it. Late static binding allows this to happen with the "static" keyword.

Sorry to mention him 3 times in consecutive posts but I really like Felix's stuff especially his post on the topic of reverse routing. He does some really neat things in there, but I think that his Post::url() doesn't take it far enough, I'm lazy and I don't want to write one of those for every damn model. I think this is a perfect place to use late static binding and override in models if its not right. If all your models extend from AppModel it's as simpile as this

In 5.3 it would look something like this

Note: This may not be prefect since I don't yet have any servers running 5.3 when I have a 5.3 server running I'll update this post
 
class AppModel extends Model {
 
	static $staticName;
 
	public function __construct() {
		$args = func_get_args(); 
		call_user_func_array(array($this, 'parent::__construct'), $args);
		static::$staicName = $this->name;
	}
 
	public static function url ($id = null, $options = array()) {
		$name = inflector::underscore(inflector::pluralize(static::$staticName))
		if (!isSet($options['action'])) $options['action'] = 'view';
		return Router::url(array(
			'controller' => $name,
			'action' => $options['action'],
			$id
		));
	}
}
?>
 

Unfortunately though, 5.3 isn't "stable" yet, so until that is the case this little trick won't be available for all models by default in my applications. Some day soon though it will be. Until that day, just change the static url function per class to make it match the proper circumstance. Remember to keep those models fat.

Sincerely,
~Andrew Allen

Welcome

By: Andrew Posted on 20/8/08

Hello my name is Andrew Allen, freelancer and student in the state of Colorado. This is my little contribution to the CakePHP community. I've long been sucking the intelligence of men much smarter than I, and I've decided to share what little tidbits of knowledge that I can with anyone who happens to grace my pages.

Much in the same fashion of Felix Geisendörfer I feel that I too (along with my new semester starting next monday) will start blogging again. I'm going to throw out whatever Cake tidbits that I can to see if any of it become useful to anyone. All of this will culminate in the release of a behavior that provide you the ability to treat remote xml feeds as you would regular models.

This being the first real post on Life Is Cake I would like to take this opportunity to say that any code released here is licensed under the MIT license unless otherwise stated.

One of the things I often deal with are seemingly insignificant requirements for page url's. However, that said, the client is always right. One of my recent projects asked me to modify the urls so that prefix routing would occur based on a variable in the session ("Auth.User.type"). If the user is logged in as admin and goes to /posts/edit/2 they get routed as if they went to /admin/posts/edit/2. This (in the minds of the client) meant that it was more secure. After attempting to assure them that that was not the case and that it would be secure either way I started drafting a little code.

What I did was essentially override the default route that is implied at the bottom of /config/routes.php with a route that pulled the user's type in and I arrived at this (rather hackish) solution at the bottom of my router file.

 
<?php
/*
... Some routes that are important to your project
*/
/**
 * Connect the prefixes
 */
$session = new CakeSession();
$session->start();
$prefix = @$session->read("Auth.User.type"); //Don't show any warning if it doesn't exist
Router::connect('/:controller/:action/*', array('prefix' => $prefix));
Configure::write('RouterPrefixing', true); //Used possibly to check against in some of the controllers
 
?>
 

One of the unforeseen benefits of this is that it's no matter if Router::url decides to forget to put the prefix onto the link destined for the user, as it will get routed magically to the right place based on their user type.

Day 1 – check.

Sincerely,
~Andrew Allen

<< Newer
|
Older >>