Laravel Relationship - Additional fields in pivot table linking to additional models - php

I have simplified the problem to get to the point. I have three tables, users, roles, account.
Normally I would set up the User model to have a many to many relationships with roles but I want those roles to be specific to each account. So I have added an additional field to the pivot table. Here are the tables and fields that I have;
‘users’ table
|—————————
| id | name |
|—————————
| 1 | Bob |
| 2 | Jim |
| 3 | Fred |
|—————————
‘roles’ table
|—————————
| id | title |
|—————————
| 1 | Administrator |
| 2 | Manager |
| 3 | Approver |
|—————————
‘accounts’ table
|—————————
| id | name |
|—————————
| 1 | ABC Company |
| 2 | XYZ Shipping |
| 3 | KLM Transport |
|—————————
I then have the pivot table role_user with an additional pivot field for the account;
|—————————
| role_id | user_id | account_id
|—————————
| 1 | 3 | 1
| 2 | 2 | 1
| 3 | 2 | 3
| 3 | 1 | 2
|—————————
I have used the withPivot function on the belongsToMany function when setting up the many to many relationships. This allows me to get the information using $user->roles->pivot->account_id but what I need is to be able to get the name of that company. All it’s passing to the blade template is the id from the pivot table and not linking that to an actual Account model.
Is there a way with Eloquent to get this entire model in the same way as the original relationship?

Create a Custom Pivot Model
use Illuminate\Database\Eloquent\Relations\Pivot;
class RoleUserAccountPivot extends Pivot
{
public function user()
{
return $this->belongsTo(User::class);
}
public function role()
{
return $this->belongsTo(Role::class);
}
public function account()
{
return $this->belongsTo(Account::class);
}
}
Update your belongsToMany relationships
Bellow is an example with the User::roles relationship
class User //extends...
{
public function roles()
{
return $this->belongsToMany(Role::class, /*other parameters*/)->using(RoleUserAccountPivot::class)->withPivot('account_id');
}
}
Usage
$user->roles->first()->pivot->account // returns Account model
Hope it helps.
Reference links:
Laravel doc on custom pivots

Related

How to create a Pivot table in Laravel

I have a sponsorship app I'm creating and trying to get a pivot table working between my users and kids, but when I have a user sponsor a kid, the pivot table kid_user is not populated with the kid_id or the user_id. Not sure what I'm missing.
A Kid can have as many users (sponsors) as their slots allow them.
Users can sponsor a kid, multiple kids or the same kid multiple times
if needed.
Here is my kid_user pivot table:
public function up()
{
Schema::create('kid_user', function (Blueprint $table) {
$table->integer('kid_id')->unsigned()->index();
$table->foreign('kid_id')->references('id')->on('kids')->onDelete('cascade');
$table->integer('user_id')->unsigned()->index();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->primary(['kid_id', 'user_id']);
});
}
Then on the User model: The user can have many kids...
public function kid()
{
return $this->hasMany(Kid::class);
}
Then on the Kid model: The kids can have many users...
public function user() {
//return $this->belongsTo(User::class); (Tried this..)
return $this->hasMany(User::class);
}
Here are the tables I'm using. (Only relevant info is included in the tables)
+---------------------+
| Tables_in_Database |
+---------------------+
| kid_user |
| kids |
| users |
+---------------------+
+-----------------------------+
| users table |
+----+------------+-----------+
| id | first_name | last_name |
+----+------------+-----------+
| 1 | John | Smith |
| 2 | Jane | Doe |
+----+------------+-----------+
+-----------------------------+
| kids table |
+----+------------+-----------+-------+
| id | first_name | last_name | slots |
+----+------------+-----------+-------+
| 1 | Bobby | Little | 3 | -> Can be sponsored 3 times
+----+------------+-----------+-------+
+--------------------+
| kid_user table | THE BELOW RESULTS ARE WHAT I'M LOOKING FOR.
+--------------------+
| *kid_id | *user_id | * = Primary Key
+---------+----------+
| 1 | 1 | -> Sponsored 1st Slot
+---------+----------+
| 1 | 2 | -> Sponsored 2nd Slot
+---------+----------+
| 1 | 1 | -> Sponsored 3rd Slot
+---------+----------+
So when a User sponsors a Kid. I would like the kid_id & user_id to be entered into the kid_user pivot table. Any help would be appreciated.
First of all you might wanna rename the functions in your models in plural as it does not have one but many from the relationship.
So in your user model add this:
public function kids()
{
return $this->belongsToMany(Kid::class);
}
And in your Kid model:
public function users()
{
return $this->belongsToMany(User::class);
}
Then in order to save to the pivot table since your table naming is correct, just doing:
$user->kids()->attach($kid);
Will save it properly in the pivot table. Making sure first that you have existing User and Kid for the variables. More details here

laravel 5 - Eloquent Relationships (permissions table)

I think am struggling to get the relationships correctly in the scenario I have.
I have three tables
Table: users
| id | username |
----------------------
| 1 | pluto |
|------|-------------|
Table: permissions
| id | user_id | level_id | app_id |
--------------------------------------------------
| 1 | 1 | 2 | 9 |
|------|-------------|--------------|------------|
Table: levels
| id | level |
----------------------
| 1 | admin |
|------|-------------|
| 2 | manager |
|------|-------------|
| 3 | editor |
|------|-------------|
The result I am looking to get is
manager //through User:: model or Auth::
I would like to get a value from levels table in level column either through User model. This is the last version on what I have in my classes...
class User extends Authenticatable
{
public function permissions()
{
return $this->hasOne('App\Permissions');
}
}
class Permissions extends Model
{
public function user()
{
return $this->hasMany('App\User');
}
public function levels()
{
return $this->hasMany('App\Levels');
}
}
class Levels extends Model
{
public function permissions()
{
return $this->belongsTo('Modules\Ecosystem\Entities\permissions');
}
}
Using this in the controller I am able to retrieve values from permissions table. However I am unable to get values from levels table...
$user = User::with('permissions')->find(Auth::user()->id);
However I am unable to get values from levels table when I try this...
$user = User::with('permissions.levels')->find(Auth::user()->id);
That produces an error:
Column not found: 1054 Unknown column 'levels.permissions_id' in 'where clause' (SQL: select * from `levels` where `levels`.`permissions_id` in (1))
I understand that I am not understanding exactly how relationships would work in this instance but I don't want to just guess a solution. I'd like to understand it.
The thing is ,, Levels table serves only as a list of permission levels (roles). I recognize that I can define permission levels in some other way but for the moment this is how everything is set up.
If you are making a usual user permission system, where 'roles' is replaced by 'levels', then you need to reorganized your tables and relationships.
Table: users
| id | username | level_id |
---------------------------------------
| 1 | pluto | 2 |
|------|-------------|----------------|
Table: levels
| id | level |
----------------------
| 1 | admin |
|------|-------------|
| 2 | manager |
|------|-------------|
| 3 | editor |
|------|-------------|
Table: permissions
| id | level_id | app_id |
-----------------------------------
| 1 | 2 | 9 |
|------|-------------|------------|
So now User
hasOne('App\Levels', 'level_id');
Levels
hasMany('Modules\Ecosystem\Entities\permissions', 'level_id');
Permissions
belongsTo('App\Levels', 'level_id');
That is probably what you were trying to do, and it will works.
However, if many roles may have similar permissions, e.g: admin, manager & editor can all have access to a 'Page' or 'Content' or whatever that they all have access to, then you will need a 4th table to have many-to-many relationship between permissions and levels.
Table: permissions
| id | app_id |
---------------------
| 1 | 9 |
|------|------------|
Table: levels_permissions
| level_id | permission_id |
-----------------------------------
| 2 | 1 |
|-------------|-------------------|
With this, in Levels
belongsToMany('Modules\Ecosystem\Entities\permissions', 'levels_permissions', 'level_id', 'permission_id');
Inverse the relation in Permissions
belongsToMany('App\Levels', 'levels_permissions', 'permission_id', 'level_id');
In both approaches, you can now do
$user = User::with('levels.permissions')->find(Auth::user()->id);

How to eager load and query a pivot table using Eloquent - Laravel 5

Situation: A single appointment can be related to many clients and many users, so I have a many-to-many relationship (pivot table) between appointment and client, and another between appointment and user
I'm trying to write the eloquent query to get a client's information, with all its related appointments and the users related to each of those appointments.
Tables
|appointment|
| |
-------------
| id |
| time |
| created_at|
| updated_at|
| client | | appointment_client |
| | | (pivot table) |
------------- ----------------------
| id | | appointment_id |
| name | | client_id |
| created_at| | created_at |
| updated_at| | updated_at |
| user | | appointment_user |
| | | (pivot table) |
------------- ----------------------
| id | | appointment_id |
| name | | user_id |
| created_at| | created_at |
| updated_at| | updated_at |
Appointment.php model
public function client()
{
return $this->belongsToMany('App\Client')->withTimestamps();
}
public function user()
{
return $this->belongsToMany('App\User')->withTimestamps();
}
Client.php model
public function appointment()
{
return $this->belongsToMany('App\Appointment')->withTimestamps();
}
User.php model
public function appointment()
{
return $this->belongsToMany('App\Appointment')->withTimestamps();
}
On the client/id show page, I would like to show the client's information, list all of the appointments related to this client, and get the related user information for each appointment.
Not sure how to get the users related to the appointments with that:
$client = Client::with('appointment')
->where('id', '=', $id)
->firstOrFail();
It should be enough to just do:
$client = Client::with('appointment', 'appointment.user')->findOrFail($id);
This will load the client with given ID, then eager load their appointments and then, for each of the appointments, it will load related users.

Laravel Eloquent how to return one to many to many collection

Is it possible to get an eloquent relation based on a One->Many->Many relationship? In the below example, I would want to get all of the gallery_votes for a given user.
users
+----+---------------+
| id | user_name |
+----+---------------+
| 1 | bob |
| 2 | sam |
+----+---------------+
galleries
+----+---------------+-----------+
| id | gallery_name | user_id |
+----+---------------+-----------+
| 1 | Alaska Pics | 1 |
| 2 | Texas Pics | 1 |
| 3 | California | 2 |
| 4 | Cars | 2 |
+----+---------------+-----------+
gallery_votes
+----+---------------+--------+
| id | gallery_id | vote |
+----+---------------+--------+
| 1 | 1 | 1 |
| 2 | 1 | 1 |
| 3 | 1 | 1 |
| 4 | 2 | 1 |
+----+---------------+--------+
The relationships are setup as follows.
class User extends Model implements AuthenticatableContract, CanResetPasswordContract {{
...
public function galleries()
{
return $this->hasMany('App\Gallery');
}
}
and
class Gallery extends Model {
....
public function votes()
{
return $this->hasMany('App\GalleryVote');
}
}
$user->galleries returns a collection of associated galleries, so that works correctly. and $gallery->votes returns a collection of associate votes, so that also works correctly. But $user->galleries->votes gives
Undefined property: Illuminate\Database\Eloquent\Collection::$votes on line 1
Is what I'm trying to do even possible with straight Laravel? Or do I need to write the SQL directly?
The "has many through" relation provides a convenient short-cut for accessing distant relations via an intermediate relation.
class User extends Model implements AuthenticatableContract, CanResetPasswordContract {{
...
public function galleries()
{
return $this->hasMany('App\Gallery');
}
public function votes()
{
return $this->hasManyThrough('App\GalleryVote', 'App\Gallery');
}
}
Now $user->votes will return all votes for that user. Remember you need to create the GalleryVote eloquent model as well.
You can read more about that type of relation, as well as some example usage in the documentation.

Laravel getting results from model belongsToMany relationship back to controller

I'm using Laravel 5 and Eloquent, I have 3 tables setup:
photos
+----+---------------+------------------+
| id | photo_name | photo_location |
+----+---------------+------------------+
| 1 | kittens.jpg | C:\kittens.jpg |
| 2 | puppies.jpg | C:\puppies.jpg |
| 3 | airplanes.jpg | C:\airplanes.jpg |
| 4 | trains.jpg | C:\trains.jpg |
+----+---------------+------------------+
photo_set (pivot table)
+------------+----------+
| set_id | photo_id |
+------------+----------+
| 1 | 1 |
| 1 | 2 |
| 2 | 3 |
| 2 | 4 |
+------------+----------+
sets
+----+----------------+
| id | description |
+----+----------------+
| 1 | cute animals |
| 2 | transportation |
+----+----------------+
I created a belongsToMany relationship in my photos and sets models to link these two together.
class Photo extends Model {
public function sets()
{
return $this->belongsToMany('App\Set');
}
}
and
class Set extends Model {
public function photos()
{
return $this->belongsToMany('App\Photo');
}
}
However I'm trying to reference the $Sets->photos() model function in my controller, so that given a set of id=1, I can return the photo_location value for each row [C:\kittens.jpg,C:\puppies.jpg], but I don't know how to access it..
Also, I can "sort of" access this information in the view with:
#foreach($sets as $set)
{{$set->images}}
#endforeach
but it looks like it only iterates through once and returns a json (?) of the necessary information, though I'm not sure how to parse that to regular HTML either.
So in short, I'm having trouble accessing this data (photo_location) from both the controller and the view
Use Collection::lists()
$locations = Set::find(1)->photos->lists('photo_location');
It will return an array ['C:\kittens.jpg', 'C:\puppies.jpg']
http://laravel.com/api/4.2/Illuminate/Database/Eloquent/Collection.html#method_lists
In your controller try this :
$result = PhotoSet::where('set_id', $set_id)
->with('sets')
->with('photos')
->get();
$photo_location = $result->photos->photo_location;
here PhotoSet is the model for photo_set table, because it is a pivot table we will be able to access both sets and photos table from it.

Categories