I implemented a rating system looking at the rateable laravel package and it works as it should. I have avg rating for each product but want to add besides it the number of users that rated that product. For instance an avg rating of 4.5 that was rated by 5 users.
The package has userSumRating below which outputs the sum of a user's rating but that is not the same with what i am looking for
public function userSumRating()
{
return $this->ratings()->where('user_id', \Auth::id())->sum('rating');
}
I have tried a couple of things but none has worked yet.
I would utilise group by and count from SQl terminology. This is rough idea, not ranned but i think this should solve it. Group by squashes all rows with the same user into one, then we just count how many there is after that.
public function usersRated()
{
return $this->ratings()->groupBy('user_id')->count('user_id');
}
After looking at package you have mentioned in the question, ratings() gives all the ratings associated with the product so you can count those ratings like this:
$product->ratings()->count();
I am not sure but this might work. I have assumed user rates particular product only once.
Well got it working. First made a user only able to rate once with subsequent ratings as updates then
$this['count'] = Advert::with(
[
'ratings' => function ($query) {
$query->count('rating');
}
])
->where('slug', $slug)
->groupBy('user_id')
->first();
then in twig
{{ count.ratings|length }}
Related
I built an application to keep track of the sales. In my customers view, I want a column with total sales per customer, but as the customer base is growing, the list is loading more and more slowly. This is what I did (simplified):
Controller:
$customers = App\Customer::get();
View:
#foreach ($customers as $customer)
{{ $customer->name }} {{ $customer->totalSales() }}
#endforeach
Model:
public function totalSales()
{
$invoiceLines = InvoiceLine::whereHas('invoice', function ($query) {
$query->where('customer_id', $this->id);
})->get();
$sales = $invoiceLines->reduce(function ($carry, $invoiceLine) {
return $carry + ($invoiceLine->quantity * $invoiceLine->pricePerUnit);
});
return $sales ?: 0;
}
What would be the best way to make this view/report more "scalable"?
I have been thinking in creating command that calculates the total sales per customer overnight and put the result in the customer table, but that means that the numbers won't be accurate during the day...
this seems like a very interesting problem.
I have been thinking in creating command that calculates the total
sales per customer overnight and put the result in the customer table
this is a good option.
but that means that the numbers won't be accurate during the day...
You can keep the numbers accurate by doing the following:
by incrementing the customers table count every time a invoice is made.
This should work for total sales.
Make sure you have an index on the customer_id column.
Search for ways to do a "SQL SUM on 2 columns using laravel".
Try and find some way to do "SQL SUM on 2 with a GROUP BY. Doing this will replace #2
A good way to speed up your application is to avoid making calls to the database in a loop. That is what #3 is suggesting (the loop in this case is the #foreach in your View and the database call is the InvoiceLine::...->get(); in totalSales()
Adding the index (if missing) and reducing the # of calls to the DB will yield the best results.
I have limited knowledge of Laravel but one way to do this with raw SQL would be:
SELECT c.name, ts.totalSales
FROM customer c
INNER JOIN (
SELECT customer_id, SUM(quantity * pricePerUnit) as totalSales
FROM invoice
GROUP BY customer_id
) ts ON c.id = ts.customer_id
You can see how all the data you're trying to print is pulled at once? I assume you'd want to try and write that using Laravel's Eloquent thingy.
Based on the answers above, I came to the following solution:
I created an event: App\Events\InvoiceSaved which is dispatched every time an invoice is "touched" (created, updated or deleted). The App\Events\InvoiceSaved event will calculate the total sales for the customer and add the result to the customers table (extra field total_sales). Now I can just query my customers table and need to query a relation. The loading time dropped from 7 seconds to 0.5 second!
I am working on some reporting for an app that has users and bookings.
The aim is to return all users that haven't made a booking within the last 2 months.
My query currently returns all users who has ever made a booking at the given team and order the bookings by newest to oldest.
My question is how can I only return users where the last booking made has an estimated booking time more than 2 months ago?
My current query is:
$collection = User::whereHas('customerBookings', function($q) use ($teamId) {
$q->where('team_id', $teamId);
})->with(['customerBookings' => function ($q) use ($teamId) {
$q->where('team_id', $teamId);
$q->orderBy('estimated_booking_time', 'desc');
}])
->get();
This correctly returns all users that have bookings (relationship to a bookings table) that have the requested team_id and then only return the bookings that match the same requirements.
I was going to use ->reject on the collection but I would rather just filter out in the query itself.
Ideally I want to add something to the with claus:
$q->where( FIRST RELATIONSHIP ITEM ->estimated_booking_time, '<=', Carbon::now()->subMonths(2) );
The issue for me is how I can get the first item from the relationship and use it to only return the users depending on this last item.
You can just put in where() the time of last booked (e.g. 2months ago) minus the time now. You may also have a look at this reference. I think this has similar to yours.
Probably you have a has-many relationship between the User and the Booking models.
The User model does have a "bookings()" method related to the Booking model.
Said that, you can do:
$users = \App\User::whereHas('bookings', function ($query) {
$query->where('lestimated_booking_time', '<=', Carbon::now()->subMonths(2));
});
Quick question to which I haven't been able to find a solution for myself, nor by googling it.
On my dashboard view I show my payment plans, simply by doing $plans = Plan::all() and I return that to the view. Nothing fancy there. But, on my actual dashboard view, I want to show the total amount in dollars from the transactions that have been done. So on my view, I do the following (stripped example):
#foreach($plans as $plan)
{{ $plan->paidAmount() }}
#endforeach
Which is using the following method from my model:
private function paidAmount()
{
return $this->transactions->sum('amount');
}
Which basically just grabs the total amount of all related transactions to that plan, and displays it on the page. However, this generates a new query for each of my plans. Let's say I have 5 plans, it will generate 5 additional queries for each amount I need to be displayed.
Same goes for the remaining amount that I want to display:
private function paidAmount()
{
return $this->amount - $this->paidAmount();
}
Which will generate an additional 5 queries. So for a simple table in which I display some information,a whopping 11 queries are being executed.
Is there any way that I can include the paidAmount and remainingAmount in my original query? I know there should be a way, I just haven't been able to find it yet. So just something along the lines of:
$plans = Plan::with('paidAmount')->get();
You can use sub queries inside your main query Like this to calculate the sum
Like this -
$plans = Plan::all('*', DB::RAW("(SELECT SUM(amount) FROM transactions WHERE plan_id=plans.id ) as amount") );
This way you will already have sum when rendering in views
I'm trying to display a list of all the users and how many posts they've each made this week, and how many they've made last week, inside of a form
I've used a hasMany relation between the two tables and the relationship it's self is working.
return $this->hasMany('App\applications', 'user_id');
inside of the view what I have that's displaying the post count is
{{$user->applications->count()}}
The main thing I'm stuck on is the SQL Query or using Carbon inside of the controller function to achieve this.
If anyone has done this before your help would be greatly appreciated!
Thank you.
Since you want to count posts for many users, eager load the data and use withCount():
User::withCount(['applications' => function($q) {
$q->whereBetween('created_at', [Carbon::now()->startOfWeek(), Carbon::now()]);
}])->get();
If you want to count the number of results from a relationship without actually loading them you may use the withCount method, which will place a {relation}_count column on your resulting models.
https://laravel.com/docs/5.4/eloquent-relationships#counting-related-models
I've been working on a shopping cart application and now I've come to the following issue..
There is a User, a Product and a Cart object.
The Cart table only contains the following columns: id, user_id, product_id and timestamps.
The UserModel hasMany Carts (because a user can store multiple products).
The CartModel belongsTo a User and CartModel hasMany Products.
Now to calculate the total products I can just call: Auth::user()->cart()->count().
My question is: How can I get the SUM() of prices (a column of product) of the products in cart by this User?
I would like to accomplish this with Eloquent and not by using a query (mainly because I believe it is a lot cleaner).
Auth::user()->products->sum('price');
The documentation is a little light for some of the Collection methods but all the query builder aggregates are seemingly available besides avg() that can be found at http://laravel.com/docs/queries#aggregates.
this is not your answer but is for those come here searching solution for another problem.
I wanted to get sum of a column of related table conditionally.
In my database Deals has many Activities
I wanted to get the sum of the "amount_total" from Activities table where activities.deal_id = deal.id and activities.status = paid
so i did this.
$query->withCount([
'activity AS paid_sum' => function ($query) {
$query->select(DB::raw("SUM(amount_total) as paidsum"))->where('status', 'paid');
}
]);
it returns
"paid_sum_count" => "320.00"
in Deals attribute.
This it now the sum which i wanted to get not the count.
I tried doing something similar, which took me a lot of time before I could figure out the collect() function. So you can have something this way:
collect($items)->sum('amount');
This will give you the sum total of all the items.
you can do it using eloquent easily like this
$sum = Model::sum('sum_field');
its will return a sum of fields,
if apply condition on it that is also simple
$sum = Model::where('status', 'paid')->sum('sum_field');
You can pass this as UserModel attribute. Add this code to UserModel.
public function getProductPriceAttribute(){
return $this->cart()->products()->sum('price');
}
I assume something like this:
UserModel has a one to many relationship with a CartModel named cart
CartModel has a one to many relationship with ProductModel named products
And then you can get sum price of the product like this:
Auth::user()->product_price
Since version 8, there is a withSum method on Eloquent, so you could use this.
Auth::user()->withSum('products', 'price')->products_sum_price;
This won't load all products into memory and then sum it up with collection method. Rather it will generate a sub query for the database, so it's quicker and uses less memory.
Also using query builder
DB::table("rates")->get()->sum("rate_value")
To get summation of all rate value inside table rates.
To get summation of user products.
DB::table("users")->get()->sum("products")
For people who just want to quickly display the total sum of the values in a column to the blade view, you can do this:
{{ \App\Models\ModelNameHere::sum('column_name') }}
You can also do it for averages:
{{ \App\Models\ModelNameHere::avg('column_name') }}
Min:
{{ \App\Models\ModelNameHere::min('column_name') }}
Max:
{{ \App\Models\ModelNameHere::max('column_name') }}
To get the Count of a table:
{{ \App\Models\ModelNameHere::count() }}