Trying to get table alias regularly crashes

Hi,

I’m working a lot with (default) scopes. Often when I try to get the table alias, it crashes - telling me, that the allowed memory size is exhausted. E.g.:


Fatal error: Allowed memory size of 33554432 bytes exhausted (tried to allocate 20 bytes) in /.../yii/framework/db/ar/CActiveRecord.php  on line 1206

This is an example query in the controller:


Product::model()->with('prices')->findByPk(4);

This is my Product.php code:




...

public function defaultScope()

{

    return array(

        'condition' => sprintf('`%s`.`is_active` = 1', $this->getTableAlias()),

    );

}


public function relations()

{

    'prices' => array(self::HAS_MANY, 'Price', 'product_id'),

}



This is my Prices.php code:




...

public function defaultScope()

{

    return array(

        'condition' => sprintf('`%s`.`is_active` = 1', $this->getTableAlias()),

    );

}



So the SQL query should look sth like this (pseudo code)


SELECT a.*, b.* FROM Product a, JOIN Price b WHERE a.id=4 AND a.active=1 AND b.active=1

Any suggestions? Did I miss sth?

Update: Seems that this method already causes the memory overflow:


$this->getDbCriteria(false)

I set the memory usage up to 1GB (in php.ini), but same effect: Memory usage is growing until 1GB, then the process get interrupted. Looks like an endless loop.

OK, got it.

The method getTableAlias calls getDbCriteria that calls defaultScope, which is calling getDbCriteria.

So an endless loop :(

But what is the right approach to get the upper mentioned request done?

We have:

  • 2 tables,

  • 2 models,

  • 2 same called columns,

  • 2 defaultScopes.

The only solution I have in mind is to ensure that every column of my 92 tables has a unique name. But that’s not the way I want to spend the next 2 weeks.

Seems that it’s a bug anyway. Or am I wrong?

Looks interesting. Could you create a minimalistic test case so we can reproduce it?

Thanks Samdark for your answer. I think we don’t need a test scenario here.

The only question is: How do I get the table alias for my defaultScope()?

The method $this->getTableAlias() causes an endless loop in "CActiveRecord"

Hmmm… yes, it seems it can’t be done with current code.

I’ve created new issue ticket for this case: http://code.google.com/p/yii/issues/detail?id=1183

Thanks for that.

I tried to find a work around: My idea was to override the default alias t whith the table name. So I created a parent model class which extends from CActiveRecord and contains only this method:




class ActiveRecord extends CActiveRecord

{

    public function defaultScope()

    {

        $criteria = new CDbCriteria;

        $criteria->alias = $this->tableName();

        return $criteria;

    }

}



Every model now extends from ActiveRecord. The defaultScope method is tweaked like this:


[...]

public function defaultScope()

{

    // Only return actire records

    $criteria = parent::defaultScope();

    $criteria->condition = sprintf('`%s`.`is_active` = :active', $criteria->alias);

    $criteria->params = array(':active' => true);

    return $criteria;

}



This works - well - sometimes ;)

After an extatic change of my models (I thought I had the solution), I figured out another problem:


Fatal error: Cannot use object of type CDbCriteria as array in /.../yii/framework/db/ar/CActiveRecord.php  on line 1703

Is this another bug, or is a CDbCriteria object not allowed for scopes? Do you have an idea how to continue programming anyway?

Normally defaultScope returns an array that is later used to instantiate CDbCriteria.

In your case it’s probably better to use named scope instead of defaultScope:




public function active()

{

    $this->getDbCriteria()->mergeWith(array(

        'condition' => sprintf('`%s`.`is_active` = 1', $this->getTableAlias()),

    ));

    return $this;

}






Product::model()->active()->with('prices')->findByPk(4);



That’s my prior version. ;)

The user can activate or deactivate (is_active),

an email address only can be used if verified via activation link

and a foto can be banned by the support team.

So the defaultScope often looks like "where is_active=1 and is_banned=0 and is_verified=1".

So I don’t want to create such a long pipe (…->active()->notBanned()->verified()) in every controller action. The possibility is too high to forget one scope.

But - I found an easier solution/work around:


public function defaultScope()

{

    return array(

        'alias' => $this->tableName(),

        'condition' => sprintf('`%s`.`is_active` = 1', $this->tableName())

    );

}

Addition to my last post:

This doesn’t work when one tries to fetch a record by its primary key. My SQL log then reads like that:




SELECT * FROM `Product` `Product` WHERE (`Product`.`is_active` = 1)  AND (`t`.`id` = 23);



Unnecessary to say, that the table alias t breaks it all down :(

PS: Therefore I already opened a ticket

Ticket is closed, problem is solved. Thank you!