eloquent many to many shows double results - laravel-4

I have two models User and Afdeling('department'). Between them is a many-to-many relationship.
In my junction table i have two entries:
Afdeling_id | user_id
----------------------
3 | 45
4 | 45
Now i want to display both departmens that are connected with id 45.
Now in my controller i do:
$afdelingslijst = User::find(45)->afdelingen;
In my view i have
<?php
foreach($afdelingslijst as $item) {
echo $item->pluck('afdelingen');
}
?>
As output i see "department1 department1". This should be "department1 department2".
Why could this be happening?

It's because you're using "pluck". You should access it as if you're accessing an object's attribute, like so:
echo $item->afdelingen;
The definition of pluck is to "Pluck a single column from the database".

Related

Confused with Laravel's hasOne Eloquent Relationships

I have a new Laravel 5.8 application. I started playing with the Eloquent ORM and its relationships.
There is a problem right away that I encountered.
I have the following tables. (this is just an example, for testing reasons, not going to be an actual application)
Login table:
--------------------------
| id | user | data_id |
--------------------------
| 1 | admin | 1 |
| 2 | admin | 2 |
| 3 | admin | 3 |
--------------------------
Data table:
--------------
| id | ip_id |
--------------
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
--------------
IP table:
----------------------
| id | ip |
----------------------
| 1 | 192.168.1.1 |
| 2 | 192.168.1.2 |
| 3 | 192.168.1.3 |
----------------------
What I wanted is to get the IP belonging to the actual login.
So I added a hasOne relationship to the Login table that has a foreign key for the Data table:
public function data()
{
return $this->hasOne('App\Models\Data');
}
Then I added a hasOne relationship to the Data table that has a foreign key for the IP table:
public function ip()
{
return $this->hasOne('App\Models\Ip');
}
Once I was done, I wanted to retrieve the IP address for the first record of the Login table:
Login::find(1)->data()->ip()->get();
But I get this error:
Call to undefined method Illuminate\Database\Eloquent\Relations\HasOne::ip()
What am I missing here and how can I get the IP of that login in the correct way? Do I need a belongsTo somewhere?
1st error: Wrong relationship definition
Laravel relationships are bi-directional. On one-to-one relationships, you can define the direct relationship (HasOne) and the inverse relationship (BelongsTo)
The direct relationship should be:
HasOne HasOne
[ Login ] <----------- [ Data ] <----------- [ IP ]
And the inverse relationship should be:
BelongsTo BelongsTo
[ Login ] -----------> [ Data ] -----------> [ IP ]
See Eloquent: Relationships - One-to-One docs for details on how defining it.
Note that you don't need to define both directions for a relationship unless you need it. In your case, I think you just need to define the belongsTo direction.
2nd error: You are calling the relationship method, not the relationship itself
When you do:
Login::find(1)->data()->ip()->get();
You are calling the method data that defines your relationship, not the related model. This is useful in some cases, but not on your case.
The correct is call the relationship magic property instead:
Login::find(1)->data->ip;
Note that we don't use the () and we do not need the get() here. Laravel takes care of loading it for us.
Use Eager Loading
Laravel Eloquent have a Eager Loading for relationships that's very useful in some cases because it pre-loads your relationships, and reduce the quantity of queries you do.
In the situation that you described (loading a single Login model) it doesn't make any performance improvement, but also it doesn't slow down.
It's useful when you load many models, so it reduces your database query count from N+1 to 2.
Imagine that you are loading 100 Login models, without eager loading, you will do 1 query to get your Login models, 100 queries to get your Data models, and more 100 queries to get your Ip models.
With eager loading, it will do only 3 queries, causing a big performance increase.
With your database structure:
Login belongsTo Data
Data hasOne Login
Data belongsTo IP
IP hasOne Data
After fixed your methods you can use of your relations like this
$login = Login::with(['data.ip'])->find(1);
You can try like this:
Login
public function data()
{
return $this->belongsTo('App\Models\Data', 'data_id');
}
Data
public function ip()
{
return $this->belongsTo('App\Models\Ip', 'ip_id');
}
$login = Login::with(['data.ip'])->find(1);
And inside data you will have ip like $login->data->ip.

Laravel Foreach Repeating in View

So, I have a form that has multiple text inputs. When the user posts info using these inputs it's put into my model with the user's ID, a "type ID", and the input from the user. So I'll end up with something like this in the database.
ID User_ID Type_ID Type_Details
1 1 1 yada
2 1 1 foo
3 1 1 bar
In my view I do this.
#foreach ($user->UserType as $type)
What happens is that in the view I then get the output 3 times, since it sees "type_id" for user #1. For reference, this table shown is a pivot table joining the user model and the type model. I simply added another column to retrieve the type_details information.
Ultimately, since there is only one Type_id (1), I want to only show that type once with the "yada", "foo", "bar". Not 3 times.
Thanks!
Instead of using a foreach, you can group the types using Collections:
$types = $user->UserType->implode('Type_Details', ', ')->toArray();
Then you can display in your view as:
{{ $types }}

Laravel: get Many-to-Many column data

Yo all,
I have a users relationship pivot db table as follows:
id | user_id | relation_id | relationship
1 4 2 tutor
1 4 3 parent
The table relates user with one-and-other for various reasons.
I am trying to get the relationship column within the $user. I have managed to pull the related users
details no problem - $user->relations.
However, I just need to get the relationship - eg. Tutor or parent.
I am getting no dice with $relative->pivot->relationship
Any ideas? Thanks for taking the time to help.
#foreach($user->relations as $index=>$relative)
{{ $relative->first_name . ' ' . $relative->last_name}}
{{ $relative->pivot->relationship }}
#endforeach
To access ->pivot->whatever you need to add withPivot('whatever') to the relation definition:
public function relations()
{
return $this->belongsToMany('Relation')->withPivot('relationship');
}
Note: I wouldn't use relations and Relation names here, since it's misleading AND it may collide with Eloquent\Model stuff.

Joining an additional table with belongsToMany()?

This question is best illustrated by an example:
users
id
name
roles
id
name
role_user
user_id
role_id
rank_id
group_id
...
ranks
id
name
groups
id
name
I can easily eager load a users table by specifying the following relationship in my User.php model:
public function roles() {
return $this->belongsToMany('Role');
}
Which will output the table below when calling User::with('roles'):
User | Role
-------------
Jon | Admin
Jan | Mod
However I have no idea how to extend this to include:
User | Role | Rank | Group
-----------------------------
Jon | Admin | Boss | Blue
Jan | Mod | Minion | Red
What I've tried doing User::with('roles', 'ranks', 'groups') but that is certainly wrong since I'm telling Laravel there are rank_user and group_user intermediate tables too but there aren't. What is the correct way?
PS: I know it's better to separate the ranks and groups into their own relationship/pivot tables, this is simply an example.
EDIT: Closest example I can find for this: https://github.com/laravel/framework/issues/2619#issuecomment-38015154
You can just treat your model's relations methods as ordinary queries and build upon them:
public function roles() {
return $this->belongsToMany('Role')
->join('role_user', 'role_user.role_id', '=', 'roles.id')
->join('ranks', 'ranks.id', '=', 'role_user.rank_id')
->join('groups', 'groups.id', '=', 'role_user.group_id');
}
Relations queries like the above are not so intuitive to understand when they get too complex, so it may be better to rethink database design, but in theory it's possible to manipulate them.

one to many save, codeigniter DataMapper

Working on a site where a user can add videos. Each video can be in many sections. It can also have many questions. Video has a one-to-many relationships with both the question and section classes.
I'm getting the section and video classes like this:
$s = new Section();
$s->where('section', $this->post->section)->get();
then saving like this:
$v->save($u, $s, $q);
where $v is a video object, $u is a user object and $q is a question object.
I want to allow the user to POST multiple questions and sections. How do I save those relationships. Should $s and $q be arrays of objects?
I am not 100% sure that I understand what you mean but yes yu can save multiple relations at the same time.
Like this:
$s = new Section();
$s->where_in('section', $array_with_sections_ids)->get();
$v->save(array($u, $s->all, $q));
You use different tables in your database. Like Video, Question and VideoQuestion where you store the video ID with the different Question id's
Then VideoExample would look like:
| video_id | question_id |
--------------------------
| 1 | 1 |
| 1 | 5 |
...
for inserting this data, first insert the video and get its ID with
$videoId = $this->db->insert_id()
Do the same for your question and you've got your ID's ;)
The same for the other tables and you're done. Good Luck!

Resources