views:

107

answers:

2

I have the problem with the sequence of joins. The similar problem was in another question http://stackoverflow.com/questions/3343348/manipulating-order-of-joins-in-cakephp. The answer was to use Containable behavior. In my case that is unacceptable because I have deeper associations and containable generates too many queries. Containable does not generate joins for the three level associations. It generates additional queries for every entry from the second level table.

My query is:

$this->LevelOne->find('all', array(
    'joins' => array(array(
         'table' => 'level_three',
         'alias' => 'LevelThree',
         'type' => 'LEFT',
         'conditions' => array(
              'LevelThree.id = LevelTwo.level_three_field_id'
          )
     ))
));

The problem here is that cake generates several joins but the join of the LevelThree table is done before the joins of the LevelTwo tables and that throws an SQL error "Unknown column 'LevelTwo.level_three_field_id' in 'on clause'". If the LevelThree join would be at the end of the query after all LevelTwo joins the query would be okay.

So, the question is how to change the sequence of joins?

A: 

Have you thought about creating a HABTM model and inserting your own 'finderQuery' to override the model query? http://book.cakephp.org/view/1044/hasAndBelongsToMany-HABTM

Sometimes with complex queries, it make more sense to create the custom query so cake doesn't have to deal with it. To do that, you can just create the function in the model and then call it like you would any other query.

function customJoin(){
    return $this->query('CUSTOM QUERY HERE');
}

Then call it from the controller:

$this->ModelName->customJoin();

I think sometimes we rely too heavily on the automation that a framework provides and forget that WE are in control and can implement code outside of the core. I do it all the time. Hope this helps.

cdburgess
There is no HABTM relation between tables related to the problem. I have not really considered `finderQuery` because of lack of the documentation. But I guess that should contain a raw query. Am I right? I would like to avoid using $this->query() in my application in order to keep the data abstraction in place and to keep the application portable between databases. The last solution I would use is unbinding and binding models if there is no way to change the sequence of joins.
bancer
cdburgess is basically saying to wrap your $this->query(...) inside a model method (since I don't think you can use finderQuery w/o HABTM relationships). I don't see any issues with writing your own custom queries, as long as they reside inside the models. They will remain portable between databases, because it's still wrapped in the PDO layer. The data abstraction is still in place -- you're just extending it per your application requirements. I consider this eminently acceptable.
Travis Leleu
Actually, there is a relationship between the tables or you would not be able to join them. You may be correct in that they may not be HABTM, however, the finderQuery option will override the automagical query and basically provide a custom HABTM model based on the query you provide. I know I may get some flames for this, but think of it as a sql view. You are trying to merge data from multiple tables into one output. (Basically a view). You can achieve that in one of three ways: - write a view with a model- create a HABTM with a finderQuery custom query- or a custom function in the model
cdburgess
@Travis Leleu: Do you mean that if I write a query with $this->query(...) for MySQL that is not compatible with PostgreSQL cake will make that query compartible?@cdburgess: Could you point me to some good examples of finderQuery? You are right - there are relationships between tables. But I was surprised that 'joins' created the join before other joins. I was expecting it to be at the very end of the query.
bancer
bancer, you can refer to the HABTM link I provided in the answer, but basically this is what the site says:finderQuery, deleteQuery, insertQuery: A complete SQL query CakePHP can use to fetch, delete, or create new associated model records. This should be used in situations that require very custom results. You would put that in the LevelOne model and have it refer to the LevelThree model in order to work properly. You may be better off writting this as a view in the database and then just creating a model for that view.
cdburgess
I am under the impression that as long as you stick with SQL syntax that both databases support, your $this->query(...) query will run through PDO which will make it compatible with other databases
Travis Leleu
According to the cookbook http://book.cakephp.org/view/1027/query "Cake does not provide any data abstraction when running queries manually". There are more than two database types. If SQL syntax is good for both it may not be good for the third database.
bancer
+1  A: 

Finally I figured out how to do that:

$this->LevelOne->unbindModel(array('belongsTo' => array('LevelTwo')));
$this->LevelOne->find('all', array(
    'joins' => array(
          array(
             'table' => 'level_two',
             'alias' => 'LevelTwo',
             'type' => 'LEFT',
             'conditions' => array(
                  'LevelTwo.id = LevelOne.level_two_field_id'
              )
          ),
          array(
             'table' => 'level_three',
             'alias' => 'LevelThree',
             'type' => 'LEFT',
             'conditions' => array(
                  'LevelThree.id = LevelTwo.level_three_field_id'
              )
          )
     )
));
bancer
Also, there is a behavior that replaces Containable using joins instead.http://github.com/Terr/linkable
Abba Bryant