Solved thread

This post is marked as solved. If you think the information contained on this thread must be part of the official documentation, please contribute submitting a pull request to its repository.

Saving model with relations

Hi all,

I have found very buggy behaviour in saving a model with relations. Let's suppose that we have two classes:

<?php
class Album extends \Phalcon\Mvc\Model
{
    public $id;
    public $name;
}

class Song extends \Phalcon\Mvc\Model
{
    public $id;
    public $albumId;
    public $name;

    public function initialize()
    {
        $this->hasOne('albumId', '\Album', 'id', [
            'alias' => 'album'
        ]);
    }
}

Each song can be related to some album. And we have following DB data:

|--------------|
| id | name    |
|--------------|
| 1  | Album 1 |
| 2  | Album 2 |
|--------------|

|-----------------------|
| id | albumId | name   |
|-----------------------|
| 1  | 1       | Song 1 |
|-----------------------|

Now let's move song ID#1 from Album 1 to Album 2

$song = \Song::findFirst(1);
$song->albumId = 2;
$song->save();

Let's check the database:

|--------------|
| id | name    |
|--------------|
| 1  | Album 1 |
| 2  | Album 2 |
|--------------|

|-----------------------|
| id | albumId | name   |
|-----------------------|
| 1  | 2       | Song 1 |
|-----------------------|

Looks fine till now. But let's change the code a little (don't forget to move song record back to album 1). Let's decide that we want to print song's album name before we change it.

$song = \Song::findFirst(1);
echo $song->album->name;
$song->albumId = 2;
$song->save();

Now let's check the database:

|--------------|
| id | name    |
|--------------|
| 1  | Album 1 |
| 2  | Album 1 |
|--------------|

|-----------------------|
| id | albumId | name   |
|-----------------------|
| 1  | 2       | Song 1 |
|-----------------------|

What??? Album 2 now called Album 1?

Actually that is very simple example. In that case album model have only one field besides id, but in real case all fileds for changed record are replaced by values of previous record. In my opinion that is very unexpected behaviour and it should not work that way.

Tested on: PHP Version 5.6.21; Phalcon Version 3.0.0;



99.6k
Accepted
answer
edited Sep '16

You just have wrong relation. hasOne is for actually accessing SONG from album. I mean this is bad example. You just need to change:

$this->hasOne('albumId', '\Album', 'id', [
            'alias' => 'album'
        ]);

to:

$this->belongsTo('albumId', '\Album', 'id', [
            'alias' => 'album'
        ]);

There you go with good example for what is hasOne:

class User extends Model
{
    public $id;
    public $login;
    public $profileId;

    public function initialize(
    {
        $this->belongsTo('profileId', 'Profile', 'id', ['alias' => 'profile']);
    }
}

class Profile extends Model
{
    public $id;
    public $firstName;
    public $lastName;

    public function initialize()
    {
         $this->hasOne('id', 'User', 'profileId', ['alias' => 'user']);
    }
}


449

Now I got reverse problem.

According to your suggestion I've changes Song class:

class Song extends \BaseModel
{
    public $id;
    public $albumId;
    public $name;

    public function initialize()
    {
        $this->belongsTo('albumId', '\Album', 'id', [
            'alias' => 'album'
        ]);
    }
}

Now when I try to change album for specific song:

$song = \Song::findFirst(1);
echo $song->album->name;
$song->albumId = 2;
$song->save();

It doesn't change it! It only works if I don't access relative model:

$song = \Song::findFirst(1);
$song->albumId = 2;
$song->save();

How to solve that?

I think this is a known bug. You would need to set $song->album to new album i think.