Laravel - Nested Relationships - laravel

I'm learning Laravel and need to get a list of nested relationships. My foreign keys seem to be set up correctly.
products->servers->configs
Product Controller
$products = Product::with('servers')->get();
Product Model
public function servers()
{
return $this->hasManyThrough(Config::class, Server::class);
}
I'm only getting a list of servers that are the configs. Eg
products:{
id:1,
servers:[
ram:16gb //this is the config not the server
]
}
How can I get the list of configs inside the servers inside the products? Eg
products:{
id:1,
server:{
id:1,
name:'big server',
config:{
ram:16gb
}
}
}

In Product Modal Use hasMany Method
public function servers()
{
return $this->hasMany(Server::class);
}
In Server Modal Use hasMany(for many rows get) or hasOne(for single row get) method
public function configs()
{
return $this->hasMany(Config::class);
}
public function config()
{
return $this->hasOne(Config::class);
}
Now In ProuctController see how to get nested relationship data
$products = Product::with('servers.configs')->get();

Related

laravel call to undefined method addEagerConstraints()

I have two models
Post.php
id
post
show_id
type = 'movie' or 'tv'
Show.php
id // this only auto increment counter ids
show_id
show_type = 'movie' or 'tv'
the thing is show can be either tv or movie and may two with the same show_id for exmaple one tv could have a show_id of 10 and also one movie can have it but the types are diffrent
i have in post model
public function show(){
return $this->belongsTo('App\Show', 'show_id');
}
in show model
public function post(){
return $this->hasMany('App\Post', 'id');
}
this relationship get the first show with matching show id it sees, wheather its a movie or tv, i want to restrict it to match type column on both sides
post.php:
public function show() {
return $this->belongsTo('App\Show', 'show_id', 'show_id')
->where('type', $this->type);
}
show.php
public function posts() {
return $this->hasMany('App\Post', 'show_id', 'show_id')
->where('type', $this->show_type);
}
UPDATE (the code above does not work!)
Trying to use where clauses (like in the example below) won't work when eager loading the relationship because at the time the relationship is processed $this->f2 is null.
Read more here: Compoships
I just came accross a package https://github.com/topclaudy/compoships
what it does it allows creating relationships based on more than one FK, which laravel doesnt support by default
I think what you're looking for is a polymorphic relation. Instead of having a model that may be one of two "types" there should probably be two separate models on the same relation. For example:
class Post
{
public function Show()
{
return $this->morphTo();
}
}
class TvShow
{
public function Post()
{
return $this->morphMany('App\Post', 'show');
}
}
class Movie
{
public function Post()
{
return $this->morphMany('App\Post', 'show');
}
}
Then your posts table would have a show_id and show_type field that would relate to either a tv show or movie. Check out the docs for more info, I'm not sure of the rest of your project so I'm not 100% this will fit but anytime you start putting "_type" fields in your table you should question whether or not you should be using a polymorphic relation. This will also likely keep your models cleaner and free of a bunch of if statements as you realize there are other differences between movies and shows and how you handle them.
https://laravel.com/docs/5.7/eloquent-relationships#polymorphic-relations

Laravel - How to merge relationship results?

how can i merge laravel relationship results in to one object?
MyController.php
public function getUser($id) {
return TournamentUser::where('customer_id','=', $id)->with('user')->get();
}
MyModel.php
public function user() {
return $this->hasOne('App\Models\Customer','customer_id', 'customer_id');
}
returning result :
[{
"id":1,
"tournament_id":3,
"customer_id":2016827550,
"point":0,
"user":{
"id":1,
"customer_id":2016827550,
"nickname":"OMahoooo"
}
}]
as expected query returns user section as array but i expect result like this :
[{
"id":1,
"tournament_id":3,
"customer_id":2016827550,
"point":0,
"nickname":"OMahoooo"
}]
only get nickname section without user array. I hope you understand me.
Sorry for my english , have a nice day
You can load the data and remap it manually using map() method. I've just tested this code and it works perfectly with hasOne() relationship:
$users = TournamentUser::where('customer_id', $id)->with('user')->get();
$users->map(function ($i) {
$i->nickname = $i->user->nickname; // Set related nickname.
unset($i->user); // Delete user data from the collection.
return $i;
});
Alternatively, you could use an accessor, but in this case, you'll meet the N+1 problem.

How to setup conditional relationship on Eloquent

I have this (simplified) table structure:
users
- id
- type (institutions or agents)
institutions_profile
- id
- user_id
- name
agents_profile
- id
- user_id
- name
And I need to create a profile relationship on the Users model, but the following doesn't work:
class User extends Model
{
public function profile()
{
if ($this->$type === 'agents')
return $this->hasOne('AgentProfile');
else
return $this->hasOne('InstitutionProfile');
}
}
How could I achieve something like that?
Lets take a different approach in solving your problem. First lets setup relationship for the various models respectively.
class User extends Model
{
public function agentProfile()
{
return $this->hasOne(AgentProfile::class);
}
public function institutionProfile()
{
return $this->hasOne(InstitutionProfile::class);
}
public function schoolProfile()
{
return $this->hasOne(SchoolProfile::class);
}
public function academyProfile()
{
return $this->hasOne(AcademyProfile::class);
}
// create scope to select the profile that you want
// you can even pass the type as a second argument to the
// scope if you want
public function scopeProfile($query)
{
return $query
->when($this->type === 'agents',function($q){
return $q->with('agentProfile');
})
->when($this->type === 'school',function($q){
return $q->with('schoolProfile');
})
->when($this->type === 'academy',function($q){
return $q->with('academyProfile');
},function($q){
return $q->with('institutionProfile');
});
}
}
Now you can access your profile like this
User::profile()->first();
This should give you the right profile. Hope it helps.
you can do this by use another method please check this:
a blog Post and Video model could share a polymorphic relation to a
Tag model. Using a many-to-many polymorphic relation allows you to
have a single list of unique tags that are shared across blog posts
and videos. First, let's examine the table structure:
https://laravel.com/docs/5.4/eloquent-relationships#many-to-many-polymorphic-relations
Looks like that should be $this->type rather than $this->$type - since type is a property, not a variable.

How to return two one to many relationships in an API response with Laravel controller?

So, I have three models - Events, Groups and Individuals. I'm trying to create an API response that provides a list of groups AND the individuals in each group. I've been able to create a response that provides a list of the groups, but I'm not sure how to also PROPERLY include the individuals of each group.
I thought about just looping through each from the first response and making another query for each, but that seemed like overkill. So, the question is how can I include the Individuals for each Group within the response as well.
I have all three eloquent models defined as such:
Events:
public function groups()
{
return $this->hasMany('App\Group');
}
Groups:
public function event()
{
return $this->belongsTo('App\Event');
}
public function individuals()
{
return $this->hasMany('App\Individual');
}
Individuals:
public function group()
{
return $this->belongsTo('App\Group');
}
Then I have my route:
Route::get('/events/{id}/groups', 'EventsController#groups');
Then that controller has the function to return the list of groups:
public function groups($eventId)
{
$event= Event::find($eventId);
$groups= $event->groups()->paginate();
return $groups;
}
Any help would be greatly appreciated. To clarify, I'd be looking for a response that looks something like:
{
id: 1,
group_name: 'Group Name',
individuals: [ .. array of individuals .. ]
}
Or if someone could give feedback on a better or more standard way to do it, I'd be open to that as well.
You need to use Eager Loading with the with() method:
public function groups($eventId)
{
$event= Event::find($eventId);
$groups= $event->groups()->with('individuals')->paginate();
return $groups;
}
Since you already know event ID, you can use Group model directly and load individuals by using with() method. This code will create just two queries and it's less verbose:
public function groups($eventId)
{
return Group::where('event_id', $eventId)->with('individuals')->paginate(5);
}

Eager loading related model using query scope

Say I have a model Box which holds many widgets. The widgets can be active or inactive (boolean). The Widget model has a query scope which can filter results:
models/box.php:
class Box extends Eloquent
{
public function widgets()
{
return $this->hasMany('Widget');
}
}
models/widget.php:
class Widget extends Eloquent {
public function box()
{
return $this->belongsTo('Box');
}
public function scopeActive($query)
{
return $query->whereActive(true);
}
}
Query scopes make it easy to get all widgets for a given box:
$box_widgets = Box::find($box_id)->widgets()->active()->get();
// returns an Eloquent\Collection containing a filtered array of widgets
But how can I use scopeActive to eliminate this eager loading with method's conditional function?
$boxes = Box::with(array('widgets', function ($q)
{
$q->active();
}))->get();
It seems like there's probably a shorthand for accessing a relation's scope, something like Box::with('widgets->active') or Box::with('widgets.active') but I haven't been able to find it.
Suppose most of the time you want only active widgets, so I suggest:
public function widgets()
{
return $this->hasMany('Widget')->whereActive(true);
}
public function widgetsDisabled()
{
return $this->hasMany('Widget')->whereActive(false);
}
You can setup up more, for example for loading all at once, like you have now.
Then eager load as easily as that:
Box::with('widgets')... // loads only active

Resources