Query More than one relationship from a model with() in Laravel - php

I need help to query a model base on its relationship:
I have a model called StoreStock, this model is related to a Product model, which has two model relationships, MasterList and Price.
I want to get all storeStock with two Product relationships.
I know i can do something like
StoreStock::all()->with('product.price')
->get()
With this, i can only pick either price or masterlist

Pass array of relationship to with method
StoreStock::with(['product.price','product.masterlist']) ->get()

A little bit explanation here, many of the Laravel methods which support string, also support arrays. You can hover over the specific method and get the intellisense. In your case, it can be written like:
StoreStock::with(['product.price','product.masterlist'])->get()
If you want to run a specific action over any specific relation, you can also write it like this:
StoreStock::with(['product.price' => function(Builder $query) {
// your action required with the query
},'product.masterlist']) ->get()
Hope someone finds this helpful

Related

How to get products based on a count on a field in this table with Laravel

I'm working on Laravel 5.8
Let's say we have a table products like this:
id | product_type_id |...
1 |______1_______|...
2 |______2_______|...
3 |______2_______|...
4 |______3_______|...
I would like to know how to get the all the products which "share" a product type.
In other words, I would like to get all the products except those whose product_type_id is unique in the table.
I know who to do it in a foreach loop but I would like to take advantage of the resources of using Laravel.
Thanks in advance.
The Laravel way of doing it would be using Eloquent relationships along with has() and whereHas(), like this:
$products = Product::whereHas('type', function ($builder) {
$builder->whereKey(Type::has('products', '>=', 2)->pluck('id'));
})->get();
I'm assuming you have defined Product and Type models and connected them together:
Product belongsTo Type
Type hasMany Product
If I'm understanding your question correctly you could do something like this.
First, make sure your ProductType model has a products relationship defined.
(I assume it does based on your Products table.)
Then you can query based on a relationship count using the Eloquent model method has.
Example:
ProductType::with('products')->has('products', '>', 1)->get();
The with('products') is optional. It simply grabs the Products at the same time to avoid additional queries. The whereHas method also works but is really only necessary if you need to filter the relationship based on more complex parameters.
You could also use the has/whereHas method inside the Product model using an inverse (ie belongsTo) relationship to get the same thing but inverted. It really depends on how you want the data presented to you.
Example:
Product::whereIn(
'product_type_id',
ProductType::has('products', '>', 1)->pluck('id')
)->get();
To sum it all up the first way will give you:
ProductType => Product
While the second example will give you:
Product => ProductType
See the related Laravel documentation for more info.
Hope that helps!

How can this be done with the relationship, and is it worth it? (Get all departments for clinic)

I have 3 tables:
clinics
departments
clinics_in_departments
Using Query Builder:
$department = ClinicsInDepartment::whereIn('clinic_id',[1,2,3])
->join('departments', 'clinics_in_departments.department_id', '=', 'departments.id')
->get();
How can this be done with the relationship, and is it worth it?
If you look at the documentation of Laravel at the Many to Many section https://laravel.com/docs/5.6/eloquent-relationships#many-to-many it's already explained in there. If you're planning to keep using Laravel I would recommend using the best practises of Eloquent. It's easier to understand and read for other developers. It's always worth to make your product the best you can. It also gives possibilities to quickly extend and maintain your application.
All you need to do is to define a relationship in your model clinics
// second, third and fourth parameter could also be optional
function departments(){
return $this->belongsToMany('App\Clinics', 'clinics_in_departments', 'department_id', 'clinic_id');
}
To retrieve the data you can use
$clinics = Clinics::with('departments')->get();
// this would hold a list of departments for each clinic
To get exactly the same data extend the query to this
$clinics = Clinics::with('departments')->whereIn('clinic_id',[1,2,3])->get();
Because it's a Many to Many relationship you could also define a relationship for the model Departments and do exactly the same as mentioned above.
You can define a belongs to many relation inside Clinics model like below code
function departments(){
return $this->belongsToMany('App\Clinics', 'clinics_in_departments');
}

Laravel model object retrieving relation model with where condition without "get" method

I'm working on octoberCMS(laravel framework), I have a doubt on retrieving relation model on where clause.
I have fetched a clothing_type record from "clothing type" model based on its primary key "id".
$clothing_type = ClothingType::where('id',post('clothingtype_id'))->where('status','Active')->first();
This "clothing type" model is related with "products" model, the relation is => each clothing type hasMany products.
Every thing works fine; Now my business logic has two cases, one is to get all the products of the clothing type and another is to get the first product of the clothing type. So I have used the $clothing_type->products to get all the products and $clothing_type->products->first() to get the first product.
Now I have to apply a condition for both the cases. The condition is that only the product whose status is "Active" should be fetched, hence
$products = $clothing_type->products->where('status','Active'); and$first_product_detail = $products->first();.
Every thing works as expected but how come the products are fetched without "get()" method. $clothing_type->products->where('status','Active')->get(); Since I'm new to relation I want to know how this works or is this a bad way to get records or improper assumption. But every thing works good.
$clothing_type = ClothingType::where('id',post('clothingtype_id'))->where('status','Active')->first();
if(count($clothing_type->products))
{
$products = $clothing_type->products->where('status','Active');
$first_product_detail = $products->first();
}
You are doing it the correct way. When you access the relationship as an attribute Eloquent automatically retrieves the records.
However, if you access the relationship as a method, you get the query itself, to which you can add your filters:
if(count($clothing_type->products))
{
$products = $clothing_type->products()->where('status','Active')->get();
$first_product_detail = $products->first();
}
This would solve your problems
(documentation is over here (see the first item))
Edit: Also note that the first method is not a method of Eloquent, but from Collection, which is pretty powerful!
Edit2:
I misread the part of your question where you want to know HOW this is possible. Both Eloquent and Collections have a where method. I assume you understand the working of the Eloquent one, but the one from Collection is pretty much the same (see documentation on the Collection where here)
I prefer the Eloquent one myself, because that limits the amount of records that is retrieved from the database. But if you need all the products (even the inactive ones) later on in your code, just use the Collection method to filter the active ones out
There is nothing to be afraid of...
first() and where()
are functions of both Illuminate\Database\Query\Builder as well as Illuminate\Support\Collection and all first does is limit the records to take 1 and then give you the first record. When you use Builder a query is made to get 1 record and 1 you use it on a collection, all records are first get() and then the first of those records is returned.
Here,
When you do,
$clothing_type->products, Laravel gives you a collection of products...
So...
$products is an object of Illuminate\Support\Collection
and
$products->first() calls for the first() function in that class.
Documentation on where and first methods of a collection...

laravel Eloquent join and Object-relationship mapping

Ok so i'm kind of newish to eloquent and laravel (not frameworks tho) but i hit a wall here.
I need to perform some queries with conditions on different tables, so the eager load (::with()) is useless as it creates multiples queries.
Fine, let use the join. But in that case, it seems that Laravel/Eloquent just drops the concept of Object-relationship and just return a flat row.
By exemple:
if i set something like
$allInvoicesQuery = Invoice::join('contacts', 'contacts.id', '=', 'invoices.contact_id')->get();
and then looping such as
foreach ($allInvoicesQuery as $oneInvoice) {
... working with fields
}
There is no more concept of $oneInvoice->invoiceFieldName and $oneInvoice->contact->contactFieldName
I have to get the contacts fields directly by $oneInvoice->contactFieldName
On top of that the same named columns will be overwrited (such as id or created_at).
So my questions are:
Am i right assuming there is no solution to this and i must define manually the field in a select to avoid the same name overwritting like
Invoice::select('invoices.created_at as invoice.create, contacts.created_at as contact_create)
In case of multiple joins, it makes the all query building process long and complex. But mainly, it just ruins all the Model relationship work that a framework should brings no?
Is there any more Model relationship oriented solution to work with laravel or within the Eloquent ORM?
Instead of performing this join, you can use Eloquent's relationships in order to achieve this.
In your Invoice model it would be:
public function contact(){
return $this->belongsTo('\App\Contact');
}
And then of course inside of your Contact model:
public function invoices(){
return $this->hasMany('\App\Invoice');
}
If you want to make sure all queries always have these active, then you'd want the following in your models:
protected $with = ['Invoice']
protected $with = ['Contact'];
Finally, with our relationships well defined, we can do the following:
$invoices = Invoice::all();
And then you can do:
foreach($invoices as $invoice)[
$invoice->contact->name;
$invoice->contact->phone;
//etc
}
Which is what I believe you are looking for.
Furthermore, you can find all this and much more in The Eloquent ORM Guide on Laravel's site.
Maybe a bit old, but I've been in the same situation before.
At least in Laravel 5.2 (and up, presumably), the Eloquent relationships that you have defined should still exist. The objects that are returned should be Invoice objects in your case, you could check by dd($allInvoiceQuery); and see what the objects are in the collection. If they are Invoice objects (and you haven't done ->toArray() or something), you can treat them as such.
To force only having the properties in those objects that are related to the Invoice object you can select them with a wildcard: $allInvoicesQuery = Invoice::select('invoices.*')->join('contacts', 'contacts.id', '=', 'invoices.contact_id')->get();, assuming your corresponding table is called invoices.
Hope this helps.

Retrieve nested relationships for a model instance in Laravel's Eloquent

My scenario is this:
I have a Course model
Each Course can have many CourseTopics, through a topics() relationship
Each CourseTopic can have many Lessons, through a lessons() relationship
Is there a compact way to retrieve and handle all the Lessons associated with a single Course (for example, to list them, or to count their total number)?
My aim would be to have a very brief syntax to use in Blade templates; I don't want to involve logic (or at least, keep it to a bare minimum) or raw SQL queries into my template.
What I've tried:
$course->with("topics.lessons")
where $course is the current instance of the course in a template, doesn't work (gives to me all the courses with all their topics and lessons).
EDIT:
A solution is to define a hasManyThrough() relationship like:
$this->hasManyThrough("Lessons", "CourseTopics");
This solves the problem for a 2-level nested relationship. How about a 3-level instead?

Categories