You are viewing revision #8 of this wiki article.
This version may not be up to date with the latest version.
You may want to view the differences to the latest version or see the changes made in this revision.
- DO name your database tables in the singular, not plural
- DON'T prepend db table names to field names
- DON'T include a table prefix in the model class name
- DO name a table's own ID column "id"
- AVOID semantically-meaningful primary key names
- DO define foreign-key relationships in the database schema
- DO name your foreign key fields ending in "id"
- DO name relations to reflect their singular/plural nature
- Translations
Virtually all Yii applications are built on top of a database, and though Yii is very flexible in how it addresses your DB, some design choices make things more convenient than others.
First, and most broadly, because Yii applications use ActiveRecord so heavily, design considerations revolve around optimization for that use, rather than for a human who will be composing complex SQL queries. In fact, many of these design notes are directly in conflict to best practices for creating SQL-friendly schemas.
But much of this is about creating code that can be read and understood by others, where naming conveys meaning, but where inconsistent conventions will make it much harder to follow.
This is especially the case if you're asking for help in the forums or in the #yii
channel: using odd names that don't reflect good meaning gets you a lot more questions clarifying what the code is doing, and sometimes less help about your actual problem.
Consistency matters a lot.
However, these are only guidelines, not rules, and your code will work if they're not followed. But you'll have an easier path if you adopt them.
DO name your database tables in the singular, not plural ¶
Though we think of an SQL table as holding many of a thing, a model is just one of them: it just seems odd to see $model = new Comments()
, and this oddness shows up again when defining relations, and elsewhere.
Call your table comment
not comments
, invoice
not invoices
, and so on, and reflect the model class names as well (Comment
, Invoice
, etc.)
If you absolutely cannot change the db schema, at least change the Yii model class name to reflect the proper case, but make an extra //COMMENT
in the code to remind the user of this mismatch.
DON'T prepend db table names to field names ¶
This practice is common in traditional SQL schema design, but it's tedious when working with ActiveRecord. In yourcategory
table,
~~~
-- NO -- YES
create table category ( create table category(
category_id INTEGER ..., id INTEGER ...,
category_name VARCHAR ..., name VARCHAR ...,
category_value INTEGER value INTEGER
); );
// YUCK // BETTER $model->category_id $model->id $model->category_name $model->name $model->category_value $model->value ~~~ Doing it the "long" way does make hand-crafted SQL queries a bit easier to read, but use in ActiveRecord less convenient.
DON'T include a table prefix in the model class name ¶
Yii supports the notion of table prefix, which is useful in shared hosting environments where all your applications share a single database. By prefixing your blog table names with blog_
, your timekeeping application table names with time_
, etc. they can all live in the same database without conflicting with each other.
The prefix tbl_
is commonly seen in many tutorials and samples.
But the classes should never contain these prefixes, because you don't have the same need to avoid conflicts: your blog application is different from your timekeeping application.
class TblComment extends CActiveRecord { // NO
class Comment extends CActiveRecord { // YES
It's very distracting to see the prefix everywhere in the code.
DO name a table's own ID column "id" ¶
Many tables have their own independent, single-column unique primary key (int NOT NULL AUTO_INCREMENT PRIMARY KEY
is a common example), and things work a bit more smoothly if it is named id
(not commentid
or postid
).
Though Yii figures out the primary key by reading the database schema no matter what you call it, other parts of the system may not be able to follow, and explicitly depend on the key being id
.
Example: CArrayDataProvider assumes the key is id
, and though you can override it with the keyField
attribute, it's more convenient to avoid the need for it in the first place.
Sometimes this rule doesn't apply, for example when a table has a multi-column primary key or when a table's primary key is a foreign key to another table's ID.
AVOID semantically-meaningful primary key names ¶
A classic design mistake is creating a table with a primary key that has actual meaning. In this example, the user table makes the username the primary key: ~~~ [sql] -- don't do this! CREATE TABLE user (
name VARCHAR(16) PRIMARY KEY, -- bad idea
email VARCHAR...
...
) ~~~ This presents two difficulties:
- it's far less efficient to reference this field from others, because they contain 16 characters instead of a four-byte integer. This can be a real performance issue in a larger application with many references.
- It's very difficult for a person to change his username if foreign-key constraints are enabled in this system: the table field and all the references would have to be changed at the same time, and this will be expensive if it's even possible.
Far better is to create an integral primary key and make the name
unique:
~~~
[sql]
-- much better
CREATE TABLE user (
id INTEGER PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(32) NOT NULL UNIQUE,
email ...
...
);
~~~
This way, changing the person's username involves updating just this one record.
DO define foreign-key relationships in the database schema ¶
Most databases allow the database to define relationships between tables, such that this field holds an ID pointing to the primary key of some other table. These are Foreign Keys, and help provide referential integrity by not allowing you to delete a row if somebody else points to it.
MySQL's InnoDB enforces foreign-key constraints, and though MyISAM allows you to define them, it won't enforce them. Yii knows how to read these relationships from the schema, and the Gii/Giix tools will create relations for you automatically.
But even without Yii considering them, foreign-keys are a vital part of maintaining referential integrity of your database; there are many tutorials on the web on how to learn about them.
DO name your foreign key fields ending in "id" ¶
Related to the prior note, if you have a field that holds the ID of a user, call the field userid
rather than user
. This is because for every foreign key you include in a table, you'll almost certainly wish to define a relation for it.
In Yii, class variables, db fields, virtual attributes, relations all share a single namespace, so it's not possible to have $model->user
be both the foreign key in the table and the relation.
By calling the FK userid
, the BELONGS_TO relation of $model->user
forms naturally and easily:
class Post extends CActiveRecord {
public function relations()
{
return array(
'user' => array(self::BELONGS_TO, 'User', 'userid')
);
}
Note: some prefer to use Id
or _id
instead of id
. This is strictly a matter of personal preference, but be consistent.
DO name relations to reflect their singular/plural nature ¶
Continuing our theme of consistency and making code easier to read by others, the relations ought to reflect their singular/pluralness in their names.
HAS_ONE
- returns a single model: singularBELONGS_TO
- returns a single model: singularHAS_MANY
- returns an array of models: pluralMANY_MANY
- returns an array of models: plural
Note that for relations returning an array, they might have only one model, but the fact that they're an array is what warrants the plural name.
You should always be able to tell just by looking whether a relation returns an array or a model:
$model->post
$model->comments
$model->author
$model->members
If you have to look it up, it makes the code much more difficult to read and maintain.
Translations ¶
- Chinese version: Yii良好的架构(schema)设计指南
- Russian version: Руководство для проектирования хорошей схемы базы данных
- Japanese version: DB スキーマ設計のガイドライン
Excellent article
Hi,
this article summarizes up my experience gathered over few years working with various databases - mySQL, MSSQL, Oracle... Principles for good DB design that will save lots of time and refactoring efforts. Thanx.
Lubos
important read
Even if some recommendations are arguable, and I'm sure some would have different thoughts, this is a basic and important read for all.
Thanks for investing time in creating this!i
nice one
This looks as if I have written it :) Good job.
Cool
Nice tips. Thanks.
A note
Well, just my opinion... I prefer to name table fields with underscores, e.g. "user_id" instead of "userid". First, inside a database it looks better to me and second, sometimes it helps to distinguish table fields and class fields (named in CamelCase). Nice article :)
I couldn't agree more!
After less than an year working on Yii I have come up to the same conclusions as you! In fact my current project follows all of the above conventions and is working very well for me.
Very good article. Thank you!
In fact I would like to see implemented an IDE which enforces them (unless user wishes otherwise)
I think the same ideas
Thank you for your good article!
Awesome
I wish I could upvote this more than once.
And thanks for referencing giix!
Not happy with: DO name your foreign key fields ending in "id"
If table a has PK id and table b has a simple identifying relation to a (a has one b, b belongs to a), then b's PK should be a_id, which should also be its FK.
A must-read article
Thank you,
I immediately modified my sql file.
Many-to-many relationship table name
How do you name an associative table which is needed to break a many-to-many relationship into one-to-many relationships? Thanks for the tips!
Naming join tables (@karminski)
@karminski asked about naming what i call join tables. I like to use the same principles as for other tables: a name that expresses what each row of the table represents in the real world. In a join table each row is a relationship so the name would describe the relationship.
For example, naming the join table between your
person
andmovie
tablesperson_movie
doesn't say much about what the relationship represents: did the person buy, watch, review, star in or direct the movie? So I preferperson_watched_movie
(or whatever).If, in addition to the FKs to the joined tables, the join table contains other information about the relationship then its name might reflect that content more than the relationship. For example, if the join relationship is "person reviewed movie" and the table contains the review text, star rating, date reviewed, etc. then
review
might be a better table name.So and So
I like the idea of convention over configuration but I think there is no FIXED rule over composite (or natural if you prefer) keys and surrogate keys.
I have been thinking and reading (http://stackoverflow.com/questions/159087/composite-primary-keys-versus-unique-object-id-field) and a VERY GOOD framework (like I believe YII2 must be) has to permit some freedom in choosing both approaches and not limiting.
I am specifically referring to GII not supporting composite key CRUD.
I agree with everything except for naming PK's simply using "id"
For me personally, while somewhat redundant, I find it more consistent to name Primary Keys as
SELECT u.id AS User_id, c.id AS Comment_id FROM User u JOIN Comment c ON u.id=c.user_id;
I could simply write
to me provides more clarity and consistency with FK and PK names, than
SELECT u.id AS User_id, u.first_name, c.id AS Comment_id, p.id AS Post_id, p.Title FROM User u LEFT JOIN Comment c ON c.user_id=u.id INNER JOIN Post p ON p.id=c.post_id;
Overall, it's just easier to see at a glance when the FK matches the PK of the target table.
DON'T conflate foreign keys and constraints
A constraint is not a foreign key nor visa versa. You can have a foreign key without a constraint and a good DBMS can support different constraint types, some but not all of which concern foreign keys. The article conflates these and would do well to separate them, for example...
In the case that a table column references another table column,
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.