I'm trying to create a Friendship system with Laravel (I'm starting with it) but I'm blocked with relationships. Here's the thing : there is one table Users and one table Friends which contains the following columns :
friends: id, user_id, friend_id, accepted.
It looks like a Many to Many so here's what I set on User class :
class User extends Eloquent {
function friends()
{
return $this->belongsToMany('User');
}
}
But when I try a :
$friends = User::find($id)->friends()->get()
I have this error :
Base table or view not found: 1146 Table 'base.user_user' doesn't exist
I would like to get a list of the Friends of a user, no matters if the user sent the invitation or received it. So the user can ba on user_id or on friend_id and then I retrieve the data of the other user depending of that column.
Any idea? Thank's!
EDIT : Here's the code I use :
$usersWithFriends = User::with('friendsOfMine', 'friendOf')->get();
$user = User::find(Auth::id())->friends;
foreach($user as $item) {
echo $item->first()->pivot->accepted;
}
tldr; you need 2 inverted relationships to make it work, check SETUP and USAGE below
First off the error - this is how your relation should look like:
function friends()
{
return $this->belongsToMany('User', 'friends', 'user_id', 'friend_id')
// if you want to rely on accepted field, then add this:
->wherePivot('accepted', '=', 1);
}
Then it will work without errors:
$user->friends; // collection of User models, returns the same as:
$user->friends()->get();
SETUP
However you would like the relation to work in both ways. Eloquent doesn't provide a relation of that kind, so you can instead use 2 inverted relationships and merge the results:
// friendship that I started
function friendsOfMine()
{
return $this->belongsToMany('User', 'friends', 'user_id', 'friend_id')
->wherePivot('accepted', '=', 1) // to filter only accepted
->withPivot('accepted'); // or to fetch accepted value
}
// friendship that I was invited to
function friendOf()
{
return $this->belongsToMany('User', 'friends', 'friend_id', 'user_id')
->wherePivot('accepted', '=', 1)
->withPivot('accepted');
}
// accessor allowing you call $user->friends
public function getFriendsAttribute()
{
if ( ! array_key_exists('friends', $this->relations)) $this->loadFriends();
return $this->getRelation('friends');
}
protected function loadFriends()
{
if ( ! array_key_exists('friends', $this->relations))
{
$friends = $this->mergeFriends();
$this->setRelation('friends', $friends);
}
}
protected function mergeFriends()
{
return $this->friendsOfMine->merge($this->friendOf);
}
USAGE
With such setup you can do this:
// access all friends
$user->friends; // collection of unique User model instances
// access friends a user invited
$user->friendsOfMine; // collection
// access friends that a user was invited by
$user->friendOf; // collection
// and eager load all friends with 2 queries
$usersWithFriends = User::with('friendsOfMine', 'friendOf')->get();
// then
$users->first()->friends; // collection
// Check the accepted value:
$user->friends->first()->pivot->accepted;
It's oviously a problem in your DB and also definition of the relation. Many-to-Many relation type expects you to use and intermediate table. Here's what you have to do :
Create a user_friend (id, user_id, friend_id) table in your schema.
Remove unnecessary fields from user and friend tables.
Create proper foreign keys . user.id-> user_friend.user_id , friend.id -> user_friend.friend_id
Better define full relation on the User and Friend models,
for example :
class User extends Eloquent {
function friends()
{
return $this->belongsToMany('User', 'user_friend', 'user_id', 'friend_id');
}
}
You can read much more in Laravel docs, HERE
Related
I have 3 tables in my database :
users (id);
interests (id);
users_interests (user_id, interests_id);
I want to be able to fetch all the user's interests in this way :
$interests = $user->interests
This is what I wrote in the User.php model, following laravel's doc:
public function interests() {
return $this->hasManyThrough(
'App\Interest', 'App\UserInterest',
'user_id', 'id', 'interest_id'
);
}
but it returns empty even though the user has a game. So there has to be something I'm doing wrong
Anyone to help me ?
I think a belongs to many would do the job:
public function interests() {
return $this->belongsToMany(
'App\Interest',
'users_interests',
'user_id',
'interests_id'
);
}
Quite similar to the example in the docs
If you were to rename users_interests table to interest_user and the column
interests_id to the singular form you would just need the first parameter:
public function interests() {
return $this->belongsToMany(App\Interest::class);
}
From my understanding the hasManyThrough is used to jump forward within a relation (also described in the docs):
The "has-many-through" relationship provides a convenient shortcut for
accessing distant relations via an intermediate relation.
I have a relationship like this
Requests
=> id
public function proposals(){
return $this->hasMany(Proposal::class)
}
Proposals
=> request_id
=> company_id
public function request(){
return $this->belongsTo(Request::class)
}
public function proposals(){
return $this->belongsTo(Company::class)
}
Companies
=> id
public function proposals(){
return $this->hasMany(Proposal::class)
}
I've tought in something like this:
$request->with('proposals')->whereDoesntHave('company', function($query){
$query->where('company_id', '<>', 1);
})->get()
But it didn't work out.
In this scenario how can I retrieve all the requests that one company has not sent a proposal?
EDIT
A short version of my DB Schema
A User can have many Companies, a User can make Requests, a Company respond to this Request through a Proposal
What I am seeing here from your schema is a pivot table relationship.
A Request can belong to many Companies, and a Company can have many Requests. The relationship between the two is the Proposal, which is your pivot table.
So firstly, you want add the correct relationships to your models
class Request
{
public function companies()
{
// You will need to probably set the foreign and local keys as extra parameters here
return $this->belongsToMany(Company::class, 'proposals')->using(Proposal::class);
}
}
and
class Company
{
public function requests()
{
// You will need to probably set the foreign and local keys as extra parameters here
return $this->belongsToMany(Request::class, 'proposals')->using(Proposal::class);
}
}
Finally, you can get the proposal using the pivot which will be a Proposal object.
To address your original query, you would just do:
$requests = Request::with(['companies' => function($q) {
$q->where('companyid', '!=', 1);
}]);
I'm using Laravel 5.4, Laravel Roles from here and Eloquent relationships.
I'm trying to retrieve the users of a company along with their roles.
User::find(1)->roles gets me the user's roles whose id =1
Company::find(1)->users gets me all the users that belongs to the company whose id =1 (without the roles of users).
Company::find(1)->users->roles returns an error Property [roles] does not exist on this collection instance.
Questions
Is it possible to do what I want to do ?
If so, how should I do it ?
User.php
class User extends Authenticatable
{
use HasRoleAndPermission;
public function company()
{
return $this->belongsTo('App\Company');
}
public function user()
{
return $this->belongsTo(User::class);
}
}
HasRoleAndPermission.php
trait HasRoleAndPermission
{
public function roles()
{
return $this->belongsToMany(config('roles.models.role'));
}
}
Company.php
class Company extends Model
{
public function users() {
return $this->hasMany('App\User');
}
}
$company = Company::with('users.roles')->find(1);
Will load all the users, and all the roles for each user.
Update
According to your comment, you don't want the company data. Just users and roles Using eager loading and relationship existence.
$users = User::with('roles')
->whereHas('company' => function($query){
$query->where('name', '=', 'company'); //If you don't have the company ID
})->get();
Without relationship existence
$users = User::where('company_id', 1)->with('roles')->get();
1 company has many users.
1 users has many roles.
You are trying to get the roles of a collection of users (the property only exists for one user) thus, the property doesn't exists for the collection.
If you want to get all the roles of all users in the company, you might try the above code:
$roles = [];
Company::find(1)->users->foreach(function($user) {
$roles = array_merge($roles, $user->roles);
});
--------- edit ---------
For eager loading the roles of users, you must use with, as suggested by #Ohgodwhy, but I'd refactor a little:
$users = User::with('roles')->where('company_id', $companyId)->get();
Now you have the array of users eager loading their roles. You still can't access directly $users->roles, you must first get a user, only then get its roles.
I have User and Project models.
The relationship is many to many.
public function projects()
{
return $this->belongsToMany('App\Project')->withPivot('id');;
}
And in Project Model I have :
public function users()
{
return $this->belongsToMany('App\User')->withPivot('id');;
}
Now what I am doing :
$projectsAsFreelancer = App\Project::where('assignedTo',$id)->get();
What i want is to get the username from the users table of the user, Who have posted the project. The Project means projects table have the forign_key->employeer_id.
Currently $project->pivot->username gives me error.
pivot table name is project_user.
What i am missing ?
$project->pivot->username requires the username field in your pivot table. ->withPivot('id'); is only required if you wish to get id column of the pivot table, $project->pivot->id wil give the value.
If you want to get username from users table you don't require pivot.
$projectsAsFreelancer = App\Project::where('assignedTo',$id)->get();
foreach($projectsAsFreelancer as $project)
{
echo $project->name;
foreach($project->users as $user)
{
echo $user->username;
}
}
Don't use get(). It could not fetch the relations. You could use first() or find() (if you want to select with primary key)
$projectsAsFreelancer = App\Project::where('assignedTo',$id)->first();
foreach ($projectsAsFreelancer->users as $user) {
//var_dump($user->username);
}
I have four tables:
Users: id, username
Roles: id, name
Domains id, title
DomainsAssignedRolesUsers id, role_id, user_id, domain_id
I want to get all user roles for a domain.
I do:
User::find(1)->domains->first()->roles
But i get all domains roles, not only for my user. Help me to get user roles only for the selected domain/
My relations:
// User model:
public function rolesDomain() {
return $this->belongsToMany('Role', 'domainsAssignedRolesUsers', 'user_id', 'role_id');
}
public function domains() {
return $this->belongsToMany('Domain', 'domainsAssignedRolesUsers');
}
// Role model:
public function domains() {
return $this->belongsToMany('Domain', 'domainsAssignedRolesUsers');
}
// Domain model:
public function roles() {
return $this->belongsToMany('Role', 'domainsAssignedRolesUsers', 'domain_id', 'role_id');
}
public function users() {
return $this->belongsToMany('User', 'domainsAssignedRolesUsers', 'domain_id', 'user_id');
}
You want to get all the roles, from a specific domain, in relation with an user?
So this should do the trick:
User::find(1)->rolesDomain()->where('domain_id', $specificID)->get()
But if you want to only get the roles from the first domain for an user.
User::find(1)->domains()->first()->roles()->get();
And if you only want to retrieve the roles for a user.
User::find(1)->rolesDomain()->get()
And if you only want to retrieve all the roles from each domain in relation with an user.
User::find(1)->domains()->with('roles')->get()
Even if Eloquent documentation has a few example, This orm is really intuitive.
Check out Eloquent Triple Pivot (also on Packagist), it sounds like it does exactly what you want.
You'd set up your Models as User, Domain and Role (a User has many Domains and can have a Role in each). Then look at the docs on the Github page (particularly step 6), and you'd define this on your Domain model:
class Domain extends Eloquent {
...
public function getRolesAttribute() {
return $this->getThirdAttribute();
}
}
Then you can simply call
$user = User::find(1);
$domain = $user->domains->first();
// or
$domain = $user->domains->find(73);
$roles = $domain->roles;
// Or the above can be condensed into just...
$roles = User::findOrFail( 1 )
->domains()
->findOrFail( 73 )
->roles;