Laravel 5.1: Pagination on Models with hasManyThrough() - php

I have "Brand" hasManyThrough "MarketProduct" through "Product". So,
Brand >> Product >> MarketProduct
where >> represents a hasMany relationship. I'm able to get a collection (Illuminate\Database\Eloquent\Collection) of MarketProductsas below using the hasManyThrough() method defined in Brand.
$collection = Brand::find($someId)->marketProducts;
The issue is that $colletion is not an instance of the Query Builder or Eloquent, so I can't use ->paginate($num). Is it possible to use the default pagination feature when using hasManyThrough, or do I need build the query manually to use the pagination?

If you look at the document, it mentions
...since all relationships also serve as query builders, you can add
further constraints to which comments are retrieved by calling the
comments method and continuing to chain conditions onto the query...
So the solution is:
$collection = Brand::find($someId)->marketProducts();

Related

Calling relations on other relations of model yii 1.1

I have 3 model Price, Description and Product
the Description model has relationship on Price model and Price model has relationship on Product.
Can I call the product when I try to get it on Description model using the relationship of Price?
here's what I'm trying to do
$description = Description::model()->with('Price')->findAll();
// after that I want to call the product which is on price.
they're all connection using their id. product_id, description_id and price_id
How can I achieve this? is there a way?
First of all, JFYI when you use with() method to establish join part from ActiveRecord's query builder, please just double check also the relations() method from the specific AR model to see the actual defined relations. I pointed to this because defining relations into model and access them in later stage via query builder is case-sensitive.
Back to your question.
If you have already fetched your Description model joined eagerly with() your Price model, when you call $description->price->product, behind the scenes ActiveRecord will perform the so-called lazy-loading approach.
According to Yii 1.1 docs -> https://www.yiiframework.com/doc/guide/1.1/en/database.arr :
Performing Relational Query
The simplest way of performing relational query is by reading a relational property of an AR instance. If the property is not accessed previously, a relational query will be initiated, which joins the two related tables and filters with the primary key of the current AR instance. The query result will be saved to the property as instance(s) of the related AR class. This is known as the lazy loading approach, i.e., the relational query is performed only when the related objects are initially accessed.
The example below shows how to use this approach:
// retrieve the post whose ID is 10
$post = Post::model()->findByPk(10);
// retrieve the post's author: a relational query will be performed here
$author = $post->author;
Hope this helps.

Is there any way to avoid these $with models from loading whenever loading the parent in Laravel 5.7 Eloquent?

I have a laravel application in which I have Models using $with, in the frontend whenever I load this Model it loads its child's $with as well. Can you guys tell me how to avoid that?
Below is an example for you guys to understand the scenario.
ModelA.php
$with = [ModelK, ModelL, ModelM];
ModelK.php
$with = [ModelM, ModelN];
ModelM.php
$with = [ModelX, ModelY, ModelZ];
In my frontend, wherever I use ModelA with eloquent, it makes query on ModelM, ModelX both. This is making my site slow. Also if I retrieve 4-5 rows data from ModelA then it also make runs queries on all these models ModelM, ModelX and that too one query for each row. So the number of queries is also increase.
The relationship between these models are One-One, One-Many and Many-Many.
Is there any way to avoid these child Models whenever loading the parent?
If you are using the $with property in your models, you can disable the eagerLoading within a query with the following:
ModelA::setEagerLoads([])->first();

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.

Laravel Inserting nested Eloquent ORM model

Suppose I have three tables with relationships like below:
product
has many options
option
can belong to many product
has option_values
option_value
belongs to many option
Using Laravel I can update option like this
product->options()->insert($stuff);
However I want to insert into the option_value as well.
I tried
product->options()->values()->insert($otherStuff);
But that doesnt work. How can this be done with Eloquent ORM?
You can't access a sub-relationship this way, you have to actually have the model result and call the method on it, or use eager loading but eager loading does not help with an insert.
You would have to loop the option models and insert trough them, but that results in way too many insert queries. Better may be to just grab the related ids and run the insert in a single fluent query instead of many Eloquent.
$option_ids = $product->options()->lists('id');
DB::table(OptionValue::$table)->insert(array_merge($data, $option_ids));

Categories