mralston's avatar

setAppends() with eager loading & pagination

I have an attribute which is somewhat intensive to run. I want to append the attribute to the model only when needed, so I'd like to use the setAppends() method when I need it, rather than the $appends property which would append the attribute every time.

The context I'm doing it in is confusing me. The model with the attribute is being eager loaded on a relationship using the with() method and the whole lot is being paginated. Here's the code:

$appointments = Appointment::with([
    'customer',
    'customer.lead.source',
    'rep',
    'status',
    'quote',
    'quote.basket',
])->paginate();

The Quote model has an attribute called pricePerSquareMetre, so I'd like to use this:

->setAppends('price_per_square_metre')

I can't figure out where to put it. In the past when I've had a collection, I've iterated through the items in the collection using each() and used setAppends() in there, but I'm not sure how to iterate over the items in the paginated results. Also, I'm sure there's a better way.

I tried this, but the parameter passed to the closure is the relation method, which doesn't support setAppends().

'quote' => function ($q) {
    $q->setAppends('price_per_square_metre');
},

Any iteas?

0 likes
5 replies
rodrigo.pedra's avatar

iterating over items on a paginator:

$appointments = Appointment::with([
    'customer',
    'customer.lead.source',
    'rep',
    'status',
    'quote',
    'quote.basket',
])->paginate();

$appointments->getCollection()
    ->each
    ->setAppends('price_per_square_metre');
rodrigo.pedra's avatar
Level 56

You can also call ->through() but as it uses Collection@transform under the hood you need to remember to return the model:

$appointments = Appointment::with([
    'customer',
    'customer.lead.source',
    'rep',
    'status',
    'quote',
    'quote.basket',
])->paginate()->through(function (Appointment $appointment) {
    return $appointment->setAppends('price_per_square_metre');
});
1 like
mralston's avatar

Thanks @rodrigo.pedra, I really appreciate your help. Very quick reply and right on the money.

Both solutions work an absolute treat.

I had to make a minor tweak to each as the model with the attribute to be appended is a little deeper.

Here's the first solution:

$appointments->getCollection()
    ->each(function ($appointment) {
        $appointment->quote
            ?->setAppends(['price_per_square_metre']);
    });

I'm going to use the through() method as it feels cleaner:

$appointments = Appointment::with([
    'customer',
    'customer.lead',
    'customer.lead.source',
    'rep',
    'status',
    'quote',
    'quote.basket',
])->paginate()
    ->through(function (Appointment $appointment) {
        return tap($appointment, function ($appointment) {
            $appointment->quote
                ?->setAppends(['price_per_square_metre']);
        });
    });
1 like

Please or to participate in this conversation.