I have 3 Tables:
Customers
id
name
Sales
customer_id
sale_date
Contacts
customer_id
contact_date
There aren't any update operations in the contacts table. Each process opens a new record in the contacts table. So, a user can have more than one records in the contacts table.
Here are my relations in models:
Customer
public function contacts()
{
return $this->hasMany(Contact::class);
}
public function sales()
{
return $this->hasMany(Sale::class);
}
Contact
public function customer()
{
return $this->belongsTo('App\Customer', 'customer_id');
}
Sale
public function customer()
{
return $this->belongsTo('App\Customer');
}
I would like to have the latest record of the contacts table and make it join with the other related tables.
Here is the query which I have tried:
$record = Contact::groupBy('customer_id')
->select(DB::raw('max(id)'));
$result = Customer::query();
$result->where('is_active', 'YES');
$result->with('sales');
$result->whereHas('contacts', function ($q) use($record){
return $q->whereIn('id', $record)->where('result', 'UNCALLED');
});
return $result->get();
In the blade file, I get some result in foreach loops. However, I am unable to get the related data from the sales and contacts table.
#foreach($result as $item)
#foreach($item->sales as $sale) // Has no output and gives error: Invalid argument supplied for foreach()
#foreach($item->contacts as $contact) // Has no output and gives error: Invalid argument supplied for foreach()
Can anyone help me how to display the sale and contact date? Or any idea for how to improve this code quality?
If you want the latest record of the contacts you can declare another relationship on the Customer model, e.g.:
public function latest_contact()
{
return $this->hasOne(Contact::class)->latest('contact_date');
}
BTW you can always declare one or more hasOne additional relationship if you have a hasMany in place the foreign key used is the same.
In this way you can retrieve latest_contact eager loaded with your Customer model:
$customer = Customer::with('latest_contact')->find($id);
Or use this relationship in your queries, something like that:
$customers = Customer::where('is_active', 'YES')
->with('sales')
->with('contacts')
->whereHas('last_contact', function ($q){
return $q->where('result', 'UNCALLED');
})->get();
Or that:
$customers = Customer::where('is_active', 'YES')
->with('sales')
->with('contacts')
->with('last_contact', function ($q){
return $q->where('result', 'UNCALLED');
})->get();
If you want you can declare last_contact with the additional where:
public function latest_contact()
{
return $this->hasOne(Contact::class)
->where('result', 'UNCALLED')
->latest('contact_date');
}
This way all other queries should be easier.
I hope this can help you.
I'm not sure, but can you try to do the following:
return Customer::where('is_active', 'YES')
->with([
'sale',
'contact' => function ($query) use($record) {
return $query->whereIn('id', $record)->where('result', 'UNCALLED');
}
])->get();
Related
I have 3 tables
user, specialities & results
results contain specialitie_id, user_id
I have create many-to-many relationship
Here's the relationship in user model
public function specialities()
{
return $this->belongsToMany(Speciality::class, 'results', 'user_id', 'specialitie_id')->withPivot('result', 'color');
}
I want to fetch data from the result table based on specialitie_id
If my url is
abc.com/result/5
I have attached table structure as well,
it should show me result related to specialitie_id 5
I tried this but it doesn't work
$result = User::with('specialities')->where('id', 5)->get();
Any help would be appreciated.
Your current query:
$result = User::with('specialities')->where('id', 5)->get();
Is saying, give me all results where the USER_ID = 5 WITH all the specialities related to it.
You are very close but did it backwards:
//Give me all results where the SPECIALITY_ID = 5
//WITH all the users related to it
$result = Speciality::with('users')->where('id', 5)->get();
Please note that the above is an array
Example of a Controller Function:
public function results($id){
$results = Speciality::with('users')->where('id', $id)->get();
return view('user_specialities', compact('results'));
}
Example view user_specialities:
#foreach($results as $result)
<p>Result: {{$result->result}}</p>
<p>Formulary: {{$result->formulary}}</p>
<p>Color: {{$result->color}}</p>
<p>User ID: {{$result->user->id}}</p>
<p>User Name: {{$result->user->name}}</p>
#endforeach
Speciality Model:
public function users()
{
return $this->belongsToMany(User::class, 'results', 'specialitie_id', 'user_id')->withPivot('result', 'color');
}
User Model:
public function specialities()
{
return $this->belongsToMany(Speciality::class, 'results', 'user_id', 'specialitie_id')->withPivot('result', 'color');
}
I have two models with relations as defined below
Order
public function owner()
{
return $this->belongsTo(User::class, 'owner_id');
}
User
public function company(){
return $this->belongsTo(Company::class, 'company_id');
}
company table have 'title' field.
what I want is to get all the orders sorted/order by company title. I've tried different solution but nothing seems to work. Any help or hint would be appreciated.
Recent solution that I tried is
$query = OrderModel::whereHas('owner', function($q) use ($request){
// $q->orderBy('owner');
$q->whereHas('company',function ($q2) use ($request){
$q2->orderBy('title',$request->get('orderByDirection') ?? 'asc');
});
});
but I am not getting user and company relation in query results. also the result remains same for 'ASC' and 'DESC' order.
You could sort the query after adding join like:
return Order::join('users', 'users.id', '=', 'owner_id')
->join('companies', 'companies.id', '=', 'users.company_id')
->orderBy('companies.title')
->select('orders.*')
->get();
You can define new relations in User and Company models.
User
public function orders()
{
return $this->hasMany(Order::class);
}
Company
public function users()
{
return $this->hasMany(User::class);
}
Now you can fetch companies that are in asc order and with the use of relation, you can fetch users and orders. So the ORM like be,
$companies = Company::with('users.orders')->orderBy('title', 'ASC')->get();
So these are the company-wise orders. You can use this too.
I have a query where I get values from 3 tables, for first 2 I use leftJoin, and is Ok, but for third one I try to get an array of objects, and I am not sure how.
In relationship table a have multiple rows for each ID from People table. HasMany type.
$q = Person::leftJoin('registers', 'people.register_id', '=', 'registers.id')
->leftJoin('relationships', 'people.id', '=', 'relationships.person_id') //if I comment this it works for first 2 tables
->find($id);
return response()->json($q);
Person
public function relationship()
{
return $this->hasMany(Relationship::class);
}
public function register()
{
return $this->belongsTo(Register::class);
}
Relationship
public function person()
{
return $this->belongsTo(Person::class, 'person_id');
}
Register
public function people(){
return $this->hasOne(Person::class);
}
UPDATE -> this works,but is kind of ugly, I think that should be a better way in Laravel
$q = Person::leftJoin('registers', 'people.register_id', '=', 'registers.id')
->find($id);
$q2 = Person::find($id)->relationship;
return response()->json([
'values' => $q,
'relationship' => $q2,
]);
You can just use with like this:
Person::with(['register', 'relationship'])->find($id);
i have three table one is category table and another is product table and one more product_to_category table, and it has only product_id and category_id column.
Now i want to get top 10 categories with maximum number of product, with details of 10 products from each category.
What i wrote is
$result = ProductToCategory::groupBy('category_id')->with(['product',function($q){
$q->take(10);
}])->orderBy('category_id)->take(10);
But this is not working.How to write this query properly
Can anyone please help. ty
Model relations
For Product model
public function category(){
return $this->belongsTo(ProductToCategory::class);
}
For Category model
public function products()
{
return $this->hasMany(ProductToCategory::class);
}
For ProductToCategory model
public function product()
{
return $this->hasMany(Product::class);
}
public function category()
{
return $this->belongsTo(Category::class);
}
The most efficient way would be using a raw SQL query because you can't filter products by using eager loading constraint.
But if you want an Eloquent solution anyway, define the relationships:
In the Product model:
public function categories()
{
return $this->belongsToMany(Category::class, 'product_to_category');
}
And in the Category model:
public function products()
{
return $this->belongsToMany(Product::class, 'product_to_category');
}
Then you'll have two options, both have their pros and cons:
1. This code will execute just 2 queries but will use more memory. You could get top ten categories with their products:
$categories = Category::withCount('products')->latest('products_count')->take(10)->with('products')->get();
And then keep only first ten products:
$categories->transform(function($category) {
$topProducts = $category->products->take(10);
unset($category->products);
$category->products = $topProducts;
return $category;
});
2. This solution will create 12 queries but will save the memory:
$categories = Category::withCount('products')->latest('products_count')->take(10)->get();
$categories->transform(function($category) {
$category->products = Product::whereHas('categories', function($q) use($category) {
$q->where('id', $category->id);
})
->take(10)
->get();
return $category;
});
Here is the DB facade version:
$tenPopularTags = DB::table('product_to_category')
->join('category', 'product_to_category.category_id', '=', 'category.id')
->select(DB::raw('count(product_to_category.category_id) as repetition, question_tag.tag_id'))
->groupBy('product_to_category.category_id')
->orderBy('repetition', 'desc')->take(10)
->get();
However I like #Alexey Mezenin way of doing it. Because that is the cleaner way have customized it a bit:
$tenCategories = Category::withCount('products')->orderBy('questions_count', 'DESC')->take(10)->get();
Have used both in my project blog with post and categories relationship and it works!
I have a 3 models: Phone, Product and Store.
A phone has many Product which belongs to a Store.
I am trying to add a golbal scope so that every time I load a phone, products and stores counts are loaded automatically.
products_count works fine, however stores_count is a bit tricky since store is not a Phone's relationship but a Product's.
I have tried the following but it gives me an error "Method getRelated does not exist.", I assume because stores() now returns a collection.
Any ideas as to how I could add the stores_count?
public static function boot(){
parent::boot();
static::addGlobalScope('products', function ($builder){
$builder->withCount('products');
$builder->withCount('stores'); <----- gives error
});
}
public function products(){
return $this->hasMany(Product::class);
}
public function stores(){
$store_ids = $this->products()->get(['store_id'])->unique();
return Store::find($store_ids);
}
Update after #Sandeesh answer.
I tried to use hasManyThrough but it returns an empty collection which is wrong.
When I dd($phone->products); I can see 7 products which have 3 different stores.
public function stores(){
return $this->hasManyThrough(Store::class, Product::class,
'store_id', 'id');
}
Database schema
Phone
-id
Product
-id
-phone_id
-product_id
-store_id
Store
-id
Update 2
So I managed to get the produced query from the stores() method above.
select `phones`.*,
(select count(*) from `products` where `phones`.`id` = `products`.`phone_id`) as `products_count`,
(select count(*) from `stores` inner join `products` on `products`.`id` = `stores`.`id` where `phones`.`id` = `products`.`store_id`) as `stores_count`
from `phones` where `slug` = ? limit 1
The problem is in the third line. The query is messed up, not sure what is wrong with the relationship though.
You can using hasManyThrough
https://laravel.com/docs/5.4/eloquent-relationships#has-many-through
public function stores()
{
return $this->hasManyThrough(Store::class, Product::class);
}
Edit
This should give you what you need. But eager loading is always better
protected $appends = [
'productCount',
'storeCount'
];
public function getProductCountAttribute()
{
return $this->products()->count();
}
public function getStoreCountAttribute()
{
return Store::whereIn('id', $this->products()->pluck('store_id')->toArray())->count();
}