We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

Storing related records: Object of class Artists could not be converted to string

Hello,

I'm trying to run the first example in the Storing related records documentation but all I get is an error message reading Object of class Artists could not be converted to string.

In case it could help, the following steps do produce the error:

First create mysql schema and tables:

CREATE SCHEMA TEST;
USE TEST;

CREATE TABLE Artists (
  id INT UNSIGNED NOT NULL AUTO_INCREMENT,
  name VARCHAR(45) NOT NULL,
  country VARCHAR(45) NULL,
  PRIMARY KEY (id)
);

CREATE TABLE Albums (
  id INT UNSIGNED NOT NULL AUTO_INCREMENT,
  name VARCHAR(45) NOT NULL,
  artist INT UNSIGNED NOT NULL,
  year YEAR NULL,
  PRIMARY KEY (id),
  CONSTRAINT fk_Albums_Artists
    FOREIGN KEY (artist)
    REFERENCES Artists (id)
);

Create a brand new project: $ phalcon project test and edit database details in /app/config.php.

Create /app/models/Artists.php:

class Artists extends \Phalcon\Mvc\Model
{
    public $id;
    public $name;
    public $country;

    public function initialize() {
        $this->hasMany('id', 'Albums', 'artist');
    }
}

Create /app/models/Albums.php:

class Albums extends \Phalcon\Mvc\Model
{
    public $id;
    public $name;
    public $artist;
    public $year;

    public function initialize() {
        $this->belongsTo('artist', 'Artists', 'id');
    }
}

Edit /controllers/IndexController.php with the code from the doc:

class IndexController extends ControllerBase
{

    public function indexAction()
    {
        // Create an artist
        $artist = new Artists();
        $artist->name = 'Shinichi Osawa';
        $artist->country = 'Japan';

        // Create an album
        $album = new Albums();
        $album->name = 'The One';
        $album->artist = $artist; //Assign the artist
        $album->year = 2008;

        //Save both records
        $album->save(); // this line triggers the error
    }

}

Run the page and get:

Catchable fatal error: Object of class Artists could not be converted to string

Thanks for any help.

I don't know if this is what's causing this particular error, but your Album/Artist relationship isn't set up properly. In the Artist class, this line:

$this->hasMany('id', 'Albums', 'artist_id');

Is referencing the artist_id property of Albums, but Albums doesn't have that property - it just has an artist property.



8.2k
edited May '14

Thank you for spotting that, quasipickle. You are totally right. However, I still get the same error :-(

I've edited the code in my first post.



8.2k

Nobody seems to have any idea about this. Should I consider it a bug and report it?



8.1k

$album->artist = $artist; //Assign the artist

your mistake here $artist is object. $album->artist is integer



8.2k
edited May '14

Hi Oleg. I understand that $album->artist is expecting an id, but if I read the documentation correctly, this integer should automagically be extracted from the $artist object when $album gets saved. So is it a Phalcon bug or an error in the doc (or just me missing something obvious)?

Thanks anyway for helping!



8.1k
edited May '14

I understood your mistake :)

  1. You have defined the wrong relations.

CREATE TABLE Albums (
  id INT UNSIGNED NOT NULL AUTO_INCREMENT,
  name VARCHAR(45) NOT NULL,
  artist INT UNSIGNED NOT NULL,
  year YEAR NULL,
  PRIMARY KEY (id),
  CONSTRAINT fk_Albums_Artists
    FOREIGN KEY (artist)
    REFERENCES Artists (id)
);

artist must have name artist_id, for example - it link to id of artist

  1. Then all code have sense.

class IndexController extends ControllerBase
{

    public function indexAction()
    {
        // Create an artist
        $artist = new Artists();
        $artist->name = 'Shinichi Osawa';
        $artist->country = 'Japan';

        // Create an album
        $album = new Albums();
        $album->name = 'The One';
        $album->artist = $artist; //Assign the artist
        $album->year = 2008;

        //Save both records
        $album->save(); // this line triggers the error
    }

}

You object Albums in this case have not artists property. But have artists_id for relations between Albums and Artists Then you create new (dynamic if you want named) property artists

$album->artist = $artist;

Before your Albums has structure like Albums-> ... any property fron DB. After this operand your Albums have structure Example.

$artist = new Artists();
        $artist->name = 'Shinichi Osawa';
        $artist->country = 'Japan';

        // Create an album
        $album = new Albums();
var_dump($album);
        $album->name = 'The One';
        $album->artist = $artist; //Assign the artist
        $album->year = 2008;

var_dump($album);

Before :


object(Albums)#2 (4) {
  ["id"]=>
  NULL
  ["name"]=>
  NULL
  ["artist_id"]=>
  NULL
  ["year"]=>
  NULL
}

After :


object(Albums)#2 (5) {
  ["id"]=>
  NULL
  ["name"]=>
  string(7) "The One"
  ["artist_id"]=>
  NULL
  ["year"]=>
  int(2008)
  ["artist"]=>
  object(Artists)#1 (3) {
    ["id"]=>
    NULL
    ["name"]=>
    string(14) "Shinichi Osawa"
    ["country"]=>
    string(5) "Japan"
  }
}

class Artists 
{
    public $id;
    public $name;
    public $country;

}

class Albums 
{
    public $id;
    public $name;
    public $artist_id;
    public $year;

}


8.2k

Oleg, what I understand you're telling me is that the code example in the doc is wrong and should be changed this way:

// Create an artist
$artist = new Artists();
$artist->name = 'Shinichi Osawa';
$artist->country = 'Japan';
$artist->save(); // Added so that we get an id for the artist

// Create an album
$album = new Albums();
$album->name = 'The One';
// $album->artist = $artist; // Wrong
$album->artist = $artist->id; // Right
$album->year = 2008;

//Save both records
$album->save();

Please note that I haven't renamed the artist property/field "artist_id", I'm perfectly conscious it is an id (integer) but for the sake of simplicity I'll stick to the naming used in the doc. If the naming is important and I have to use the form <table>_id, please tell me.

Of course this would most probably work, but:

  1. the code in the doc is radically different, it only does one save() operation to save both records, and it uses a whole object to link records (not just an id property)
  2. I don't see any magic here (see "Magic properties can be used to store a records and its related properties")

I'm not sure what to think...



8.1k
edited May '14

Code in the docs are right.

Authors of docs are suppose that user know database and relations.

Usualy you can create relations as Albums.artists_id -> Artists.id , where Albums.artists_id is INTEGER and Artists.id is INTEGER. When you need, you can tie in Albums and Artists as complex Object.

An exemplary sequence of operations :

  • you request album,

  • then request artist with id albums.artists_id equal

  • you insert object Artists to Album object as a new property artist ( $album->artist = $artist )

Then you get object Album, which have property artists as object. And you can get, for example :

album->artist->name

P.S. Magic property - is a big name, of course. In fact - this is a common operation in PHP.

Your main mystake is wrong relations between tables in dabase. Artist must have relation to Album via id, not object.



8.2k
edited May '14

Oleg, sorry but I am not convinced at all. Don't take it personally though, you've dedicated a good deal of your time answering my doubts and I thank you so much for that. But I still don't see why the doc authors would have created this section about something which is not in any way a Phalcon feature ; I can't think of a reason why they would mention a "magic" operation where there is nothing more than plain, dead common, variable assignments ; or why they speak about "Saving the album and the artist at the same time" if in reality you simply can't save them at the same time ; or why they would provide two code examples that just don't work...

If a phalcon dev stops by, I'd appreciate a confirmation of all of this, but meanwhile I'll go back to developping without taking this (hypothetic) feature into account. Thank you again for your time, Oleg!



8.1k
Accepted
answer
edited May '14

You should give your relationship an alias. I guess in your example, since no alias, this should work: $album->artists = $artist

I guess you would want to name that alias 'Artist' but then you have a collision with your column named artist... (case doesn't matter here).

To solve that you can:

  • rename the artist column (e.g. to artist_id);
  • use a column map to rename the column;
  • use another alias for the relationship ('AlbumArtist' perhaps? - there could be more kinds of Artists related to the Album of course..) (but it would still be a good idea to apply option 1 or 2 anyway, to prevent accidental collisioning and to elliminate (some) ambiguity).

Hope this helps (and I am not mistaken)? ;)



8.2k

Renskii, you are completely right!

I understand now that the Album model does NOT have any "artist" property and so $album->artist is a magic property, meaning that through PHP's magic methods, it gets to either a relationship with an "Artist" model or one aliased with this name.

Thanks, case closed!

you change artist column to artist_id like this :

CREATE TABLE Albums (
  id INT UNSIGNED NOT NULL AUTO_INCREMENT,
  name VARCHAR(45) NOT NULL,
  artist INT UNSIGNED NOT NULL,
  year YEAR NULL,
  PRIMARY KEY (id),
  CONSTRAINT fk_Albums_Artists
    FOREIGN KEY (artist_id)
    REFERENCES Artists (id)
);