Laravel Has Many Trough with multiple levels - php

I have a very specific query that I don't know how to get in Eloquent
I have the following tables
Orders, OrderInvoice,OrderPayment
So each Order has many OrderInvoices and each OrderInvoice has many OrderPayments
Then I have the table turns which has many payments
So what I want is to get all the orders related to a specific turn
I know how to get all the invoices:
$this->belongsToMany('OrderInvoice','orders_payments','turn_id','invoice_id');
But I need the next level and get the Orders,
How can I achieve that in eloquent?
Thank you so much!
EDIT: Tables structure
Orders
id
OrderInvoice
id
order_id
OrderPayment
id
invoice_id
turn_id
Turns
id

Most straightforward:
// load related collections
$turn->load('invoices.orders');
// then
$turn->invoices; // collection of invoices, for each you can do this:
$turn->invoices->first()->orders; // collection of orders for an invoice
If you want to get single collection of orders for given turn, then you need this trick, which is the easiest way (no joins etc), but not the best in terms of performance for sure:
// store orders in a separate variable
$turn->load(['invoices.orders' => function ($q) use (&$orders) {
$orders = $q->get()->unique();
}]);
// then
$orders; // single collection of all the orders related to the given turn
// also still accessible as usually:
$orders->invoices;
$orders->invoices->first()->orders;

Related

GroupBy data with a specific column using Laravel 8 [duplicate]

My goal is to retrieve all of a user's 'items' and display then in my view grouped by their 'status'. There are 4 possible statuses, each with their own <div> on the page containing the items' info. After some poking around I believe I need to use the groupBy() method like so:
$items = Item::ownedBy( Auth::id() )->groupBy('status')->get();
This does seem to do some sort of grouping, but when I iterate over the collection I get a max of 4 results, one for each status. This doesn't really solve my problem as I need to display all of a user's items for each status, not just one. I must be missing something here, I'd really like to avoid making a query for each status and displaying them that way. I suppose I could filter the collection by status and create 4 new collections, but isn't this what groupBy() is supposed to do?
You can do that very easy with laravel and Collections. Collections have powerful API and a lot of handy methods, to easily achieve what you want.
Your mistake here is that you are calling groupBy on the QueryBuilder object which returns only groupped by records from the database. Instead you must select all of the records you need, then they will be returned as a collection. After that you can manipulate the collection as you wish. So what you need is:
$items = Item::ownedBy( Auth::id() )->get()->groupBy('status');
You can view all of the Colletion class useful methods here.

Filter Illuminate collection based on emptiness of child collection

Orders implements a HasManyThrough relationship to Items. I want to filter the Orders collection to ones just the orders that have zero items. I tried:
$orders->where('items', '!=', []);
This of course doesn't work as it's only possible to do discrete comparisons (equal, not equal, less/greater than, etc) against concrete values. There is no "isEmpty" property. Something like a whereCallback would be super.
What would be the easiest way to perform this? The same $orders collection is already used in multiple ways in the same request, so I'd rather not do another roundtrip to the database.
It would be easier if you include the items_count to the orders collection while fetching it from the database.
$orders = Order::withCount('items')->restOfTheQuery()->get();
this will add another property called items_count to each and every order object in the $orders collection
and then, when you want only the orders which doesn't have items
// using collections where function
$orderWithoutItems = $orders->where('items_count', 0);
I think filter() is kind of like the whereCallback you're looking for.
$ordersWithoutItems = $orders->filter(function($order) {
return $order->items()->doesntExist();
});

How to return a record with its relation only if the relation has not been deelted

I am using Laravel 5.2 and have some relationships between my models setup.
I have a Product model and a Customer model (a product has a customer).
I am using the following to get a list of customers, however I am also using soft deletes. If the customer has been soft deleted (the relation), I don't want to return the product.
How do I achieve this in Laravel?
$products = Product::with('customer')->get(); --want to say "where customer.deleted_at is null"
You have to call has on the query:
$products = Product::with('customer')->has('customer')->get();
As #Lock put it, use
$products = Product::with('customer')->has('customer')->get();
TIP: For the opposite - i.e to get only the products with deleted customers (doesn't make much sense here but you may find this handy in future), use below
$deleted=Product::with(['customer' => function ($q) {
$q->withTrashed();
}])->onlyTrashed()->get();

Laravel 5.1 / Eloquent: How do I use a 3rd table to select from the second table

I have three tables: users, purchase_orders and approvals.
One purchase_order has to be approved by multiple users.
When a new purchase_order gets created, I also create 3 pending approvals belonging to that PO.
The approvals table has a field allowed_user_type that determines who can approve it.
I can't figure out, what is the Eloquent way of selecting the pending purchase orders that can be approved by a specific user, as these are determined from the approvals table.
So far I can pull the pending approvals from the approvals table for a user with the following in the User model.
public function approvals_pending()
{
return $this->hasMany('App\Approval', 'allowed_user_type', 'user_type')
->where('approved', '=', 0);
}
The question is, how do I combine this with a theoretical filter?
I mean ideally, I would love to write:
return $this->hasMany('App\PO')->whereIn('id', '=', $this->approvals_pending()->get()->po_id);
Or something like that...
Any ideas would be greatly appreciated.
OK, for anyone interested I found a solution:
It's very close to what I thought I would have to write.
The lists method basically creates a single array out of the selected field, so it can be plugged-in directly to a whereIn method like so:
return \App\PO::whereIn('id', $this->approvals_pending()->lists('po_id'));
I don't know if this is the most Eloquent way of doing this but it does work.

Laravel Eloquent Sum of relation's column

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() }}

Categories