Laravel join three tables with relationships - php

How can I join three tables where the foreign ID of each table are part of another,
In the below code, I am trying to get all payments from PaymentsTable which have invoice_id, and InvoiceTables which have customer_id,
Here is DB relationships
Table : Customers
customer_id; Primary Key
Table: Invoices
invoice_id: Primary key
customer_id: Foreign Key, Customers
Table : Payments
payment_id: Primary Key
invoice_id: Foreign Key, Payments
$payments = SalesPayments::where('payment_amount', '!=' , NULL)
->join('sales_invoices', 'sales_invoices.invoice_id', '=', 'sales_payments.invoice_id')
->join('payment_options', 'payment_options.paymentoption_id', '=', 'sales_payments.paymentoption_id')
//->join('customers', 'customers.customer_id', '=', 'sales_payments.invoice_id')
->get();
Below Relationship has been tried in Payments model,
public function customers()
{
return $this->hasManyThrough(Customers::class, SalesInvoices::class, 'payment_id' ,'invoice_id', 'payment_id', 'customer_id' );
}
Now with each payment row, I want to get customer information as well, So How can I get it?

As you have not defined relationships, we have to go with Query builder, Update your joins as below
$payments = DB::table('sales_payments')
->join('sales_invoices', 'sales_invoices.invoice_id', '=', 'sales_payments.invoice_id')
->join('customers', 'sales_invoices.customer_id', '=', 'customers.customer_id')
->join('payment_options', 'payment_options.paymentoption_id', '=', 'sales_payments.paymentoption_id')
->select('sales_payments.*', 'sales_invoices.*', 'customers.*', 'payment_options.*')
->get();
Then you can add where clause.

Lets assume that you need all payment information with its invoices and customer details related to that payment.
If you are Looking for the above Response then You can Simply Retrieve Data with Nested Eager Loading
Like
$payments=SalesPayments::with('salesInvoice.customer')->where(Your condition)->get();
In Payments Model
public function SalesInvoice(){
return $this->BelongsTo(Your sales model class)
}
In SalesInvoice Model
public function customer(){
return $this->BelongsTo(Your user model class)
}

Related

Laravel eloquent restrict withCount with whereHas or another

I have 3 models Supplier, Purchase and PurchaseDetail.
To join the Supplier model with PurchaseDetail through Purchase I have created a hasManyThrough relation.
hasManyThrough inside Supplier model:
public function detail_purchases(){
return $this->hasManyThrough(PurchaseDetail::class, Purchase::class);
}
The relationship works well and I can count the quantities purchased from sellers as follows:
$collectors = Supplier::withCount(['detail_purchases as qty_sold' => function($query) {
return $query->select(\DB::raw('SUM(qty)'))
->where('unit', '=', 'kg');
}])
->where('supplier_type','=','persona_natural')
->orderBy('qty_sold','desc')
->get();
SQL Query output:
select `suppliers`.*, (
select SUM(qty)
from `purchase_details`
inner join `purchases` on `purchases`.`id` = `purchase_details`.`purchase_id`
where `suppliers`.`id` = `purchases`.`supplier_id`
and `unit` = 'kg'
and `purchases`.`deleted_at` is null
) as `qty_sold`
from `suppliers`
where `supplier_type` = 'persona_natural'
order by `qty_sold` desc;
Output rows:
My problem is that this query is bringing me sellers that I did not make purchases from them, I don't know why they infiltrate the query if it is assumed that the hasManyThrough relationship only joins those who are registered in Purchase or made purchases from them.
Also the Supplier model has another relation called purchases:
public function purchases() {
return $this->hasMany(Purchase::class, 'supplier_id');
}
And the model Purchase has a relation hasMany with PurchaseDetail :
public function details(){
return $this->hasMany(PurchaseDetail::class, 'purchase_id');
}
Updated
Using whereHas now I can get all the suppliers that I did purchases however the qty_sold is not appearing in the results:
$collectors = Supplier::whereHas('purchases', function($query){
$query->withCount(['details as qty_sold' => function($query){
$query->select(\DB::raw('SUM(qty)'))
->where('unit', '=', $this->unit);
}]);
})
->where('supplier_type','=','persona_natural')
->get();
This selection is important because I want to know how many kgs of all products I purchased.
Thanks #Tony
In your updated query can you move the withCount out of the subquery? So you would have Supplier::withCount(...)->whereHas('purchases')..
Ok, I fixed it, adding first the whereHas directive the I used the hasManyThrough
relation with withCount. Of this way only suppliers that has purchases are selection.
$collectors = Supplier::whereHas('purchases')
->withCount([
'detail_purchases as qty_sold' => function($query){
$query->select(\DB::raw('SUM(qty)'))
->where('unit', '=', 'kg');
}
])
->where('supplier_type','=', 'persona_natural')
->get();

Laravel join two tables for ruled out records

In Laravel, how can I reject a record in a query based on a value with one of the table with which this table has relations?
For example, I have the Products table and the Categories table. The Categories table has a one-to-many relationship, one category can have many products. The categories table are is_visible is_deleted. How to make inquiries on the Products table so that it rejects products that belong to the category that has and set fields is_visible = false or is_deleted = true ?
I tried something like this:
$products = ProductTable::join('product_category_tables', 'product_category_tables.id', '=', 'product_tables.id')
->where('product_category_tables.is_visible', '=', true)
->where('product_category_tables.is_deleted', '=', false)
->where('product_tables.is_visible', '=', true)
->where('product_tables.is_deleted', '=', false)
->paginate(50);
But from this query I have only one record. I can't make this query on Category table becouse I want get only 25/50/100 products for paginate.
If you are using Laravel Modal(Eloquent) and have defined the one to many relationship properly in both Product and Category Model. then you can achieve this by the following query:
$products = Product::where("is_visible", true)
->where("is_deleted", false)
->whereHas('category', function($categories){
$categories->where("is_visible", true)
->where("is_deleted", false);
})
->get();
In Product model you should have defined the relationship as bellow:
public function category()
{
//From your mentioned query I am seeing that
//product table's id is actually categories table's id(which should not be like this though),
//so the relationship is
return $this->belongsTo(Category::class, 'id');
}

Laravel Group By relationship column

I have Invoice_Detail model which handles all products and it's quantities, this model table invoice_details has item_id and qty columns.
The Invoice_Detail has a relation to Items model which holds all item's data there in its items table, which has item_id, name, category_id.
The Item model also has a relation to Category model which has all categories data in its categories table.
Question: I want to select top five categories from Invoice_Detail, how?
Here's what I did:
$topCategories = InvoiceDetail::selectRaw('SUM(qty) as qty')
->with(['item.category' => function($query){
$query->groupBy('id');
}])
->orderBy('qty', 'DESC')
->take(5)->get();
But didn't work !!
[{"qty":"11043","item":null}]
Category::select('categories.*',\DB::raw('sum("invoice_details"."qty") as "qty"'))
->leftJoin('items', 'items.category_id', '=', 'categories.id')
->leftJoin('invoice_details', 'invoice_details.item_id', '=', 'items.id')
->groupBy('categories.id')
->orderBy('qty','DESC')
->limit(5)
->get();
This will return you collection of top categories.
Tested on laravel 5.5 and PostgreSQL.
UPD:
To solve this without joins you can add to Categories model this:
public function invoiceDetails()
{
return $this->hasManyThrough(Invoice_Detail::class, Item::class);
}
And to select top 5 categories:
$top = Category::select()->with('invoiceDetails')
->get()->sortByDesc(function($item){
$item->invoiceDetails->sum('qty');
})->top(5);
But first solution with joins will work faster.

how to use where for relationship in laravel eloquent?

I want to use where clause on the another relationship not my current selecting model as below table
table Customer
-------id-----customer_name
Table ModelA
table Customer
-------id-----fk_custome_id
table ModelB
-------id-----fk_ModelA_id
Function in Controller
$data['data'] = customer::with(['modelA','modelA.modelB'])
->where('fk_customer_id', 2)->get();
Customer Model
final function ModalA (){
return $this->hasMany('App\Models\ModelA', 'fk_customer_id', 'id');
}
ModelA Model
final function Modelb (){
return $this->hasMany('App\Models\ModelB', 'fk_modelA_id', 'id');
}
Error:
I will got error as below because select sql don't find the column name fk_customer_id in table customer, So how can I user fk_customer_id (in table ModelA) for where.
You can useYou can use whereHas like:
$data['data'] = customer::with(['modelA','modelA.modelB'])
->whereHas('modelA', function ($query) {
$query->where('fk_customer_id', 2);
})->get();
It's because when you're using with you just eager load constrints but you won't attach it to the main query (of the customer model). So there are two way: one modern with using whereHas or using join queries.

Laravel 5 - how to perform left join on same table

In my application, there's two tables at the moment: users and employees. The latter is simply a profile table for the boilerplate users table that ships with Laravel.
I have two foreign keys inside of employees, one is user_id that points to id on users table. So basically, the profile data of the registered user. The second foreign key in employees is manager_id. That is the id of the person who is the manager of an employee. Some users are managers and have a blank manager_id field.
I want to retrieve the row from my Employee model where the user_id matches the manager_idand then send them all to my view, where they will see the first_name and last_name of the manager. At the moment I see the manager ID instead of their name.
Sample LeftJoin =>
DB::connection('testing')->table('table1 as t1')
->select('t1.column, t2.column')
->leftJoin('table2 as t2','t2.client_id','=','t1.id')
->get();
Try some thing like this:
$users = DB::table('users t1')
->join('users t2', 't1.id', '=', 't2.user_id')
->select('your column list')
->get();
// here we are creating 2 aliases for users table which make it self join
In your Employees model add the following method:
public function manager()
{
return $this->hasOne('App\User', 'id', 'manager_id');
}
You can then get the name of the Manager like this:
$employee = \App\Employee::find(1);
$manager = $employee->manager()->first();
$manager_name = $manager->name;

Categories