Can "for" relations in a laravel seeder use the child model? - php

I'm writing a seeder in which one model (ProductVariant) belongs to (many to one) other model (Product). For properly seeding all relations on the Product, I need to access the ProductVariant model. The Laravel documentation shows this is possible with the ->has() function, but because of the nature of this relationship I need to use a ->for(), which doesn't seem to work in the same way.
Below is the snipped of code I'm talking about and how I would expect it to work:
ProductVariant::factory()
->for(
Product::factory()
->state(function (array $attributes, ProductVariant $productVariant) {
dd(($attributes, $productVariant);
})
)->create();
Is there another way to do what I need it to do or am I missing something?
Documentation I'm referring to: https://laravel.com/docs/9.x/database-testing#has-many-relationships

Related

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

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

Laravel model relation - change to mutator, without chaning anything in cotrollers

I have 2 tables, estimates and models.
There's a 1-to-1 relation between the two.
I want to move model names from the models table to the estimates table. That will be done with a script that I will have to write myself.
The way this database was set up was wrong, for many reasons I don't need to specify here.
The models table has only 2 columns - id and name.
I access this relation in a lot of controllers, and views all over my app :
$estimate->model_info->name
So I would keep both the models table (with no records) and the Model.php model, keep the old code in the controllers and views, but the new code of accessing model names would be just :
$estimate->name
In the Estimate.php model I have this relation :
public function model_info() {
return $this->hasOne('App\Models\Model', 'id', 'model_id');
}
How can I just change this relation into a mutator, so the old way of accessing model names and the new way would work at the same time?
I've tried the withDefault() callback method with no luck, it returns an empty value :
public function model_info() {
return $this->hasOne('App\Models\Model', 'id', 'model_id')
->withDefault([
'name' => $this->attribute->name
]);
}
Do I have to update my code in all controllers and views or if there's an easier way to do this?

Laravel relationship eager loading limiting all results instead of model specific

I have a nested relationship like:
Model One has many Model Twos which has many Model Threes.
The aim is to get all Model Ones and all their Model Twos and for the each of the Model Twos only get the latest Model Three.
I have tried:
return ModelOne::with(['modelTwos.latestModelThree'])
->paginate();
But this is only giving the Model Three for the last Model Two of the last Model One.
All other Model Twos have no Model Three in the response.
The method for latestModelThree on the Model Two class is:
public function latestModelThree()
{
return $this->hasMany(ModelThree::class)
->latest()
->limit(1);
}
Changing larestModelThree() relationship to hasOne() and removing the limit should do the trick:
public function latestModelThree()
{
return $this->hasOne(ModelThree::class)
->latest();
}
It's a little hacky, and don't solve the problem if you need N items of that relationship instead of a single item, but, solves your problem.
The answer by Elias Soares does work but causes major performance issues with large datasets.
A more performant solution is to use this package: https://github.com/staudenmeir/eloquent-eager-limit

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.

how to implement sub model in Laravel

my base class is post
and many submodel such as : video post , image post
all class have specific attribute & inherit parent attrib
& all class need specific behaviors
Problem
when find on post model elequent give super model(post) instance, its wrong
i need instance of submodel
If I understood you correctly, you need relationships
Add a hasMany relationship to your Post.php model:
public function videos()
return $this->hasMany(App\PostVideo::class);
}
As long as your post_video table has a post_id column that references a post, you can call this relationship like this:
foreach($post->videos as $video) {
// Do something
}
And the inverse relationship:
Add a relationship to your PostVideo.php model:
public function post() {
return $this->belongsTo(App\Post::class);
}
And of course, if you have a video, you can access the post it belongs to by doing:
$video->post
It is looked like you want a single table inheritance. In laravel this could be done manually or use package like nanigans or intrip. To use single table inheritance manually, i could suggest you start with reading this stackoverflow question first. However, notice that single table inheritance put everything in a single table but refered by several models that have different behavior. If this is not what you want, just use simple eloquent queries and models - which already explained by Pistachio.

Categories