views:

238

answers:

3

I've just started a project and need a little push in the right direction. Here is my table structure:

users         departments     sub-departments
-------       -------         -------
id            id              id
name          name            name
email
password
created
modified

posts         photos        profiles
-------       -------       -------
id            thumbnail     id
content       large         photo_id
created       created       user_id
user_id       profile_id    department_id
profile_id    id            sub-department_id

The profiles table has about 5 more fields that are inconsequential. If any of this looks a little off it probably is.

users have one department
users have one sub-department
users have many posts
users have one photo
users have one profile

What I "wanted" to do was create all my tables I needed and bring them all together under the profile table using foreign keys. A CakePHP snippet of my profile model looks like:

var $belongsTo = array('User', 'Department', 'Sub_Department', 'Photo')
var $hasMany = array('Comment');

If by now you're thinking "WTF is this crap?".. I'm right there with you. This association works in scaffolding. But I don't want to run into trouble when I really start plugging in my logic.

I'm still new to ERD and CakePHP. Should I declare everything belonging to the User and under my ProfilesController query from there like $this->Profile->User->find('all', array('contain' => ....)); ?

I'm a little lost at this point. Any help would be appreciated. How would you implement this?

+3  A: 

You said:

  • users have one department
  • users have one sub-department
  • users have many posts
  • users have one photo
  • users have one profile

Funny terminology...users work in one department and, in fact, work in one sub-department. Since, presumably, a sub-department is a part of just one department, you don't need to record both department and sub-department in the profile table. In fact, if you do record both, you have a complex constraint to enforce. So, unless there's something you've not told us, the department is not needed in the profile. Though, I note that there is actually no cross-reference between departments and sub-departments, so a single sub-department can apparently be associated with multiple departments - there is nothing to stop sub-department number 1 from being associated with one person who works in department 1 and another person who works in department 2. That's unusual - not necessarily wrong, but not the way most organizations work.

There's a temptation to ask "if users only have one photo at a time, and only have one profile, why separate those from the user table?", but there are some reasons for keeping them separate.

One of the key points in modelling tables is to identify the natural primary keys. The ID columns don't count - or can be counted but you need to work out what other combinations of columns must be unique. For example, in the Profile table, although there is a Profile ID, the User ID must be unique according to the rules you stated, so in fact the Profile ID is superfluous - a waste of space, in fact (twice; once for the data column, and once for the index that will be created on it). Now, if you decided that a user might have multiple profiles over time, and profiles have a valid period, or something similar, then the Profile ID column makes sense - but the last bullet point is no longer valid.

In the posts table, why do you record both user ID and profile ID? Again, it gives you a complex constraint to enforce to no obvious benefit. The profile ID is sufficient; from that you can find the user.

You say that a user has one photo, but that is not what you've modelled. You've modelled 'each profile has a photo, and a photo is only used by one profile'. Likewise, in fact, you've not modelled 'users have one sub-department'; you've modelled 'profiles have one sub-department'. Most likely that is a problem with sloppy definitions, but you need to be careful because sloppy definitions lead to sloppy databases, and sloppy databases lead to incorrect answers and bad performance.

Fix those issues and you'd be a long way towards having a more workable design. However, I'm not sure I've discovered all the anomalies that exist in this outline schema.

Jonathan Leffler
Awesome! I'd like to thank you for taking the time and explaining the faults in my design. I agree it is sloppy. This is the first complex (well to me it is) design I've done.
centr0
Note:This comment didn't fit so I posted two. Posting an answer gave me a warning and suggested to leave a comment. :\"There's a temptation to ask "if users only have one photo at a time, and only have one profile, why separate those from the user table?", but there are some reasons for keeping them separate."I had planned on allowing the User to have more than one photo in the future. It's still up in the air. I (kind of) understand what you mean about the my Profile table. You're saying the Users table can be the actual "Profile". Would I then declare a HABTM between Users and Posts?
centr0
I had to go and look up HaBTM - http://www.acronymfinder.com/ knew what you meant. If you combined profile and user (but you may legitimately decide to keep profiles separate from users), then yes, you'd declare a 'has and belongs to many' between users and posts (instead of the HaBTM between profiles and posts).
Jonathan Leffler
Some issues with this answer - natural keys for example are NOT a concept used in CakePHP development. All keys are surrogate keys. This invalidates a great deal of what J. Leffler was trying to communicate (which is good, correct, nth normal form db design) but is irrelevant in this context as you have to have an 'id' column on all tables anyways.
Abba Bryant
@Abba: the answer explicitly recognizes that ID columns may be (you say 'are') necessary. However, there should also be an alternative combination of attributes that is unique - and there should be a unique constraint on that alternative combination.
Jonathan Leffler
I agree, you should have a unique index normally. With Cake however, there is no "may be" necessary with regard to surrogate keys. You have to have an id column on all tables - the only option you have is whether it is auto-increment or UUID based.As for creating a unique constraint on the natural keys, with Cake that will actually cause you issues in some cases. I can tell you don't do Cake development by your answers and wanted to make sure that since the OP is asking about Cake specifically that some of the parts of your **correct** answer that didn't apply were addressed is all.
Abba Bryant
A: 

You can live test CakePHP database design concepts on CakeApp.com.

powtac
+2  A: 

I have provided a sample schema / diagram for you to look at.

A few things to note. The Post model ( posts table ) has a field slug. Use the Sluggable behavior to handle creating slugs here for you.

The Department Model ( departments table ) has a field named department_id, this should be parent_id but the builder tool throws errors on that. Change it as you need it. The lft, rght and parent_id fields correspond to the Tree Behavior. Using the Tree Behavior on the Department Model will allow departments to belong to 'parent' departments arbitrarily. This prevents you from needing a sub_departments table as a sub-department is really a department with a parent_id set to another department's id.

A attached Profile to User, and Photos habtm Profile so that you can have an unlimited number of photos attached to a user. I like to keep my User Model lean, with only what is needed for the Auth Component. The Profile Model is where I would keep first_name, last_name, age, city, etc.

Use an Upload Behavior ( MeioUpload ) on the Photo Model to handle the uploads automatically.

This should be a sound starting point for you to revise your design from.

http://cakeapp.com/sqldesigners/sql/centro

password : centro

Abba Bryant
Cool application! Good hint!
powtac