Relations: BELONGS_TO versus HAS_ONE

You are viewing revision #6 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.

« previous (#4)next (#7) »

It's very common to see new Yii users confusing the relations HAS_ONE and BELONGS_TO, and getting it wrong means you won't get proper values back. And though we'll talk about HAS_MANY as well, we're specifically omitting the MANY_MANY relation because it's a whole different animal.

Both BELONGS_TO and HAS_ONE are about linking two models together, and sound like the same thing, but they link in essentially opposite directions. Let's illustrate with three simple tables, each of which has a primary key (id), and a number of linking fields (user_id) that reference the User table. ~~~ USER table

  • id
  • name

POST table

  • id
  • user_id REFERENCES User.id
  • content

PROFILE table

  • id
  • user_id REFERENCES User.id
  • profile_info
    **KEY POINT**: A `BELONGS_TO` relation says that a field in **this** model points to the primary key in **another** model; in this case, _the current model owns the linking field_.
    

KEY POINT: A HAS_ONE relation says that some other model has a linking field pointing to this model's primary key; in this case, the related model owns the linking field.

We can thik that a PARENT table will be the one that doesn't have a foreign key, and a CHILD table as the one who "depends" on the parent table, that is, it has a foreign key.

Given that, a CHILD BELONGS_TO a PARENT and a PARENT HAS_ONE CHILD.

Let's put these in context (numbered for reference)

// Post model relations
1.   'user'    => array(self::BELONGS_TO, 'User',    'user_id'),
// Post belongs_to a user, because it is a child table.

// Profile model relations
2.   'user'    => array(self::BELONGS_TO, 'User',    'user_id'),
// Profile belongs_to a user, because it is a child table.

// User model relations
3.   'posts'   => array(self::HAS_MANY,   'Post',    'user_id'),
// User has_many posts, because User is a parent table.
4.   'profile' => array(self::HAS_ONE,    'Profile', 'user_id'),
// User has_one profile, because User is a parent table.

Relations 1 has the linking field user_id in this model, pointing to the primary key of the related model User. Likewise with relation #2.

Relations 3 and 4 are essentially the same thing as each other: the linking field user_id is not in this model, but in the related model, and the primary key involved is in this model (User). The difference is that HAS_MANY returns an array of possibly multiple objects, while HAS_ONE returns a single object.

HAS_ONE is just a special case of HAS_MANY, and the circumstances where HAS_ONE makes sense are far more limited than HAS_MANY and BELONGS_TO.

Weird example

USER table
- id 
- name
- status_id         REFERENCES status.id

STATUS table
 - id
 - name

Here one would think in human terms that a USER HAS_ONE STATUS. But that doesn't work. As you see, the USER is in fact a "child" table of STATUS, because it is referencing it, it depends on it. So the relations would be:

// User model relations
1.   'status'    => array(self::BELONGS_TO, 'Status',    'status_id'),
// User belongs_to a status, because it is a child table.

// Status model relations
2.   'users'    => array(self::HAS_MANY, 'User',    'status_id'),
// Status has_many users, because it is a parent table.

Some points to remember:

  • When defining one of these relations, you don't ever name the primary key; Yii figures it out from the DB schema
  • BELONGS_TO is where this model owns the linking field
  • BELONGS_TO references the related model's primary key
  • HAS_ONE/HAS_MANY is where the related model owns the linking field
  • HAS_ONE/HAS_MANY reference this model's primary key
  • If you define a BELONGS_TO relation in one model, you probably want an associated HAS_MANY relation in its related mode
  • You probably don't really want HAS_ONE ever