wilsmex
wilsmex

Reputation: 143

Ignore Callbacks (beforeFind, beforeSave etc) in CakePHP 3.x

In CakePHP 2.x we have the find('all','callbacks'=>false) What is an equivalent alternative in CakePHP3?

I have a situation where in my beforeFind callback (in my model's behavior) I'm appending a site_id to every query (for a multi-tenant app).

90% of the time I want this query appended via beforeFind, but %10 of the time I want my finds to ignore the callbacks.

I've looked at: Cakephp 3: How to ignore beforefind for specific queries? which comes close, but applying that method won't 'chain' the ignored beforeFind() callback on associated models, which I need it to do.

Updated code:

I've got two tables 'sites' and 'details' sites hasOne details, details belongs to sites. Inner Join.

In my AppController's initialize() function I've got

$tbl = TableRegistry::get( 'Sites' );
    $options = [
        'conditions' =>
            ['Sites.domain' => 'three.dev.mac',
            'Sites.is_current' =>  1,
            'Sites.is_archive' =>  0],
        'contain' => 'Details', // this contain causes the problem
        'ignoreCallbacks' => true
    ];
    $tenant_details = $tbl->find('all',$options)->first();

My models beforeFind() behavior callback

    public function beforeFind( Event $event, Query $query, ArrayObject $options ) {    
        debug($options);
        if(isset($options['ignoreCallbacks']) && $options['ignoreCallbacks'] === true) {
            // don't filter where clause
        }
        else{
            // filter where clause by default
            $query->where( [$this->_table->alias().'.'.'site_id'=> 7 ]);
        }
    return $query;
}

If I comment out the 'contain' line of my find call, the where query get's ignored as it should and my debug call returns 'ignoreCallbacks' => true' which is great.

If I leave the 'contain' line in the find() call (which is what I want) I get 2 debug outputs from beforeFind(), the first has 'ignoreCallbacks' => true', the second is empty. Apparently the second overrides the first and the query tries to append a site_id, which I don't want.

Any thoughts?

Upvotes: 1

Views: 2671

Answers (2)

wilsmex
wilsmex

Reputation: 143

I've found a way to (although it seems ugly) to have the custom $options that are passed (as @burzum mentioned in his answer) in a find call to the associated table's beforeFind() method if using 'contain' in the find. Hope this helps someone who was experiencing the same issue.

    $tbl = TableRegistry::get( 'Sites' );
    $options = [
        'conditions' =>
            $conditions,
        'contain' => [
            'Details'=> function ($q) {
           return $q->applyOptions(['ignoreCallbacks' => true]); // IMPORTANT this is required to send the 'ignoreCallbacks' option to the contained table.
        }
    ],
        'ignoreCallbacks' => true
    ];

    $tenant_details_query = $tbl->find('all',$options)->first();

Upvotes: 0

floriank
floriank

Reputation: 25698

http://book.cakephp.org/3.0/en/orm/retrieving-data-and-resultsets.html

Any options that are not in this list will be passed to beforeFind listeners where they can be used to modify the query object. You can use the getOptions() method on a query object to retrieve the options used.

So just pass a custom option to your queries find() call as 2nd arg and read that option in the beforeFind() like described above.

if (isset($options['useSiteId']) && $options['useSiteId'] === true) { /*...*/ }

Upvotes: 1

Related Questions