I'm working on a small project using Laravel, and I would like to create a like and dislike system, so a user can like one to many more users.
I already have a table called Users (default by Laravel), now I have created another table called favorite_user with id, user_id and liked_the_user.
How can I get all the users that a user already liked? Is my table correct or should I change something?
All what I want to do is add like and show the people I liked. I don't know how to make this relationship since I am new to Laravel.
Thank you
Looks like you have a pivot table there so a Many to Many relationship is what you need to setup. You will need to pass some additional arguments since the naming won't fit with convention.
User Model:
// users the user has liked
public function liked()
{
return $this->belongsToMany(self::class, 'favorite_user', 'user_id', 'liked_the_user');
}
// users who like the user
public function likes()
{
return $this->belongsToMany(self::class, 'favorite_user', 'liked_the_user', 'user_id');
}
Controller:
public function liked(Request $request)
{
// get liked users for current user
$liked = $request->user()->liked;
// get liked users for an arbitrary user
$liked = User::findOrFail(...)->liked;
...
}
Laravel 8.x Docs - Eloquent - Relationships - Many to Many - belongsToMany
Related
In my app, you can create lists of roles that are attached to contacts. So you can assign the contact "Bob" the roles of "Gardener" and "Pet Sitter". Then you can create the list "People" and add "Gardener (Bob)" and "Pet Sitter (Bob)" to it.
I have the following tables:
contacts
id
name
roles
id
name
contact_role (pivot)
id
contact_id
role_id
lists
id
name
contact_role_list (pivot)
id
contact_role_id
list_id
Everything was working smoothly until the second pivot table linked to the first pivot table. My pivot tables are (currently) not having any models so I'm not sure if there is a built-in feature to tackle this in Laravel or if I need to think differently.
I currently have this in my List model:
public function list_roles(): BelongsToMany
{
return $this->belongsToMany(XYZ::class, 'contact_role_list', 'list_id', 'contact_role_id');
}
Is this even close? What do I put where it says XYZ::class?
Ok, so the below is doing what I want, but is there an even better way to do it? The key to solving my problem was to create a Model for ContactRole and changing extends Model to extends Pivot.
I placed this in my List Model:
public function list_roles(): BelongsToMany
{
return $this->belongsToMany(ContactRole::class, 'contact_role_list', 'list_id', 'contact_role_id');
}
And this in my ContactRole Model:
public function contact(): BelongsTo
{
return $this->belongsTo(Contact::class);
}
Now I could reach the contact data by using something like this: List::first()->contact_roles->first()->contact
Any way to use with, pivot or similar to tidy this up even more? Thanks!
I like to approach these issues in terms of Models rather than pivots. I think many new Developers in Laravel get over obsessed with what's going on in the Database which is fine, but theres a lot of Magic going on so you can write very simple code that does a lot of Heavy lifting, so that being said if I fully understand your problem
You have a Contacts Model
This model can have many roles
so in your contacts Model you need a role relationship
public function roles()
{
return $this->belongsToMany(Roles::class);
}
next of course you have a role Model (pun intended)
your each role can have many list
public function lists()
{
return $this->hasMany(List::class)
}
then the idea is now that you have roles on contacts and lists on roles you should be able to have many lists through contact
public function lists()
{
return $this->hasManyThrough(List::class, Role::class);
}
I've done similar things before and for your description it seems like that's the approach you might need to take.
I have 4 models, User, Profile, Audit and AuditApplications.
My table structure looks something like:
User
- id
- ... other user-related fields
Profile
- id
- user_id
- ... various profile fields
Audit
- id
- user_id
AuditApplications
- id
- audit_id
- user_id
My User model looks like this:
public function profile()
{
return $this->hasOne('App\Profile');
}
public function auditApplications()
{
return $this->hasMany('App\AuditApplications');
}
So I can get the user and their related profile easily and also a list of audits a user has applied for.
Next, I have AuditApplications table which holds a list of user IDs and audit ids of users that have applied to an audit. My AuditApplications table has the following relationship.
public function user()
{
return $this->belongsTo('App\User');
}
So I can return a list of users that have applied to an audit like so:
$applicants = AuditApplications::where('audit_id', $audit->id)->with('user')->get();
All this works well as expected but now I want to access the user's profile when I get a list of applications. The relationship is a distance one so I want to get a list of users and their profiles in the AuditApplications model.
Is there a way of doing this in Eloquent without creating a loop in my controller to then loop over each of the users getting their profiles individually?
you just need to do ->with('user.profile')
I am a bit curious about the performance of my app. Therefore I just want to know that when the queries for the hasMany and belongsTo are executed?
Here is the relevant sample code for example:
class Article extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
}
class User extends Authenticatable
{
public function articles()
{
return $this->hasMany(Article::class);
}
}
If I fetch all the user with:
$user = App\User::find(1);
Now if I need the articles for this user, I would do it like this:
$user->articles;
This in the backend in Laravel executes the following query:
SELECT * FROM articles WHERE user_id = 1
and it will give me all the articles for the user. So far so good. But in most scenarios, I will just need the information of the user and would not want Laravel to execute that query for getting articles of the user. The same I need for the belongsTo.
My Question is, does laravel execute the query for articles at the time when I execute the query for User? or it executes the query at the time when I call:
$user->articles;
This is just an example scenario. I have many real world scenarios where one model has relationship with many other models.
So here if I fetech all users with:
$users = App\User::all();
Will it execute that articles query for all users one by one? or at once? and at what time does it query for the articles? if I do not call:
$user->articles;
does it still query database for articles of the user?
Update
So after some efforts I found out that when you query a user/users:
$user = App\User::find(1);
it does not get the articles for the user until you call articles on it:
$user->articles;
But now there is another problem. If I am fetching all users and I also need the articles for each of them, I will have to do something like this:
$users = App\User::all();
and then in a loop I will get the articles for each of the users, and the articles query will be executed for each user separately. Which is still a performance concern. If there are 100 users so I will have to query users table once with all() method and then 100 queries for articles of each user. Which is not what I want. I need one query for users and one query for articles. Once the articles for all users have been brought then the relevant articles should be assigned to each user.
But it seems like there is no such thing in Laravel. I will have to get user_ids of all the users and make a query like this:
$articles = \App\Article::whereIn("user_id", $user_ids_array)->get();
Then I will have to loop through the articles and assign each user their articles.
I just wanted to find a short way using Laravel.
But it seems like that is not possible for now. I wish if this would be available in future versions of Laravel.
You need to read more about Eager-loading in Laravel.
In your case, you need to do :
$users = App\User::with('articles')->get();
The "with" method is here exactly for this.
Another alternative to get all articles after your initial query call is this :
$users = App\User::get();
$users->load('articles');
Of course, this is implying you have a working relationship and a "public function articles() {}" in your User Model. Probably something like this :
public function articles() {
return $this->hasMany(Article::class);
}
You can read more about this feature here : https://laravel.com/docs/6.x/eloquent-relationships#eager-loading
Say I have an orders table and a users table and a guests table, for a company who accept orders from both users and guests.
I also have a is_guest boolean on the orders table which lets me know quickly whether the order has been made by a guest or a registered user.
I have a model method called customer which returns the eloquent relation like so:
public function customer()
{
return $this->is_guest ? $this->belongsTo('Guest', 'user_id') : $this->belongsTo('User', 'user_id');
}
This works very well when I pass orders to the view and access the customer's data like so:
// Maps to either $order->user->firstname OR $order->guest->firstname
echo $order->customer->firstname;
But when I try to use this functionality when using eager loading it doesn't quite work:
$orders = Order::with('customer')->get();
return Response::make($orders);
It only returns the eager loaded customer data for either the user or the guest and never both.
I'm wondering why this is and how to get round it?
Is it because of the way the eager loading works - it only calls the customer() method once just as it only hits the database once, so whatever the first result of the query is, it will use that to determine the rest of the query too?
If I am trying to get a JSON response of all the order data with all of the customer's details too, how would I do that?
Thanks.
UPDATE
Yes, turns out I was abusing the Laravel relationship method by adding a condition.
Using the polymorphic relations is a much better approach as suggested by #lukasgeiter.
http://laravel.com/docs/4.2/eloquent#polymorphic-relations
I did have trouble loading other relations based on the polymorphic relation. For example if both User and Guest had corresponding tables UserDetail and GuestDetail then I couldn't get the eager loading to work. So I added this to both the User and Guest models:
protected $with = ['detail'];
This means that whenever the customer polymorphic relation is eager loaded, then both User and Guest will load their relationships automatically. I think I could have also used the 'lazy loading' functionality $order->load('customer') but I didn't try it out.
public function detail()
{
return $this->hasOne('UserDetail');
}
Thanks guys.
how can I pull all the posts of a certain user? should i foreach all the posts of a certain user? Like if user1 posted something and I want to pull out whatever he posted and only shows when he's logged in.
In Laravel, if you are using Eloquent relationships things would look something like this.
First, define your relationships within your models. If you're following the example below, I'm going to assume the following.
Your users table in database is named users. Your posts table in
database is named posts, and has an integer column named user_id
corresponding to the id of the user the post belongs to. If your
table and column names are different, make sure you read the Laravel
docs to learn how to set custom table names and column names for
Eloquent models.
app/models/User.php
<?php
class User extends Eloquent {
// All the default User.php stuff....
public function posts()
{
return $this->hasMany('Post');
}
}
app/models/Post.php
class Post extends Eloquent {
public function user()
{
return $this->belongsTo('User');
}
}
After we've defined those relationships, you can get a particular user using something like this.
$user = User::find(1); // This will get the user with an ID of one.
Then, you retrieve that user's posts, we can use the Eloquent relationship we defined earlier.
$posts = $user->posts()->all();
If the user is already logged in, you can user the Auth class to do this for the logged in user.
$posts = Auth::user()->posts()->all();
You'll now realize that ->posts() actually returns a Collection, and you can use all of the Query and Eloquent methods on that collection as normal.