Love Fuel?    Donate

FuelPHP Forums

Ask your question about FuelPHP in the appropriate forum, or help others by answering their questions.
Access a related object with another field than id
  • Hi,

    I have a model with a has_many relation, and I wonder what's the best way to access a related object with a field.

    Lets say that I have a Model_House which contain many Model_House_Part objects (The relation is named 'part'). My Model_House_Part has a 'type' field which is unique for each set of object related to the same parent. The final purpose of this is to be able to call $my_house->parts->window or $my_house->parts['window'] in order to access the related object of whom the type is 'window'

    Of course the easy answer would be to go through the object relation after the query and set each related object in an array using custom properties. But do we have a cleaner solution to do it ? Also ORM objects are pretty heavy in RAM, I dont know if doing this would copy them or just pass them by reference but I want to avoid too much manipulation.

    Thanks
  • That doesn't work, as you access column names, not values. And since you have a has-many relation, you can't access a direct part this way as well.

    You are looking for $my_house->parts[#id]->type == 'window', which is not something you can access directly, you'll need a custom method and a foreach loop.

    What you can do is reverse the query:

    $part =
    Model_House_Part::query()
        ->where('part_id', '=', $partid)
        ->where('type', '=', 'window')
        ->related('part')
        ->get_one();

    but that is database intensive if you need a lot of parts.

    All ORM objects are cached and stored only once, and always passed by reference.
  • HarroHarro
    Accepted Answer
    You could use an EAV container for it, if you don't have fields in your parts table you want to query directly. An EAV container would allow you to use $my_house->window directly.
  • I never really understood EAV containers (But I not really tried aswell). Anyway, if ORM objects are passed by reference, if I write a custom method in my model which return the wanted part and then change that part, it will be saved when I save my parent model ? I'll go that way then.
  • HarroHarro
    Accepted Answer
    Yup, that's a perfectly acceptable solution.

    Just be careful when selecting subsets.

    Because of the caching done by the ORM, once you selected a subset of parts (for example by using a WHERE clause), you can not access all of them anymore, because the object and it's parts will be returned from cache (which is then incomplete).

    EAV is basically a has-many related table with two columns, 'key' and 'value', and the foreign key to the parent.

    Say you have "users -> properties". You could implement that as an EAV container, and have records like:
    [
        [ 'key' => 'fullname', 'value' => 'AdamW' ],
        [ 'key' => 'birthdate', 'value' => '01-01-1970' ],
    ]

    you can then access these values as $user->fullname, $user->birthdate, etc.

    It works fine with single values, it gets more complex if you want to store multiple values, like an object or an array. Then you have to serialize (which in itself is not a problem, the Typing observer can do that for you on the fly), and then you can't query individual values anymore.

    So you basically use EAV for single key:value combinations, where the keys aren't fixed (otherwise you could make them columns, which is a lot easier).

    See https://en.wikipedia.org/wiki/Entity–attribute–value_model for in-depth info.
  • Yeah I see, A little the same pattern as MongoDB but on two tables... Thanks for the explanation.

    Just a quick question related to your post : (I dont talk about EAV specifically) Why not auto serialize / unserialize arrays in ORM ?

  • You can do that, just use an array and use the Typing Observer to do the serialization (see for example \Auth\Model\Auth_User, it uses that for profile_fields).

    The advantage is that you keep all data in a single table, the downside is you can't query it anymore. So it depends on your use-case what is the best solution.
  • I know that you can do it that way, but I was asking why its not automatic. If you want to put an array in a field you have to serialize it anyway. Do you think it would be a good idea ? I'm ready to help  and send a pull request if so. So if you do :

    $something = Model_Something::forge(array(
    'my_field' => $my_array,
    ));

    If the ORM see that's the var is an array, serialize it automatically before save or update. To decode it when you get an ORM object, the pattern of a serialized object can be identified and unserialized automatically as well 
  • You don't want that, that is what the typing observer is for.

    If you go down this route, you'll have to implement automatic conversion for any possible data type, and not only arrays, after which it will become quite messy and complex very quickly.

    The problem is also that if you do this, you also have to do this the other way around, because the operation has to be symmetrical to make sense. And on load, the problem is even more complex, since all you receive from the database is a string, and no indication of what the target data type is supposed to be.

    You'll end up with a bloated and slower ORM, which is not a good idea.
  • Also, not every RDBMS has the same capabilities.

    So where one supports a given datatype, others don't, so you don't always want the ORM do an automatic conversion.

    Compare for example the list of supported datatypes between Postgres and Mysql (Postgres has a data type "array").
  • Yeah you are right, that's not such a good idea. 

Howdy, Stranger!

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

In this Discussion