Friday 25 April 2008

More on Grids

The more I read about this subject, the more interesting it becomes. Try this presentation by Khol Vlnh at SXSW Interactive last year. Well, that's why we use grids.

Then try Mark Boulton's post about Blueprint. Not sure I agree with all of this tho (as per my previous post).

And then theres this.... http://webpatterns.org/. Its not there yet, but looks very cool.

Wrestling with CSS Frameworks

Say you're working on a project where you need to standardise your entire layout to a grid system, and you want your entire organisation to conform to this in a single way in code? Not easy. Everyone does their layout differently, but it looks the same! Why do this, why not code the same way to make things look the same. The best way, the right way.

Granted this doesn't work for decor, typography etc, but for layout it does. This is where some CSS Frameworks come it. The main ones being YUI Grids and Blueprint CSS.

On the face of it, they looks great - especially Yahoo's Grids Builder interface. Build a page in 8 clicks, copy paste the code. Done. How easy was that!

BUT. The world is never so rosy. What about semantics? Even back in 2006, Elliot Swan talked about how un-semantic the markup is when produced using these frameworks. The principle objection is that you have to have these ugly class names like "span-6 append-3 last" all over your beautiful document structure which relate not to the type of content, but purely to the layout. Not Cool.

Or is it? Here comes pragmatism v.s. purity again. In an ideal world, what we need in CSS is class-based inheritance. So you can define a class like "menu", give it some decor code, and then tell it to inherit another class like 'span-6' which makes it fit your grid. How cool would that be. You could program your CSS just like an app. You'd never have to repeat yourself in CSS declarations, your markup could be clean, single-class-per-element, and semantic.

However, the world isn't like that. The only way to get an element to work like this it to give multiple classnames to an element -- OR -- manually build your site's css to match the grid based on your semantic classnames - meaning that just like we do now, we have to re-define the grid every time.

I'm not decided on this yet, but I'm leaning towards the pragmatism of ugly grid-based classnames ALONGSIDE nice semantic classnames too. Something like this... class="5cols menu" where '5cols' is your grid CSS, and 'menu' is obviously your semantic one.

Why do I think that? Well, layout is already implied by the position of the content in the DOM - or it should be in 90% of cases. This means that layout CSS, specifically grid and only grid layout CSS is already implied in the DOM, so why do you have to be semantic with it? Other CSS stuff like microlayouts (borders, padding), decor and typography CSS for example DO belong under semantic classname definitions.

Its a delecate balance between purity, and a pragmatic 'lets get things done' attitude. For the latter, CSS Grid frameworks make a lot of sense. However, when used carefully, they can live alongside pure semantic markup.

At least this is the best we can do until css inheritance comes along -- which'll be never.

Want another opinion? Have a read of Nate Koechley.

Friday 18 April 2008

CakePHP Tree Behaviour

Its often really useful to be able to represent data in a tree, or self-referential way - like product categories, music genres, organisational structures etc. Often, that takes quite a bit of code. However thanks to CakePHP, we can do it in far less lines of code, thanks to a behavior var $actsAs = array('Tree');.

First create a db table as normal with an id and title. Then add three special fields: 'parent_id', 'lft' and 'rght' - all Integers. Cake uses these to manage the hierarchical relationships between each row, and it does this all behind the scenes. Next, add the behaviour tag to your model. My example is for Genres. and its as easy as this... in genre.php...


var $actsAs = array('Tree');


Some of the clever stuff happens in the Controller to make nice URL's so lets look at that now.

What I want is a URL structure like this www.example.com/genres/genre1/genre2/genre3 etc where genre 3 is the child of genre 2 which is the child of genre 1 etc. To get this behaviour, we first add a line to our app/config/routes.php file.


Router::connect('/genres/*', array('controller' => 'genres', 'action' => 'index'));


Its a very simple line which routes any sub URL to the index action of the genre controller. Then we can do all the goodness there. So, in our genres controller, we need to get to work building a clever index action.

First, we get the current URL, split it into an array on every slash using explode. If there are no other URL paths, we assume we're at the route and list all top level genres accordingly. If there are other paths, then we can lookup current genre (the last element in the array) from the database.


function index(){
$url = $this->params['url']['url'];
$genreUrl = explode('/',$url);
if (count($genreUrl)>1){
$genreCurrent = $genreUrl[count($genreUrl)-1];
if($genre = $this->Genre->findByTitle($genreCurrent)){
pr($genre);
}else{
$this->Session->setFlash('There such genre as ' . $genreCurrent);
$this->redirect(array('action' => 'index'));
}
}else{
$genres = $this->Genre->findAll("parent_id IS NULL");
pr($genres);
}
}


There are some problems with this so far. It only works if all your genre names are unique, and worse, allows you to specify any path (like www.example.com/genres/blah/this/is/wrong/validGenreName) where validGenreName is valid at any level, but the path to it does not reflect it's true higherachy. To fix this, we're now going to parse every genre given in the URL, and check to see if the one before it, and the one after it in the DB match those in the URL. This way we can ensure the URL is a true reflection of the database structure.

I've got to go now, but I'll come back and finish this post when I get another chance.

There's more info on Tree Behaviour in the CakePHP 1.2 manual.

Text to Speech

Now this is incredible.

http://www.cereproc.com/demo.html

And this... https://www.cepstral.com/demos/

Text to speech has come on a long way from the Stephen Hawking-esq stuff of the 80's.

Wanna have a real play? Make George W Bush say some dodgy stuff in this demo.

Thursday 10 April 2008

HABTM Relationship building in CakePHP

Yeah, I know its not as good as rails, but i'm trying to get to grips with it anyway: CakePHP. I'm gonna start using this blog as a way of keeping a note of things I learn as I build my first apps - mainly for my own benefit, but also to help the active and very friendly CakePHP community. Documentation for 1.2 is currently shocking, so it takes a lot of trawling to get what you need - so I need my own reference.

That said, heres my first nugget of gold.

Has And Belongs To Many (HABTM) relationships are great, but manipulating those relationships is hard. If you want to create a new object, and save relationships to it a the moment of creation, I found it hard to work out how to do both at the same time. Turns out its all about how you push data into $this->data and then pass this to be saved.

I needed to create a new Bulletin object, tie the current logged in user to it, and fill it with 5 (currently random) news stories which already exist. First I found the user_id of the current user and added that relationship to the Bulletin object using...

$this->Bulletin->create();
$user = $this->Auth->user('id');
$this->data->Bulletin->user_id = $user;

Then I got an array of the stories I wanted to attach (currently 5 random ones). Then, heres the tricky part, I had to extract their arrays, as the data findAll() returns can't be shoved into $this->data. Instead, $this->data wants an array of IDs which it can save. To do this, I did it messily....

$stories=array();
$this->Bulletin->Story->recursive = 0;
$stories = $this->Bulletin->Story->findAll(null, null, 'RAND()', 5);
$story_ids = array();
foreach($stories as $story){
$story_ids[] = $story["Story"]['id'];
}
$this->data->Story->Story = $story_ids;

Now that $this->data contains everything we want, we can just save it as normal by passing $this->data to the $this->save() method.

if($this->Bulletin->save($this->data)){
$this->Session->setFlash('A new bulletin has been generated for you');
$this->redirect('/bulletins/view/'.$this->Bulletin->id);
}else{
$this->Session->setFlash("There was an error building your bulletin");
$this->redirect('/bulletins');
}

Voila. This works nice. I hit a url like /bulletin/generate, and the system generates a random Bulletin object for me, and redirects me to its view page. Its a bit hacky tho, so any suggestions would be greatfully received.

Friday 4 April 2008

Draggable EPG

So I've been playing with re-creating a google maps style draggable interface for time-based data - anything you can plot with time on the X axis. Looking around for data to try, I thought I'd give 'schedule' data a try.

The cool stuff here is the JS/CSS stuff I've built which allows you to drag around an infinite-size surface, and the JS which interprets the users interactions, and loads in new data accordingly. It seems nice, and has got some nice noises from the guys at the BBC - we're thinking of ways to use it, perhaps the Olympics data, perhaps Glastonbury stages etc - who knows.

I'll post more about how it works later, and its still a work in progress - the code is rubbish and needs improving, but its a start. Take a look... http://www.simoncross.com/epg

At Over the Air

So, today, I'm here... http://overtheair.org/blog/ Which is nice. I'm not staying overnight tho, thats for the proper geeks.

We're here with a bunch of people from the BBC including Jason Quinn, Fraser Pearce, Tristan Fearne, Matt Wood, Paul Clifford, Chris Yanda et al.

We're planning on going to a few sessions, taking a few pics, and doing some mobile web hacking. How much of each we'll see.

Its just about to get started, so I'll keep blogging when theres stuff to say.