Love Fuel?    Donate

FuelPHP Forums

Ask your question about FuelPHP in the appropriate forum, or help others by answering their questions.
How ORM Model generate Array or KeyValuePair Array for has_many?
  • What determines if the ORM Model will be key value pair array or simply for the has_many relation?

    For example, I have the following for tables
    Weapon table:
    Weapon's primary key is weapon_id
    with column : category_id

    Appearance table:
    no primary key
    composite key: weapon_id, evolve_order
    foreign key: weapon_id to Weapon table's weapon_id

    Combo table:
    no primary key
    composite key: category_id, combo_order
    foreign key: category_id to Weapon table's category_id
    So I wrote following:
    protected static $_has_many = array(
            'appearance'  => array(
                'key_from' => 'weapon_id',
                'model_to' => 'Appearance',
                'key_to' => 'weapon_id',
                'cascade_save' => false,
                'cascade_delete' => false,
            ),
            'combo'  => array(
                'key_from' => 'category_id',
                'model_to' => 'Combo',
                'key_to' => 'category_id',
                'cascade_save' => false,
                'cascade_delete' => false,
            ),
        );
    $weapons = \Model\M\Close\Weapon::find('all', array('related' => array(
                                                                   'appearance', 
                                                                   'combo',
               )));
    $weapons = \Fuel\Core\Format::forge($weapons)->to_array();
    Weapon ORM object returns both successfully.
    Appearance is simply a list of appearance object, 
    but combo became Key Value Pair array of combo object with key as combo_order.
    So something like this:
    'Weapon' => {	'appearance' => [ {'a'=>'b'} ],
    	'combo' => [ 'evolve_order' => {'a'=>'b'} ],
    }
  • All ORM models MUST have a primary key. It doesn't work without. If your database design doesn't call for one, use an "id" with auto-increment. And primary keys must be unique. A composite key is a primary key, but one with multiple columns. ORM supports that without problems.

    Relations are mapped on primary key -> foreign key, so no primary key equals no relation possible.

    So for these tables:

    Weapons -> Appearance: not possible, no primary key in Appearance
    Weapons -> Combo: not possible, no primary key in Combo

  • Sorry, I don't really understand.
    You are saying composite key is a primary and ORM supports it.
    I guess you mean my Appearance and Combo ORM model doesn't have primary key so is not possible?

    My Appearance and Combo table have the following composite keys:

    In MySQL:
    Appearance table: composite key: weapon_id, evolve_order
    Combo table: composite key: category_id, combo_order

    In Appearance.php model:

    protected static $_primary_key = array('evolve_order');

    In Combo.php model:

    protected static $_primary_key = array('combo_order');

    I got both appearance and combo objects via relation from Weapon ORM successfully.
    combo object returns as array of key value pair (combo_order as key), but appearance objects return simply as an array of object, 

    I compared the create table SQL script from both Appearance and Combo table, I figured out they
    both using same SQL, the only difference, is the data.

    FYI, both evolve_order and combo_order are Not Null and INT(11).
    Combo table contains a row:
    category_id=cat_id, combo_order=1

    Appearance table contains a row:
    weapon_id=wea_id, evolve_order=0

    After I changed evolve_order=1, all of sudden FuelPHP magically returns the the Appearance object via relation with Key from Weapon ORM....

    Is that a bug?
  • I guess I know what is wrong...
    Properly when FuelPHP create the relation key value didn't convert the key value into string, 
    so PHP taking the 0 integer as index of array, that's why is showing:

    'Weapon' => 
    {
    'appearance' => [ 
    {'a'=>'b'} ,
    {'a'=>'c'} ,
    ],
    'combo' => [ 
    '1' => {'a'=>'b'},
    '2' => {'a'=>'c'},
    ],
    }
    which supppose to be: 
    'Weapon' => 
    {
    'appearance' => [ 
    '0' => {'a'=>'b'} ,
    '1' => {'a'=>'c'} ,
    ],
    'combo' => [ 
    '1' => {'a'=>'b'},
    '2' => {'a'=>'c'},
    ],
    }

  • Yes, ORM supports a composite primary key without problems, but your tables don't have one.

    A relation is defined by primary key => foreign key. If primary key is a composite key, ALL fields of that key should be present in the other table as part of the foreign key, which is not the case in your design.

    Since weapon_id in Weapons is the primary key, and therefore unique, you should be able to create a has_many between Weapons and Appearance on weapon_id. So that part should work fine.

    category_id is not a unique primary key in either table, so it's not possible to define a relation on that. You would probably get something that makes a bit of sense if you have a single Weapons record and use a has_many, but if you fetch the relation with multiple weapons records you create a mess...

    Again, relations should ALWAYS be defined on primary key (or unique key) => foreign key.

    If both keys in the relation are not unique, you have a many-many relation, and you need a junction table with both keys.
  • I am not so sure what do you mean by 'if you fetch the relation with multiple weapons records you create a mess', do you mind to give me an example?

    Yes.
    Weapon-Appearance is one to many relationship.
    Weapon-Combo is Many to Many relationship via category_id.

    And actually there also is a Category table which contains category_id as primary key.
    Weapon table's category_id have a foreign key to Category table's category_id.
    Just Combo table's category_id doesn't have foreign key to Category table, instead is to weapon table...

    So the Many-Many is constructed by the follows:
    N-1 relationship on Weapon-Category
    1-N relationship on Category-Combo

    I do feel Combo table should have foreign key to Category instead of Weapon....
    So as the ORM...
    $weapon->category->combo[0]
    $combo->category->weapon[0]
  • HarroHarro
    Accepted Answer
    Ah, now you have an entirely different situation. Before you described a direct relation between weapon and combo.

    The rules for ORM relations are very simple. Every relation is constructed using primary key -> foreign key. A key may be a single column or a compound key, but if it is a compound key, it has to be the entire key. And a primary key always have to be unique.

    A many-many relation is constructed via two has_many relations via a junction or relation table. In this case, the two tables in the relation both contain the primary key, and the relation table (through table in config terms) contains the foreign key of both tables.

    From this follows that you don't have a many-many between weapon and combo, you have two has_many relations, each on category id. Which is not a problem for ORM, but you need to define it as such.

    So your final observation is correct.
  • Sorry for confusing you.
    But what I had was really a direct relation between Weapon and Combo.
    Weapon N-N Combo
    Weapon had a $_has_many to Combo, Combo had a $_has_many to Weapon.

    Weapon:
    protected static $_primary_key = array('weapon_id');
    protected static $_has_many = array(
            'combo'  => array(
                'key_from' => 'category_id',
                'model_to' => 'Combo',
                'key_to' => 'category_id',
                'cascade_save' => false,
                'cascade_delete' => false,
            ),
        );
    protected static $_has_one = array(
            'category'  => array(
                'key_from' => 'category_id',
                'model_to' => 'Category',
                'key_to' => 'category_id',
                'cascade_save' => false,
                'cascade_delete' => false,
            ),
        );

    Combo:
    protected static $_primary_key = array('combo_order');
    protected static $_has_many = array(
            'weapon'  => array(
                'key_from' => 'category_id',
                'model_to' => 'Weapon',
                'key_to' => 'category_id',
                'cascade_save' => false,
                'cascade_delete' => false,
            ),
        );

    After reading what you've said, I felt I was wrong lol
    So I just changed both Weapon and Combo to $_has_one to Category ORM.
    Weapon N-1 Category
    Combo N-1 Category

    Then Category ORM have two $_has_many relation, one to Weapon and one to Combo.
    I tried and its works.

    Anyway, thanks @Harro!

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

In this Discussion