I'm trying to get the output of a belongstoMany relationship between users and sections database, which are tied to User and Section model, respectively.
User model has this method:
public function section()
{
return $this->belongsToMany('App\Models\Section','user_section')->withTimestamps();
}
Section model has this method:
public function user()
{
return $this->belongsToMany('App\Models\User','user_section')->withTimestamps();
}
My user_section table looks like this:
+----+---------+------------+------------+------------+
| id | user_id | section_id | created_at | updated_at |
+----+---------+------------+------------+------------+
| 1 | 1 | 1 | NULL | NULL |
| 2 | 2 | 1 | NULL | NULL |
+----+---------+------------+------------+------------+
2 rows in set (0.00 sec)
So why when I do this in the business logic:
$user = User::where("id",Auth::user()->id)->first(); //user with id of 1 in my case
return json_encode($user->section());
Do I get {} as output?
$user->section() will return the BelongsToMany relationship instead of the model object itself. You can do $user->section instead to load the relationship and have the model returned.
Related
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
My problem is I am trying to establish a many to many relationship between posts and genre. Here is what I have done so far.
class Post extends Model
{
public function genres()
{
return $this->belongsToMany('App\Genre');
}
}
class Genre extends Model
{
public function posts()
{
return $this->hasMany('App\Post');
}
}
genre Table
+----+--------+
| id | name |
+----+--------+
| 1 | News |
| 2 | Sports |
+----+--------+
post table
+----+----------------+
| id | title |
+----+----------------+
| 1 | Political News |
| 2 | Sport Update |
+----+----------------+
genre_post table
+----+---------+----------+
| id | post_id | genre_id |
+----+---------+----------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 2 |
+----+---------+----------+
When I am trying to acess genre list of a post, everything works fine.
Post::where('slug', '=', $id)->with("genres")->first(); // no problem
But when I tried the opposite it's not working.
$posts = Genre::where( "slug", "=", $id )->with("posts")->first();
I am getting the following error.
Column not found: 1054 Unknown column 'post.genre_id' in 'where
clause' (SQL: select * from post where post.genre_id
I understand the laravel is trying to aceess genre_id column from post table which doesn't exists since it's a many to many relation which means one post can contain more than one genre and one genre can contain more than one post.
Any idea how can I resolve this?
This is expected since hasMany itself an one to many relationship. Use belongsToMany instead.
class Genre extends Model
{
public function posts()
{
return $this->belongsToMany('App\Post');
}
}
Problem
I created a simple friendship relationship for my Laravel app which all worked ok until I noticed that when I queried the friendship of a user it would only search the current user on the UID1 field.
Since friendships are in essence a two-way relationship, Im trying to find a way in a laravel Model to retrieve ALL friendships relations by multiple columns.
Current Implementation
public function friends()
{
return $this->belongsToMany( App\Modules\Users\Models\User::class ,'friends', 'uid1');
}
Ideal Implementation
public function friends()
{
$a = $this->belongsToMany( App\Modules\Users\Models\User::class ,'users_friends', 'uid1');
$b = $this->belongsToMany( App\Modules\Users\Models\User::class ,'users_friends', 'uid2');
return combine($a,$b);
}
Table Structure
+----------------------+
| users table |
+----------------------+
+----| id: primary UserID |
| | fname: string |
| +----------------------+
|
|
| +----------------------+
| | friends table |
| +----------------------+
| | id: primary iD |
| | |
+----| uid1: user_id |
| | |
+----| uid2: user_id |
+----------------------+
The current implementation will only result in 1 of these records returning if the Current UserID = 1 as per the data in the friends table below.
+-------------------------------+
| friends table (data) |
+--------|---------|------------+
| id | uid1 | uid2 |
+--------|---------|------------+
| 1 | 1 | 7 |
| 2 | 7 | 1 |
| 3 | 9 | 1 |
+-------------------------------+
User Model
<?php
namespace App\Modules\Users\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
protected $table = 'users';
protected $fillable = [
'username', 'email', 'password', .... .
];
public function friends()
{
return $this->belongsToMany( App\Modules\Users\Models\User::class ,'users_friends', 'uid1');
}
Environment
Server = Homestead/linux
PHP = 7
MySQL
Update
I have a FriendShip helper class I created which does something similar, however in this function I pass in the UserID explicitly
Friendship::where( [
[ 'uid1' ,'=', $uid],
])->orWhere( [
[ 'uid2', '=', $uid]
])->all();
You can add additional conditions when you're declaring relationship by simply chaining it.
<?php
//...
class User extends Model {
//...
public function friends() {
return $this->hasMany(/*...*/)->orWhere('uid2', $this->id);
}
//...
But keep in mind that eloquent is not grouping the first conditions of relation in parenthesis so you might end with SQL that will not work as expected in some cases (if using or, and should be fine)
For example the above might result in a SQL that looks like this
SELECT * FROM users_friends WHERE uid1 = ? AND uid1 IS NOT NULL OR uid2 = ?
Which is a correct SQL statement but without grouping you will not get the result that you're expecting.
Another way is to use accessor and two separate relationships
<?php
//...
public function friends1() {
return $this->belongsToMany(User::class, 'users_friends', 'uid1');
}
public function friends2() {
return $this->belongsToMany(User::class, 'users_friends', 'uid2');
}
public function getFriendsAttribute() {
return $this->friends1->merge($this->friends2);
}
//...
But this way you get two separate trips to DB.
I got 4 tables:
// Table countries
+----+------+
| Id | Name |
+----+------+
| 1 | USA |
| 2 | GB |
+----+------+
// Table platforms
+----+---------+
| Id | Name |
+----+---------+
| 1 | Windows |
| 2 | Linux |
+----+---------+
// Table users
+----+-------+------------+-------------+
| Id | Name | country_id | platform_id |
+----+-------+------------+-------------+
| 1 | Admin | 1 | 1 |
| 2 | Test | 2 | 1 |
+----+-------+------------+-------------+
// Table posts
+----+-----------+------------+-------------+---------+
| Id | Title | country_id | platform_id | user_id |
+----+-----------+------------+-------------+---------+
| 1 | TestPost1 | 2 | 1 | 1 |
| 2 | TestPost2 | 2 | 2 | null |
+----+-----------+------------+-------------+---------+
The database should be able to implement the following relations:
User (N) <-> (N) Platform
User (N) <-> (N) Country
User (0..1) <-> (N) Post
Post (N) <-> (N) Country
Post (N) <-> (1) Platform
So now I tried to implement these relations following Laravel Eloquent ORM documentation:
// Country.php
public function posts()
{
return $this->belongsToMany('App\Post');
}
public function users()
{
return $this->belongsToMany('App\User');
}
// Platform.php
public function users()
{
return $this->belongsToMany('App\User');
}
public function posts()
{
return $this->belongsToMany('App\Post');
}
// User.php
public function posts()
{
return $this->hasMany('App\Post');
}
public function countries()
{
return $this->hasMany('App\Country');
}
public function platforms()
{
return $this->hasMany('App\Platform');
}
// Post.php
public function countries()
{
return $this->hasMany('App\Country');
}
public function platforms()
{
return $this->hasMany('App\Comment');
}
public function user()
{
return $this->belongsTo('App\User', 'user_id');
}
But now I am confused, as I thought the way to implement N to N relations in mysql is to add a third table to db, for example like that:
// Table CountryUserRelations to implement User (N) <-> (N) Country
+----+------------+---------+
| Id | country_id | user_id |
+----+------------+---------+
| 1 | 1 | 1 |
| 2 | 2 | 1 |
| 3 | 1 | 2 |
| 4 | 2 | 2 |
+----+------------+---------+
But how does Eloquent ORM handle the rules inside my model? Will it keep the N to N relations without having to add a relations table? Or am I missing something or misunderstanding the Eloquent ORM Relations concept?
I just joined stackoverflow so I do not have enough credit to comment so I will leave an asnwer here.
First of all please correct your relationship definition.
in User Model:( you have mistake here)
public function countries(){
return $this->belongsToMany(Country::class);
}
and in your Country Model:
public function users(){
return $this->belongsToMany(User::class);
}
second you need to create country_user table using:
php artisan make:migration create_country_user_table
after it you need to complete your table:
Schema::create('country_user', function (Blueprint $table){
$table->increments('id');
$table->unsignedInteger('country_id');
$table->unsignedInteger('user_id');
$table->foreign('country_id')->references('id')->on('countries');
$table->foreign('user_id')->references('id')->on('users');
}
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);