Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

0re1's avatar
Level 1

How to run Model methodes

Greetings,

I have a Invoice Model and it hasMany Items with Price attribute. I would like to sum all the Prices in Invoice. I have written public function on Invoice:

public function sum() {
    $sum=0;
    foreach($this->Items()->all() as $item) {
        $sum += $item->price;
    }
    return $sum;
}

I would like to call this method and pass the value to the view. Would you recommend to add it to the Invoice Model or it should reside somewhere else? Is it possible to work with $this like that or do I have to use some Mutators or Scopes and access Model instance by ID without the possibility to use $this.

Your help is much appreciated. Thanks :)

0 likes
27 replies
jakeryansmith's avatar

Laravel collections already have a sum method built in:

$this->Items->sum('price')
2 likes
sid405's avatar

@0re1 Hi there.

Assuming you have a Item relationship in your Invoice model you could do something like this:

public function scopeTotalAmount($query)
    {
        return $query->join('items', 'items.invoice_id', '=', 'invoices.id')
        ->selectRaw('invoices.*, sum(items.price) as total_amount');
    }

And then to call it do this:

return Invoice::whereId($invoice_id)->totalAmount()->get();

The returned element will have a total_amount field which is the total of the price of all the items associated with that invoice

Hope it helps

best,

sid

1 like
0re1's avatar
Level 1

@jakeryansmith : Thanks but I just tried to simplify the code a bit for the readers. I cannot use that method because I am using also currency exchange rates and the 'sum-counting' code is unfortunately not that simple...

jakeryansmith's avatar

Ok gotcha. Then yes, putting it in your invoice model is perfectly fine.

0re1's avatar
Level 1

@sid405 Thanks Sid, but ID passing was a thing I would like to avoid - it doesn't seem right to me to count the sum when I already have an instance of Invoice - to use Scopes...

jakeryansmith's avatar

I know a some people will say you should avoid putting too much business logic in your model, but I think it's a logical place for something like this.

1 like
0re1's avatar
Level 1

@jakeryansmith It may be fine to place the method in my model but when I try to call the method from a view or a controller it is not visible or I cannot simply call it on the object instance and I don't know why...

jakeryansmith's avatar

Are you getting an error when you try to call it, or is it just not returning any result? I know your code was written quick for an example but you do have a syntax error:

sum=0 should be $sum = 0;

0re1's avatar
Level 1

I am getting: Call to undefined method Illuminate\Database\Query\Builder::sum()

sid405's avatar

@0re1 Then ultimately if you strictly need to use it like that, just put it in a presenter (Laracasts/presenter will do just fine), so you can call it from the views.

No need then for a static call.

In there you can do

function getTotal()
{
return array_sum(array_pluck($this, ('items.price'))); // or any other conversion elaboration you need to do
}

And output it like

$invoice_instance->present()->getTotal();

I can't think of anything more straight forward than this.

1 like
0re1's avatar
Level 1

@jakeryansmith I know, just as you wrote - it was just quickly rewritten to bring in some simplicity to the audience here. I fix the bugs and typos not to confuse other readers...

jakeryansmith's avatar

Ok

Call to undefined method Illuminate\Database\Query\Builder::sum()

This means you are calling it on a query builder instance on not on an Eloquent instance.

What happens If you do something like:

$invoice = Invoice::findOrFail(1);
$invoice->sum()

There might also be a naming conflict. You should rename your custom sum method to something unique like sumInvoice

sid405's avatar

Guys, sum has been defined as a model method, the builder won't really respect it.

1 like
0re1's avatar
Level 1

@sid405 Are Laracasts/presenters still used? I haven't found any information regarding it in the Laravel 5 documentation. Could you send me in the right direction regarding to presenters? Thanks.

0re1's avatar
Level 1

@sid405 It seems to me pretty common issue to solve and the need to have some externality like presenter seems odd to me a bit...

0re1's avatar
Level 1

@jakeryansmith No matter what I write as the method name I cannot see it anywhere - probably as @sid405 have just written... Try yourself on some simple example that that approach with calling Model methods doesn't work anyhow...

sid405's avatar

@0re1 It's a common issue that people hack all sorts of different way. That is the textbook case for a presenter.

On the other hand you could abstract that logic to a repository-like static class and call it from the view, but that just seems an utter overkill.

To my knowledge these are the options for your problem, because as you say, the scope solution is not for your case.

Still want the presenter info?

0re1's avatar
Level 1

@sid405 I have found something (https://github.com/laracasts/Presenter) but it seems a bit obsolete to me and I don't know if it isn't intended for an older versions of Laravel so I would be glad if you show me where should I go to find the information.

Further I am not sure if this couldn't be solved by some advanced stuff like view-composers or service-containers...

sid405's avatar

@0re1 I installed it on a L5.1.11 project two days ago and it works fine and all the info is contained on the github page.

Frankly to use a view composer and service container just for this, seems a bit much. Overengineering it.

I usually use presenters for a multitude of 'bits and bobs' that are particular and need to 'just-work'.

I don't know what else to suggest you mate.

1 like
jakeryansmith's avatar

If this is just for presentation then a presenter is fine, but if you want to be able to use the summed value in other parts of your application then don't use a presenter.

Why not just create a service class for this? Then you can access it in many ways: in your controller, in a view with blade service injection, or in any other part of your app.

1 like
0re1's avatar
Level 1

@jakeryansmith Actually I would probably need later modify the code to save the sum in the database to Invoice directly due to performance reasons and recount it only if new items are added or the items price is changed. I probably try to create a service class first and in case of problems I write here again as I haven't written any service yet...

jakeryansmith's avatar

@0re1 sounds good. If you encounter any problems writing your service class I'd be happy to help.

1 like
0re1's avatar
0re1
OP
Best Answer
Level 1

Complex solution from @jakeryansmith: If this is just for presentation then a presenter is fine, but if you want to be able to use the summed value in other parts of your application then don't use a presenter.

Why not just create a service class for this? Then you can access it in many ways: in your controller, in a view with blade service injection, or in any other part of your app.

Easier solution from @sid405: It's a common issue that people hack all sorts of different way. That is the textbook case for a presenter.

On the other hand you could abstract that logic to a repository-like static class and call it from the view, but that just seems an utter overkill.

To my knowledge these are the options for your problem, because as you say, the scope solution is not for your case.

I installed it on a L5.1.11 project two days ago and it works fine and all the info is contained on the github page.

Frankly to use a view composer and service container just for this, seems a bit much. Overengineering it.

I usually use presenters for a multitude of 'bits and bobs' that are particular and need to 'just-work'.

I don't know what else to suggest you mate.

0re1's avatar
Level 1

@jakeryansmith Actually I am lost from the beginning :-/ I don't know what do you mean by service class - if it is something which is in Services directory like Registrar class (no documentation found for just service class, but maybe I haven't dug deep enough) or you mean I should write some service provider (I have found some documentation and I have seen Laracast fundamentals video series but I am not getting the concept very much and I don't know how it could help to solve my problem). I would be glad if you could explain to me shortly what should I do. How should I access the instance of Invoice Model object and get the items prices from service class?

I appreciate any help you could give me. Thanks.

ionutbajescu's avatar

I see that the people above me wrote general solutions. They're really great in case that you need more control over the presentation.

However, for the OP's use case, I think that either a presenter or a query scope are overkill.

If you just need the total amount of the items then either $invoice->items->sum('price'); or $invoice->items()->sum('price'); should do the job.

(both of the methods should work perfectly, the first method does the processing using php and the second one does it by using mysql)

jakeryansmith's avatar

@0re1 A service class can be very basic. I like to think of it as a class for handling and organizing a specific set of functionality. So you might create a class called InvoiceCalculator and this class could hold any logic for performing calculations on your invoices. You don't need a service provider, unless the class has a lot of dependancies then it would be useful to make a service provider.

class InvoiceCalculator {
    
    public method sum(Invoice $invoice) //type hint the Invoice model and pass in an instance
    {
        // Perform your complex calculation here
    }   
}

You can then inject your InvoiceCalculator class easily into your controller using controller injection.

Then in your controller you can call methods on your InvoiceCalculator

public function edit(InvoiceCalculator $invoiceClaculator)
{
    $invoice = Invoice::findOrFail(1);
    return view(invoice.index)->withSum($invoiceClaculator->sum($invoice))
}
1 like
0re1's avatar
Level 1

@ionutbajescu If you read the whole thread you get why that is not an option for me and that it was already suggested, but thanks for your effort anyway. :)

Please or to participate in this conversation.