Search code examples
symfony1doctrineleft-join

Doctrine : leftJoin on table generates unnecessary queries


Short description of my environment :

  • Symfony : 1.4.8
  • Doctrine : 1.2.3
  • Centos 5.5 (PHP 5.3.3)
  • MySQL 5.1.52

I've a new project built in Symfony and here is the project schema :

# Car
RjCar:
  actAs: { Timestampable: ~ }
  columns:
    id: { type: integer(4), unsigned: true, primary: true, autoincrement: true }
    year: { type: integer(2), unsigned: true, notnull: true }
    engine_mod: { type: string(1000) }
    exterior_mod: { type: string(1000) }
    suspension_mod: { type: string(1000) }
    audio_mod: { type: string(1000) }
    vote_pos: { type: integer(4), notnull: true, unsigned: true, default: 0 }
    vote_neg: { type: integer(4), notnull: true, unsigned: true, default: 0 }
    views: { type: integer(4), notnull: true, unsigned: true, default: 0 }
    # Foreign keys
    category_id: { type: integer(1), unsigned: true, notnull: true }
    category_check: { type: boolean, notnull: true, default: 0 }
    user_id: { type: integer(5) }
  relations:
    RjCategory: { onDelete: CASCADE, local: category_id, foreign: id, foreignAlias: RjCars }
    sfGuardUser: { onDelete: CASCADE, local: user_id, foreign: id, foreignAlias: RjCars }

# Category
RjCategory:
  columns:
    id: { type: integer(1), unsigned: true, primary: true, autoincrement: true }
    name: { type: string(255), notnull: true}

# I do not include the sfGuardUser schema, but it's the default one from the plugin

When I want to retrieve the last 10 cars with category name and username, I use the following code in the RjCarTable.class.php :

  $last_cars = $this->createQuery('car')
               ->leftJoin('car.sfGuardUser user')
               ->leftJoin('car.RjCategory categ')
               ->orderBy('car.created_at DESC')
               ->limit(10)
               ->execute();

   return $last_cars;

On my page everything looks fine, I've all my results, but in the debug bar, I see 22 queries (instead of 2 as it should be).

Here is the query output for the first one that is normal :

SELECT
/* Everything about the car */
r.id AS r__id, 
r.year AS r__year, 
r.engine_mod AS r__engine_mod, 
r.exterior_mod AS r__exterior_mod, 
r.suspension_mod AS r__suspension_mod, 
r.audio_mod AS r__audio_mod,  
r.vote_pos AS r__vote_pos, 
r.vote_neg AS r__vote_neg, 
r.views AS r__views, 
r.type_id AS r__type_id, 
r.category_id AS r__category_id, 
r.category_check AS r__category_check, 
r.user_id AS r__user_id, 
r.created_at AS r__created_at, 
r.updated_at AS r__updated_at, 
/* ... hidden because irrelevant... retrieve everything about the sfGuardUser and profile... */
/* Everything about the category */
r2.id AS r2__id, 
r2.name AS r2__name, 
FROM rj_car r 
LEFT JOIN sf_guard_user s ON r.user_id = s.id 
LEFT JOIN rj_category r2 ON r.category_id = r2.id 
ORDER BY r.created_at DESC 
LIMIT 10

So until then everything is normal, except this query is followed by 20 others to retrieve informations about each category (2 queries for each result apparently), while you can notice in the previous query these informations are available. I will not put all of them but here is some :

SELECT r.id AS r__id, r.name AS r__name FROM rj_category r WHERE (r.id = '8') LIMIT 1
SELECT r.id AS r__id, r.name AS r__name FROM rj_category r WHERE (r.id = '8') LIMIT 1
SELECT r.id AS r__id, r.name AS r__name FROM rj_category r WHERE (r.id = '9') LIMIT 1
SELECT r.id AS r__id, r.name AS r__name FROM rj_category r WHERE (r.id = '9') LIMIT 1
/* etc.. 20 times */

So my real questions are : - Why is it performing all these unecessary queries while the first query should have these information? - Why is this not happening for the sfGuardUser table? The relation between my RjCar object and sfGuardUser object is apparently the same as the one between RjCar and RjCategory.

If somebody already face the same problem I'll be really glad to ear about it. As I say everything is working fine, but I prefere this module not generate unecessary queries as it should perform on the homepage of my application.


Solution

  • I slapped myself :

    Here is what I found in the RjCar.class.php

    /**
    * Return the category of the car
    */    
    public function getRjCategory(){
      return Doctrine_Core::getTable('RjCategory')->find($this->getCategoryId());
    }
    

    So it explains the unecessary queries... I don't even remember writing this piece of code, but considering I'm the only one working on this project I guess it's me.

    As usual the problem was between the chair and the keyboard...

    Thanks for your help.