Join tables in Laravel Eloquent method - laravel

How to write this code in eloquent method ?
$product = DB::table('products')
->join('purchase', 'products.id', '=', 'purchase.id')
->join('sales', 'purchase.id', '=', 'sales.id')
->select('sales.*', 'purchase.*','products.*')
->get();

Create model Product and add one to many relationship with Purchase in Product model.
public function purchases()
{
return $this->hasMany('App\Models\Purchase');
}
Create model Purchase and add one to many relationship with Sale in Purchase model.
public function sales()
{
return $this->hasMany('App\Models\Sale');
}
Create model Sale.
You can retrieve data using following statement.
$products = Product::with('purchases.sales')->get();
Note: I am assuming the relationship as one to many you can also declare as per your data, also you can define one to many inverse relationship, please refer to laravel docs https://laravel.com/docs/8.x/eloquent-relationships#one-to-many.
You will get purchases and sales data in different key so you can use below syntax to loop over it.
foreach ($products as $product) {
foreach ($product->purchases as $purchase) {
//Purchase data for current product
foreach($purchase->sales as $sale){
//Sale data for current purchase
}
}
}

Related

Laravel eloquent for four tables

I'm new to Laravel. I am developing a project. and in this project I have 4 tables related to each other
-Users
-Orders
-OrderParcels
-Situations
When listing the parcels of an order, I want to get the information of that order only once, the user information of that order once again, and list the parcels as a table under it. so far everything ok. but I also want to display the status of the parcels listed in the table as names. I couldn't add the 4th table to the query. do you have a suggestion? I'm putting pictures that explain the structure below.
My current working code is
$orderParcels = Orders::whereId($id)
->with('parcels')
->with('users:id,name')
->first();
and my 'orders' model has method
public function parcels(){
return $this->hasMany(OrderParcels::class);
}
public function users(){
return $this->hasOne(User::class,'id','affixer_id');
}
Note[edit]: I already know how to connect like this
$orderParcels = DB::table('order_parcels as op')
->leftjoin('orders as o','op.orders_id','o.id')
->leftjoin('users as u','o.affixer_id','u.id')
->leftjoin('situations as s','op.status','s.id')
->select('op.*','o.*','u.name','s.situations_name')
->where('op.orders_id',$id)->get();
but this is not working for me, for each parcels record it returns me orders and user info. I want once orders info and once user info.
Laravel provides an elegant way to manage relations between models. In your situation, the first step is to create all relations described in your schema :
1. Model Order
class User extends Model {
public function parcels()
{
return $this->hasMany(OrderParcels::class);
}
public function users()
{
return $this->hasOne(User::class,'id','affixer_id');
}
}
2. Model Parcel
class Parcel extends Model {
public function situations()
{
return $this->hasOne(Situation::class, ...);
}
}
Then, you can retrieve all desired informations simply like this :
// Retrieve all users of an order
$users = $order->users; // You get a Collection of User instances
// Retrieve all parcels of an order
$parcels = $order->parcels; // You get a Collection of User instances
// Retrieve the situation for a parcel
$situations = $parcel->situations // You get Situation instance
How it works ?
When you add a relation on your model, you can retrieve the result of this relation by using the property with the same name of the method. Laravel will automatically provide you those properties ! (e.g: parcels() method in your Order Model will generate $order->parcels property.
To finish, in this situation where you have nested relations (as describe in your schema), you should use with() method of your model to eager load all the nested relation of order model like this :
$orders = Orders::with(['users', 'parcels', 'parcels.situations'])->find($id)
I encourage you to read those stubs of Laravel documentation :
Define model relations
Eager loading
Laravel Collection
Good luck !
Use join to make a perfect relations between tables.
$output = Orders::join('users', 'users.id', '=', 'orders.user_id')
->join('order_parcels', 'order_parcels.id', '=', 'orders.parcel_id')
->join('situations', 'situation.id', '=', 'order_parcels.situation_id')
->select([
'orders.id AS order_id',
'users.id AS user_id',
'order.parcels.id AS parcel_id',
'and so on'
])
->where('some row', '=', 'some row or variable')->get();

laravel eloquent with pivot and another table

I have 4 table categories, initiatives, a pivot table for the "Many To Many" relationship category_initiative and initiativegroup table related with initiatives table with initiatives.initiativesgroup_id with one to many relation.
With pure sql I retrive the information I need with:
SELECT categories.id, categories.description, initiatives.id, initiatives.description, initiativegroups.group
FROM categories
LEFT JOIN category_initiative ON categories.id = category_initiative.category_id
LEFT JOIN initiatives ON category_initiative.initiative_id = initiatives.id
LEFT JOIN initiativegroups ON initiatives.initiativegroup_id = initiativegroups.id
WHERE categories.id = '40'
How can I use eloquent model to achieve same results?
Since you have such a specific query touching multiple tables, one possibility is to use query builder. That would preserve the precision of the query, retrieving only the data you specifically need. That would look something like this:
$categories = DB::table('categories')
->select([
'categories.id',
'categories.description',
'initiatives.id',
'initiatives.description',
'initiativegroups.group',
])
->leftJoin('category_initiative', 'categories.id', '=', 'category_initiative.category_id')
->leftJoin('initiatives', 'category_initiative.initiative_id', '=', 'initiatives.id')
->leftJoin('initiativegroups', 'initiatives.initiativegroup_id', '=', 'initiativegroups.id')
->where('categories.id', '=', 40)
->get();
In your models define the relationships:
Category.php model
public function initiatives()
{
return $this->belongsToMany('App\Initiative');
}
Initiative.php model (If has many categories change to belongs to many)
public function category()
{
return $this->belongsTo('App\Category');
}
Then maybe change your initiativegroup -> groups table, and then create a pivot table called group_initiative. Create model for group. Group.php and define the relationship:
public function initiatives()
{
return $this->belongsToMany('App\Initiative');
}
Then you can also add the following relationship definition to the Initiative.php model
public function group()
{
return $this->belongsTo('App\Group');
}
That should get you started.
for the record..
with my original relationship, but changing table name as alex suggest, in my controller:
$inits = Category::with('initiative.group')->find($id_cat);
simple and clean

Laravel group by, count and access and eloquent relationship

I'd like to group by a foreign key ID, count that group and then in the view be able to access the relationship of that foreign key ID. The first two things (group by and count) succeed with the following query:
$collection = Example::selectRaw('count(*) AS total, foreign_key')
->groupBy('foreign_key')
->orderBy('total', 'DESC')
->get();
But I can't figure out how I can access the eloquent relationship of the foreign key, so that in the view I can do something like
foreach($collection as $c)
{
$c->total;
$c->foreign->title
$c->foreign->address
}
Note that the Example Model has the relationship defined:
class Example extends Model
{
public function foreign()
{
return $this->hasOne('App\Models\Foreign', 'id', 'foreign_key')
}
}
Try this:
$collection = Example::selectRaw('count(*) AS total, foreign_key')
->with('foreign')
->orderBy('total', 'DESC')
->get();
Also, you probably don't need groupBy() since it is a one to one relationship and thus would return only one result (as #DilipHirapara suggested).
In your COntroller
$collection = Example::selectRaw('count(*) AS total, foreign_key')->get();
In your blade
foreach($collection as $c)
{
$c->total;
$c->foreign->title
$c->foreign->address
}

Relationship BelongToMany with additional data

I've 3 tables:
Courses (have category_id)
Authors
Categories (of courses)
In my Authors model I've added:
public function courses () {
return $this->belongsToMany('App\Course', 'courses2authors')->where('status','=', 1);
}
"courses2authors" is the pivot table.
Then in my controller I retrieve courses info with:
$authors = Author::where('status', '=', 1)->orderBy('pos')->with('courses')->get();
It's ok but I've only the category_id in ->courses, how to add category name in the model relationship.
I try something like:
return $this->belongsToMany('App\Course', 'courses2authors')
->where('status','=', 1)->join('categories', 'categories.id', '=',
'courses.category_id')->select('categories.name as categoria');
But in this way in take only the category name and not the course data.
You can define belongsTo relationship in Course Model with Categories.
Course Model
public function categories () {
return $this->belongsTo('App\Categories', 'category_id');
}
While retrieving Author with Courses then you can use like this. (Controller code)
$authors = Author::where('status', '=', 1)->orderBy('pos')
->with('courses',function($query){
$query->with('categories);
})->get();
if you don't want to use like this then you can set $with attribute in Courses Model.
protected $with = ['categories']; // default with define here.
Use in controller :-
$authors = Author::where('status', '=', 1)->orderBy('pos')
->with('courses')->get();

Laravel Eloquent nested query

I was working with Laravel and got stuck in a situation. I have following models:
Category
Product
CategoryProduct
CategoryProduct holds the information about which product belongs to which category (a product may belong to multiple categories).
Now, when I want to load all products belonging to a particular category, I need to run query on Product and CategoryProduct which is where I'm stuck.
I gave it the following try but was unsuccessful:
$products = Product::where('status', '=', 'active')
->where('category_id', '=', $category_id)
->take($count)
->skip($skip)
->get();
Obviously, it will say that category_id is not a column.
Here is my DB & Model structure:
categories table
id,
name,
etc.
products table
id,
name,
sku,
etc.
category_products table
id,
product_id, ( Foreign key to Product.id )
category_id, ( Foreign key to Category.id )
etc.
Product model
class Product extends Eloquent {
protected $table = 'products';
protected $hidden = array();
public static $rules = array('name' => 'required|min:3');
}
Category model
class Category extends Eloquent {
protected $table = 'categories';
public static $rules = array('name' => 'required|min:3');
}
CategoryProduct model
<?php
class CategoryProduct extends Eloquent {
protected $table = 'category_products';
public function product()
{
return $this->belongsTo('Product');
}
public function category()
{
return $this->belongsTo('Category');
}
}
Update
A new question on this
I'm trying to display products. If category is not passed (value is -1), then I will show all products, otherwise I will show products from the passed category.
Now, when I show all products, those products may already exist in a category. I want to display ticked checkbox for products that are already in a category. I'm doing something like this:
if($category_id==-1)
$products = Product::where('status', '=', 'active')->take($count)->skip($skip)->get();
else{
$products = Product::whereHas('categories', function($q) use ($category_id)
{
$q->where('category_id', $category_id);
})->where('status', 'active')
->take($count)
->skip($skip)
->get();
}
The table category_products have product_id, category_id as columns.
Now, the query:
$products = Product::where('status', '=', 'active')->take($count)->skip($skip)->get();
will pick products only from products table. If I check each product for its existence in category_products, then there will be too many database queries for large number of products.
Any idea, how to achieve this. I hope I was able to clear my situation. Thanks
The CategoryProduct model should not be necessary unless you have additional fields besides product_id and category_id which point to other relationships.
What is necessary are the methods for setting up the relationship on the Category and Product models.
In Category, add the relationship function...
public function products()
{
return $this->belongsToMany('Product', 'category_products');
}
In your Product model, do the same for categories.
public function categories()
{
return $this->belongsToMany('Category', 'category_products');
}
Then you can query for your active products that belong to that category using your relationship method and whereHas()
$products = Product::whereHas('categories', function($q) use ($category_id)
{
$q->where('id', $category_id);
})->where('status', 'active')
->take($count)
->skip($skip)
->get();
You don't need a model for a pivot table in Many-to-Many relationships. Look at this section of the Eloquent documentation for further explanation.
You still need to create a migration to set up the pivot table (or do it manually if you don't use migrations), but not a model. Instead, create a function for Category to designate the relationship:
public function products()
{
return $this->belongsToMany('App\Product', 'category_products');
// - You might need to adjust the namespace of App\Product
// - category_products refers to the pivot table name
}
Likewise, Product needs a similar public function.
Then you're able to do it the other way around, by finding the category and then listing all its related products:
$products = Category::find($category_id)
->products()
->where('status', 'active')
->take($count)
->skip($skip)
->get();
This question could also be relevant to yours.

Resources