Hey folks,
lets say you have a model called Upload. Your Upload model has some generic fields like this:
The problem
However, you would like to store different types of meta information for different kinds of uploads. Examples:
Images:
- Width
- Height
- Quality (if jpg)
- Camera
- Lens
- Focus
Videos:
PDFs:
ZIPs:
- Original Size
- File Count
- Compression Rate
The solution
So what are you going to do? Add 13 fields to your uploads table? Probably not. It is time to normalize things:
Ok nothing fancy so far. CakePHP's associations make it easy to deal with it. However, working with this setup can be a little inconvenient at times. Everytime you fetch a set of records from Upload, you will have to manually extract the meta information from the associated UploadField records:
-
Upload:
id: 1
name: funny.mov
type: video/quicktime
bytes: 20480
created: 2008-06-01 14:47:23
UploadField:
-
id: 1
upload_id: 1
key: fps
val: 26
-
id: 2
upload_id: 1
key: bitrate
val: 376
So everytime you want to access your videos bitrate you will have to search your UploadField records for the 'bitrate' key. How annoying. But worry not, Expandable comes to rescue. With the Expandable behavior activated on your Upload model, your resultset will look like this:
-
Upload:
id: 1
name: funny.mov
type: video/quicktime
bytes: 20480
created: 2008-06-01 14:47:23
fps: 26
bitrate: 376
UploadField:
-
id: 1
upload_id: 1
key: fps
val: 26
-
id: 2
upload_id: 1
key: bitrate
val: 376
But it comes even better. Expandable also makes it dead-simple to create / update UploadField records. This is how it works:
$this->
Upload->
save(array(
'id' =>
1,
'fps' =>
30,
'rating'= >
7/
10,
));
Without you having to do anything, the following happens to your uploads resultset:
-
Upload:
id: 1
name: funny.mov
type: video/quicktime
bytes: 20480
created: 2008-06-01 14:47:23
fps: 30
bitrate: 376
rating: 0.7
UploadField:
-
id: 1
upload_id: 1
key: fps
val: 30
-
id: 2
upload_id: 1
key: bitrate
val: 376
-
id: 3
upload_id: 1
key: rating
val: 0.7
As you can see the fps UploadField value has been updated and a new record with the key rating has been created. So this means you can use the CakePHP form helper to create different editors for your uploads like this:
$form->
input('Upload.fps')
$form->
input('Upload.bitrate')
$form->
input('Upload.rating',
array('options' =>
array(1,
2,
3,
4,
5,
6,
7,
8,
9,
10)));
And even so none of those fields really exist on the Upload model, everything will work just as if they would ; ).
How to use it
- Download the behavior from: the debuggable Scraps repository at github
- Place the expandable.php file into /app/models/behaviors/expandable.php
- Optional: Place the expandable.test.php file into /app/tests/cases/behaviors/expandable.test.php
- Create a table and model for UploadField with at least the fields shown above. (replace Upload with the name of your base model)
- Setup a Upload hasMany UploadField and UploadField belongsTo Upload association
- Add this to your Upload model:
class Upload
extends AppModel
{
var $actsAs =
array('Expandable');
}
That is it. You are ready to go. Enjoy the magic ; ).
Pro |
Contra |
- Easy to use
- Saves db space
- Mostly leverages existing CakePHP magic
|
- (Small) Performance hit while updating meta data
|
Please let me know what you think about this approach!
-- Felix Geisendörfer aka the_undefined