Love Fuel?    Donate

FuelPHP Forums

Ask your question about FuelPHP in the appropriate forum, or help others by answering their questions.
ManyMany with model in the middle (with attributes)
  • Hi,

    I have a many to many with an additional table that will contain additional data. This means that this is now a many2one with one2many relationship for the middle model.

    I have Agency and Users. And an additional model AgencyUsers which will hold the relation with an additional attribute `status` for example.

    Here is the code:

    https://gist.github.com/johim/9493b006d85dfe74de81

    So the question is how to use CRUD to save data to the Agency variable when I have all of the User objects. If the ->save method of the AgencyUsers is not called then the Agency->save will throw a frozen Object.


    Another issue is the complex key - it cannot allow primary keys from the foreign keys, and this is an issue as when I want to find the exact AgencyUsers the find method cannot be called (with array($agecy_id, $user_id)) but should be query()ed;

  • If you add a model in between instead of a through table, you need to use that as well:

    // assume an existing user and agency
    $user = Model_User::find(1);
    $agency = Model_Agency::find(1);

    // construct a new through object with a default status
    $user->agency_users = Model_AgencyUsers::forge(array('status' => 0));

    // assign the agency to the user
    $user->agency_users->agency[] = $agency;

    // save, this should cascade
    $user-save();

    Your Model_AgencyUsers is a separate model, so it requires a unique PK. user_id and agency_id are both FK's in this model, and may not be part of the PK.
  • I personally never use find(), but prefer query(), it's more descriptive of what the query does.

    But if you insist, you can use find_by_user_id_and_agency_id($userid, $agencyid)...
  • Tried it and actually it says:

    Fuel\Core\PhpErrorException [ Runtime Notice ]:
    Creating default object from empty value

    This is on line $user->agency_users = Model_AgencyUsers::forge(array('status' => 0));
  • Well, it was from the top of my head. ;-)

    You are sure $user is a valid Model_User object?
  • Hey, my bad didnt notice that it queries by ID=1 (for tests I usually just use 'first'). Both agency and user are now valid but sill get the error:

    Fuel\Core\FuelException [ Error ]:
    Assigned relationships must be an array or null, given relationship value for agency_users is invalid.

    My understanding before was that the array is the $user->agency_users, so to add new Agency, i'm still wondering how to do it with CRUD.


  • So for example if I do it this way:


    $user->agency_users[] = Model_AgencyUsers::forge(array('status' => 1, 'agency_id' => $agency->id, 'user_id' => $user->id));

    $user->save();

    It works. but this doesnt seem like a CRUD way.
  • What do you mean by CRUD way?

    CRUD is an abbreviation of a computer principle that applies to a single object (create, read, update, delete).

    If you want to apply that to a one-to-many relation, you'll need a form with two sections: an upper part containing the parent data, and below rows of child records. Fieldset has support for that via tabular forms, which works fine together with ORM relations.
  • What I ment is that its easier to write:

    $user = Model_User::find(1);

    $agency_user = Model_AgencyUsers:forge(array('status' => 1));
    $agency_user->agency = $agency;

    $user->agency_users[] = $agency_users;

    $user->save();

    I was referring to ORM way.


    Btw, may be I messed the relationships??
    Should it be:

    (one to many - many to one)
    Agency has many Agency_Users
    User has many Agency_Users
    Agency_Users belongs to Agency
    Agency_Users belongs to User

    or its the (many to one - one to many)
    Agency belongs to Agency_Users
    User belongs to Agency_Users
    Agency_Users has many Users
    Agency_Users has many Agencies
  • The "belongs_to" is always the side that has the foreign key.

    So, assuming the many-many between User and Agency, I would define:

    User many_many Agency ($user->agencies)
    Agency many_many User ($agency->users)

    User has_many Agency_Users ($user->agency_users)
    Agency_Users belongs_to User ($agency_users->user)

    Agency has_many Agency_Users ($agency->agency_users)
    Agency_Users belongs_to Agency ($agency_users->agency)

    Note that I tend to make the relation name plural for a "many" type of relation, and singular for a has_one or a belongs_to. Just a rule I have for our devs, it makes it instantly clear whether or not you need to use array notation.

    This will provide you with direct access from User to Agency and vice-versa, AND access to the join table if needed.

    It also means that you can use

    $user = Model_User::find(1);
    $user->agencies[] = Model_Agency::find(1);
    $user->save();

    To directly relate an agency with a user. Note that this only works if your join table has default values for all other columns in that table. If not, you'll have to resort to using the join table directly:

    $user = Model_User::find(1);
    $agency = Model_Agency::find(1);
    $agency_user = Model_Agency_Users::forge(array('user_id' => $user->id, 'agency_id' => $agency->id, 'status' => 1));
    $agency_user->save();

    No need to relate anything, you have a model for your join table, so you can create it directly. (obviously, in this example the find's aren't needed because you have the PK's, but it's an example).

    An alternative is to create the base join object, and relate it:

    $agency_user = Model_Agency_Users::forge(array('status' => 1));
    $agency_user->user = Model_User::find(1);
    $agency_user->agency = Model_Agency::find(1);
    $agency_user->save();

    So you see, you can have plenty of options you can play with. Which one is best, probably depends on the objects you have already loaded, or which PK's you already have, to minimize the number of queries you have to run.
  • Hey Harro,

    thats is exactly the way I'm doing it, but I was wondering if there are other more direct and straightforward ways.


    I thought that I might have messed the relations because of your example

    $user->agency_users->agency[] = $agency;

    in this it looks like the user has one agency_users and the other side has many agencies (hence the array at agency).
  • That example is indeed complete rubbish. ;-)

    The most straitforward is my first example in my previous post, with a many-many relation, which you can ignore the presence of your join table model until you actually need it.

    You can even define conditions on the relation if needed, so you could make a relation that only gives you agencies with a join status of 0, and a second relation with agencies with a join status of 1.

    You can make it as crazy as you want to... :-)
  • Ah, thats nice...can you give me an example? would be helpful :)
  • They are very sparsely documented here: http://docs.fuelphp.com/packages/orm/relations/intro.html, under configuration options.

    Basically, in the relation definition in your model, you can add a 'conditions' section, which is an array that takes "order_by" and "where" clauses, which are injected into the JOIN when you query the relation.

    I vaguely remember an issue with identifying the through table, but I can't find the thread that discussed it at the moment (problem is ORM aliasses all tables).

Howdy, Stranger!

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

In this Discussion