DMA's avatar
Level 2

Prevent dynamic accessors from being applied

I have a User model and a Company modal. In the Company modal, I have a few dynamic accessors which I've set up as little helpers to make reporting a little easier.

One example is:


//Company.php 

protected $appends = ['total_users'];

/**
* Dynamic accessor to provide the total number of users an entity has.
*
* @return bool
*/
public function getTotalUsersAttribute()
{
    return $this->totalUsers();
}

// Empty the dynamic accessors appends property
public function disableDynamicAccessors()
{
    $this->appends = [];
}

This appends a total_users attribute to the Company model. This all works very well, and i've no concerns about this.

What I would like to do however, is to prevent the accessors from being parsed, in certain situations. The reason for this is, if I want to return a list of 30 users - there are an unnecessary number of queries being run on the Companies which the User collection isn't really concerned with.

Something like this:

User::with('company')->disableDynamicAccessors()->all()

Where the disableAccessors method is being run on the Company model to prevent the 'appends' attribute being populated. (See above code)

However, this doesn't seem to work as it's trying to call the disableDynamicAccessors method on the Query Builder instead of the model.

Any ideas?

0 likes
1 reply
JarekTkaczyk's avatar

@DMA appends is called when you return the model using toArray / toJson, so it doesn't make sense to use it during the query. Also, this very accessor you have might be improved - I suppose it is a count of related hasMany models, so read this http://softonsofa.com/tweaking-eloquent-relations-how-to-get-hasmany-relation-count-efficiently/ .

Now, here's the answer to your question no matter if you improve the accessors or not - use custom collection:

// Company model
use Your\Collections\Users;
...
public function newCollection(array $models = array())
{
    return new Users($models);
}

// Users collection extending Eloquent\Collection
public function setAppends(array $appends)
{
    $this->each(function ($user) use ($appends) {
        $user->setAppends($appends);
    });
}

public function disableDynamicAccessors()
{
    $this->setAppends([]);
}

Then just

$users = User::with('company')->get();
$users->disableDynamicAccessors();

Please or to participate in this conversation.