Feature Request #4445

ORM::with() doesn't prepend `WHERE` fields with alias

Added by Ivan Kurnosov 3 months ago. Updated 3 months ago.

Status:Feedback Start date:02/13/2012
Priority:Normal Due date:
Assignee:Isaiah DeRose-Wilson % Done:

0%

Category:-
Target version:Kohana v3.x - Unscheduled
Resolution: Points:1

Description

So it leads to the ambiguous references:

Database_Exception: SQLSTATE[42702]: Ambiguous column: 7 ERROR:  column reference "id" is ambiguous
LINE 1: ...N (type.pk = locationstub.location_type_id) WHERE id = 1 AND...
                                                             ^ [ SELECT type.pk AS type_pk, type.id AS type_id, type.name AS type_name, type.valid_from AS type_valid_from, type.valid_to AS type_valid_to, type.tx_from AS type_tx_from, type.tx_to AS type_tx_to, locationstub.* FROM location AS locationstub LEFT JOIN location_type AS type ON (type.pk = locationstub.location_type_id) WHERE id = 1 AND NOW() BETWEEN tx_from and tx_to AND NOW() BETWEEN valid_from and valid_to LIMIT 1 ]

History

Updated by Isaiah DeRose-Wilson 3 months ago

  • Status changed from New to Closed
  • Assignee set to Isaiah DeRose-Wilson
  • Resolution set to wontfix

This is expected. You need to include the table alias manually when it is required.

Updated by Isaiah DeRose-Wilson 3 months ago

  • Project changed from Kohana v3.x to ORM
  • Category deleted (Modules:ORM)

Updated by Ivan Kurnosov 3 months ago

Omg.

Why client should even care of that?! It is ORM business - to make the clients' life easier, by encapsulating from all db-related stuff.

And you propose to always keep in mind about weird `with()` implementation? Oh gosh

The fix took 3 (three) lines for me, don't be that lazy

Updated by Isaiah DeRose-Wilson 3 months ago

  • Status changed from Closed to Feedback
  • Resolution deleted (wontfix)

If you do where('some_field', '=', 100), how would ORM know which alias/table name to use? Please include your fix and if it can be fixed in three lines without creating more problems we will include it.

Updated by Ivan Kurnosov 3 months ago

If I do it in `where()` - obviously I apply the conditions to the main entity, that is used for `from`, because `with()` is optional and should never change the results.

        foreach ($this->_db_pending as $key => $val) {
            if ($val['name'] == 'where' && strpos($this->_db_pending[$key]['args'][0], '.') === false) {
                $this->_db_pending[$key]['args'][0] = $parent_path . '.' . $this->_db_pending[$key]['args'][0];
            }
        }

I've put them right before `$this->join(array($target->_table_name, $target_path), 'LEFT')->on($join_col1, '=', $join_col2);` in `with()`

Updated by Isaiah DeRose-Wilson 3 months ago

  • Status changed from Feedback to Rejected
  • Resolution set to wontfix

Ivan Kurnosov wrote:

If I do it in `where()` - obviously I apply the conditions to the main entity, that is used for `from`, because `with()` is optional and should never change the results.

[...]

I've put them right before `$this->join(array($target->_table_name, $target_path), 'LEFT')->on($join_col1, '=', $join_col2);` in `with()`

The problem with that solution is it makes assumptions that we don't want to make. There is no reason that where conditions couldn't be applied to the joined table. That seems like a lot to give up to save us from having to type $this->_object_name.

If you do ORM::factory('something', 1), ORM will automatically append the object_name to there WHERE id = 1 statement. For other queries you can easily do this yourself in your model.

Updated by Ivan Kurnosov 3 months ago

  • Status changed from Rejected to Feedback

There is no reason that where conditions couldn't be applied to the joined table.

The `with()` is used to have better performance (to load entities eager). Adding or removing `with()` should never change the behaviour. By definition.

So if you have had:

$result = $model->where('foo', '=', 'bar')->find();
$result->related->baz;

and you add `with()` just to improve performance:

$result = $model->where('foo', '=', 'bar')->with('related')->find();
$result->related->baz;

you should get the same results. This is how `with()` should work. But it doesn't now.

Updated by Ivan Kurnosov 3 months ago

There is no reason that where conditions couldn't be applied to the joined table.

This statement makes no sense, as long as `with()` doesn't change and shouldn't change the query logic

Updated by Isaiah DeRose-Wilson 3 months ago

  • Resolution deleted (wontfix)

Ivan Kurnosov wrote:

There is no reason that where conditions couldn't be applied to the joined table.

This statement makes no sense, as long as `with()` doesn't change and shouldn't change the query logic

You are forgetting that with() can be used for find_all queries too. Consider the following:

$no_avatars = ORM::factory('user')
->with('profile')
->where('profile.avatar', 'IS', NULL) // Where condition on joined table
->find_all();

This is a very common way to use the with() method. On reviewing your suggest patch again I see you are checking for a '.', so the above example would still work with your patch. However, I still think it's better to be explicit about what you want instead of making ORM assume that you are referring to the main object when you don't specify.

I will leave this open and get feedback from some of the other devs. I'm not totally against this, but I don't like adding additional "magic" to ORM. Writing more verbose code isn't a bad thing.

Updated by Isaiah DeRose-Wilson 3 months ago

  • Target version changed from v3.2.1 to Unscheduled

Updated by Isaiah DeRose-Wilson 3 months ago

  • Tracker changed from Bug Report to Feature Request

Updated by Ivan Kurnosov 3 months ago

I'm not totally against this, but I don't like adding additional "magic" to ORM. Writing more verbose code isn't a bad thing.

It is not verbose thing, it is killing the idea of ORM. ORM should hide all the db related stuff.

What `with()` does? It just asks ORM layer to load the data in eager manner. That's it. And the code that asks that should not even have an idea how it is implemented - with join or some other magic.

The explicit alias specification requires from us to know how exactly `with()` works and always keep in mind the fact that it performs join with special aliases.

Just look at doctrine or hibernate - they also support eager loading, but eagerly-loaded entities doesn't require to specify any more details, just because ORM already has enough information to resolve anything it needs.

Updated by Ivan Kurnosov 3 months ago

$no_avatars = ORM::factory('user')
->with('profile')
->where('profile.avatar', 'IS', NULL) // Where condition on joined table
->find_all();

If you want to join - perform `join()`. `with()` hasn't been developed to replace join, it was developed to have the way to fetch 1:M in one query, and fetching related entities barely needs any additional filters (at least I cannot think of any valid example that makes any sense for me and could be used in real life scenario)

Also available in: Atom PDF