Laravel Inserting nested Eloquent ORM model - php

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));

Related

How To Bulk Update Pivot Table For Same Attributes In Laravel Many To Many

I just found out that when i'm using sync() method for updating pivot table from many to many table, it was executing multiple query.
In my condition, i just trying to update same attributes for all related pivot table records.
This is my script :
foreach ($serials as $serial) {
$newSerials[$serial['id']] = ['warehouse_id' => 1];
}
$record->serials()->sync($newSerials)
Are there any other solution that can do update in pivot table in only one query shot in laravel with Eloquent way ?
I'm pretty sure Query Builder can handle this, but what i need to know in is Eloquent way, that maybe can combine with sync() method, because i alreay implemented sync() to the most of similar case like this in my project.
yes you can, using newPivotQuery() which make the query run directly on pivot table,
you should prepare the array you want to add, give it key value pairs according to your pivot fields names, something like:
foreach ($serials as $serial) {
$newSerials[] =
['serials_id'=>$serial->id,'record_id'=>$record->id,'warehouse_id' => 1];
}
$record->serials()->newPivotQuery()->insert($newSerials);
and there is another way, you can create model for the pivot table by inheriting from pivot, more details in:
https://laravel.com/docs/7.x/eloquent-relationships#defining-custom-intermediate-table-models
i must note that this code insert $newSerials, not updating them, if you want to update the record, method sync() is you best choice

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 Eager Loading, only when ids are available

I have a table like this:
payments
-id
-client_id
-vendor_id
-product_id
-item_id
-..._id
Using Laravel's eager loading, I can do this ->load('client', 'vendor' ... ) and load the relationships for all the returned rows of payments. But I've noticed that for example when there are no values in payments for vendor_id the eager loading query for the "vendor" relationship still happens, as below:
select * from `vendors` where `vendors`.`id` in ('')
The in ('') I understand should be filled with the values for vendor_id in the payments table, but since there were none the above query should not happen, right?! Is that a bug in Laravel?
The problem is that i got like 20 [x]_id columns in this payments table, and in each row only a couple (varies for each row) [x]_id columns have values, and I don't want to make 20 extra eager-loading queries, just the ones needed based on the actual existence of these ids!
When you are doing lazy eager loading, laravel does not know if records are present or not. If not present laravel returns empty collection.
If you want to filter out at the start itself to show only payments having vendors and clients etc, you can do :
$payments : Payment::whereHas('vendors')->whereHas('clients')->get();
Above will give you only payments which contain at-least one vendor and 1 client.
Also, you are using load() which is lazy-eager loading. It is designed so that you can get the primary collection first and later when and if you need, then only get the relation collection.
Check for with() if you do not want to lazy-eager load.
Also, if you want to avoid eager loading, you can go for join but again then you wont be using defined relations.

Eloquent Models with hasMany WHERE condition

I'm fairly new to Laravel 5.5 and Eloquent, so I'm struggling to work out how to do this using Eloquent objects.
If I was going to do this in SQL it would be pretty simple, but I'd prefer to deal in Eloquent objects.
I have two tables, Lots and Bids and I have the models that go with these.
Is there a way to get any Lot model where a specific user has bid on it (user_id column in the bids table), and then get the maximum bid amount that the user has bid?
Thanks to Devon, I used the whereHas to return the models that I needed. I then added another method to the model that returned the max bid price.

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.

Categories