3

I'm new to cakePHP and I have a Problem with Relations.

My Model Looks like this:

<?
class Operator extends AppModel
{
    var $name = 'Operator';
    var $hasOne = array('Contact','Adress','Land');
}
?>

and my Table Looks like this:

CREATE TABLE `operators` (
  `id` varchar(45) NOT NULL,
  `name` int(11) NOT NULL,
  `adress_id` int(11) NOT NULL,
  `land_id` int(11) NOT NULL,
  `contact_id` int(11) NOT NULL,
  `operator_category_id` int(11) NOT NULL,
  `legal_form` varchar(45) DEFAULT NULL,
  `affidavit` varchar(45) DEFAULT NULL,
  `comment` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `id_UNIQUE` (`id`),
  KEY `operator_category_fk_idx` (`operator_category_id`),
  KEY `contact_id_idx` (`contact_id`),
  KEY `adress_id_idx` (`adress_id`),
  KEY `land_id_idx` (`land_id`),
  CONSTRAINT `adress_id` FOREIGN KEY (`adress_id`) REFERENCES `adresses` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
  CONSTRAINT `contact_id` FOREIGN KEY (`contact_id`) REFERENCES `contacts` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
  CONSTRAINT `land_id` FOREIGN KEY (`land_id`) REFERENCES `lands` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
  CONSTRAINT `operator_category_fk` FOREIGN KEY (`operator_category_id`) REFERENCES `operator_category` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
)

but cakePHP doesn't Show any data from e.g. contacts enter image description here

what am I doing wrong?

5
  • hasOne is a rarely used association. In this case, you want use belongsTo. Commented Aug 12, 2015 at 10:58
  • Are you sure, your contacts table have any records? Commented Aug 12, 2015 at 11:42
  • @AgRizzo "rarely" is a little exaggerated - less often, perhaps. Commented Aug 12, 2015 at 13:09
  • why belongsTo in Operator? Doesn't a Contact belong to a Operator? Commented Aug 12, 2015 at 13:44
  • A model X belongsTo another Y if it has a foreign key y_id pointing to Y.id. Since Operator has a key contact_id pointing to Contact.id, Operator belongsTo Contact. Contact does not have a operator_id key (I assumed so), so Contact does not belongsTo Operator, but Contact may hasOne or hasMany Operator. Commented Aug 12, 2015 at 13:48

3 Answers 3

6

hasOne relationship only serves if you have 1 to 1 association to be able to access the associated model within the model which has no foreign key.

belongsTo

In your case, you want to use belongsTo, belongsTo is used as soon as you have a foreign key in your model pointing to another model, your Operator model has foreign keys pointing to Contact, Address, Land which are resp. contact_id, address_id and land_id.

Since the contact_id field is in the Operator model, then the Operator model belongsTo the Contact model.

class Operator extends AppModel {
    public $belongsTo = array('Contact') ;
}

When fetching an Operator entry, you will get something like:

Array(
    [Operator] => Array
        (
            [id] => 42,
            [name] => 'operator',
            [contact_id] => 33
        )
    [Contact] => Array
        (
            [id] => 33,
            [name] => 'contact'
        )
)

hasOne and hasMany

Since your Operator belongsTo Contact, your Contact hasOne OR hasMany Operator. Let's illustrate the difference between the two:

  1. Each of your Contact entry is associated to only one Operator, you could add a operator_id foreign key inside your Contact model but it would be redundant because you already have the association using contact_id in Operator. Thus, what you do is create a hasOne association inside the Contact model, so Contact hasOne Operator.
class Contact extends AppModel {
    public $hasOne = array('Operator') ;
}

Again, when fetching a Contact entry you will get:

Array(
    [Contact] => Array
        (
            [id] => 33,
            [name] => 'contact'
        )
    [Operator] => Array
        (
            [id] => 42,
            [name] => 'operator',
            [contact_id] => 33
        )
)

Note that this is the same as when fecthing an Operator with a belongsTo Contact. The difference between hasOne and belongsTo is mainly which model has a foreign key pointing to the other.

  1. Eah of your Contact entry is associated to multiple (many) Operator, in this case your Contact hasMany Operator.
class Contact extends AppModel {
    public $hasMany = array('Operator') ;
}

Again, the output of $this->Contact->find():

Array(
    [Contact] => Array
        (
            [id] => 33,
            [name] => 'contact'
        )
    [Operator] => Array
        (
            [0] => Array
                (
                    [id] => 42,
                    [name] => 'operator 42',
                    [contact_id] => 33
                )
            [0] => Array
                (
                    [id] => 47,
                    [name] => 'operator 47',
                    [contact_id] => 33
                )
        )
)

hasAndBelongsToMany

Now, assume one Operator can have multiple Contact, and one Contact can serve multiple Operator. In this case, you need an extra table (which should be called operators_contacts if you follow CakePHP naming convention), with two fields operator_id and contact_id:

class Contact extends AppModel {
    public $hasAndBelongsToMany = array('Operator') ;
}
class Operator extends AppModel {
    public $hasAndBelongsToMany = array('Contact') ;
}

The output of $this->Contact->find('all') will be similar to the one using the hasMany relationship, main difference would be that their is no operator_id or contact_id field in the model.


Full example

Let's assume I have the following models: Company, Employee, Address, and the following constraints:

  • A Company has various Employee but only one CEO which is part of its Employee
  • A Company has one Address, and there is at most one company at each Address

You have the following tables:

CREATE TABLE companies (
    id INTEGER AUTO_INCREMENT PRIMARY KEY,
    ceo_id INTEGER REFERENCES employees (id)
) ;

CREATE TABLE employees (
    id INTEGER AUTO_INCREMENT PRIMARY KEY,
    company_id INTEGER REFERENCES companies (id)
) ;

CREATE TABLE addresses (
    id INTEGER AUTO_INCREMENT PRIMARY KEY,
    company_id INTEGER REFERENCES companies (id)
) ;

Note that in this case, you cannot the table with all the constraints because you have a loop between companies and employees, you need to add the constraints after.

And the following CakePHP models:

class Company extends AppModel {

    /* A Company has many Employee. */
    public $hasMany = array('Employee') ;

    /* A Company has one CEO, but the key is in the Company model, 
       so it is a belongsTo relationship. */
    public $belongsTo = array(
        'CEO' => array(
            'className' => 'Employee', // It's an Employee
            'foreignKey' => 'ceo_id'
        )
    ) ;

    /* A Company has one address. */
    public $hasOne = array('Address') ; 

} ;
class Employee extends AppModel {

    /* An Employee belongs to a Company. */
    public $belongsTo = array('Company') ;

} ;
class Address extends AppModel {

    /* An address belongs to a Company. */
    public $belongsTo = array('Company') ;

} ;

Notice I have put a company_id foreign key in the Address model, so Address belongsTo Company and Company hasOne Address, I could have put a address_id inside the Company model and I would have had Company belongsTo Address and Address hasOne Company. I made an arbitrary choice here, in a real application you should think of which of the two above cases is the most meaningful.

Also note that the way I defined my model, there is nothing that prevent an Employee to be the CEO of a Company without being one of its own Employee.


You can find more information in the CakePHP 2.x Book.

Sign up to request clarification or add additional context in comments.

1 Comment

This is by far the best explanation I found in the web. Thank you.
4

you should you use $belongsTo instead of hasOne relationship for this purpose.

public $belongsTo = array(
        'Contact' => array(
            'className' => 'Contact',
            'foreignKey' => 'contact_id',   // check this
            'conditions' => '',
            'fields' => '',
            'order' => '',
            'counterCache' => true
        )
    );

1 Comment

Since OP followed CakePHP naming convention, you can simply do public $belongsTo = array('Contact') ;.
1

Operator belongsTo xyz, not hasOne

hasOne and belongsTo associations are similar, but not the same. To illustrate the difference consider the following schema:

users
    id (int)
    name (string)

addresses
    id (int)
    user_id (int)
    is_home (bool)
    ... other fields ...

With User and Address models. Let's assume that there is only one "home" address per user, but a user is able to create multiple address records.

belongsTo means the table has a foreign key field in it (addresses.user_id) has* associations mean the other table has the foreign key (there is no users.address_id, User does not belongTo Address).

In summary, these statements would describe the model associations:

  • User hasMany Addresses
  • User hasOne home Address
  • Address belongsTo User

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.