I want to create a relationship that checks if a user has liked a post. In order to do this, the relationship needs to check if the user is logged in, and then use their user_id to get the like record. Something like:
public function userLike() {
if (Auth::check()) return $this->hasOne('App\Like')->where('user_id', Auth::user()->id);
}
However, this doesn't work. Additionally, if the user is not logged in and this relationship is called (which it is by default), it will return an error.
What is the proper way of doing this?
If you want to conditionally load a relation then you can do so by lazy eager loading. In your example you could define the relation as
public function userLike() {
return $this->hasOne('App\Like', 'user_id');
}
Then in your controller (or wherever else) you can do the check for if the user is question is currently logged in user or not
$loggedInUser = auth()->user();
if($loggedInUser){
$loggedInUser->load('userLike');
}
Then you can continue with whatever you want to do with the loggedInUser and the userLike.
Say you have multiple users at a point (in your code) and you want to load the likes for only the currently logged in user then you can
//$users is a collection of multiple users - assumed
foreach($users as $user){
if($user->email === auth()->user()->email){
$user->load('userLike');
}
}
Hope this helps
This is how I would do that:
1) “likes” table
I would first create a table to store all the “likes” with columns for a “post_id” and a “user_id”
2) create the relationships
User Model
public function likes()
{
return $this->belongsToMany('App\Post', 'likes', 'user_id', 'post_id');
}
Post Model
public function likes()
{
return $this->belongsToMany('App\User', 'likes');
}
3) Post Controller - the method
I would do the followings:
check if user is logged in, otherwise throw an error
make sure post exists, otherwise throw an error
create a new entry in like table with user_id and post_id
(you can also check if the user is logged in directly on your route using a middleware)
4) In the view
I would call the controller method using an ajax call.
Not sure if I forgot something, but hopefully that can help you a little bit.
If you need to “like” more things than only post, check the polymorphic relationships.
Related
I started coding laravel and everything was fine till I wanted to get the result based on the user, how to do it, what do I need to do?.
You can use where clause.
Suppose you wanna fetch all the blogs of a user.
like:
Blog::where('user_id', Auth::user()->id)->get();
// Auth::user()->id returns the logged in user's id.
Or
create a relation in User model
App\Models\User
use App\Models\Blog;
public function blogs()
{
return $this->hasMany(User::class, 'user_id', 'id');
}
Then
Auth::user()->blogs;
In my Product Policy file I have the following:
public function change_customer_pricing(User $user) {
return $user->id == 2;
}
How do I return more than one user ID to the Controller?
My controller:
public function change_customer_pricing($ProdID) {
$user = Auth::user();
//dd($user->can('change_customer_pricing', Product::class));
if (!$user->can('change_customer_pricing', Product::class))
return redirect()->route('home')->with('status', 'You are not Authorized to access')->with('code', 'red');
...
public function change_customer_pricing(User $user){
return in_array($user->UserID,[2,12,14]);
}
If you're looking to determine if a User has authorisation to perform an action on a Product, you need a relationship between the User and Product and then check the relationship.
Say you have a user_id field on your Product which holds the id of the User that created that Product for example and only that User is allowed to update or delete the Product. You would want to check the id of the authenticated User matches the user_id of the Product.
public function change_customer_pricin(User $user, Product $product)
{
return $user->id === $product->user_id;
}
Then you can use any of the baked in authorisation functions with Laravel. As you're wanthing to check if a User is authorised from a controller, you can do:
public function change_customer_pricing($ProdID)
{
$product = Product::find($ProdId);
$this->authorize('change_product_pricing', $product);
}
As a side note, you might want to consider using route model binding in your controller.
Update
As your Product doesn't have a relationship to a User and you're looking to authorise multiple Users, you could check id of the currently authenticated User against an array:
public function change_customer_pricin(User $user)
{
return in_array($user->id, [1, 2, 3, 4];
}
This is the quickest and simplest solution. However, it is not very maintainable as you'd have to add/remove as requirements changed.
A better solution would be to use roles and permissions to determine authorisation. You define permissions which are used scope access then you can associate (assign) permissions directly to a User or to a Role (and the a Role to a User). When an action is performed, you check if a User has the required Role or Permission.
There are a few packages available which provide such functionalty, a very popular option is Spatie Permission and another popular choice is Laratrust.
A user can have one whitelabel.
A whitelabel can have many users.
I have a pivot table with whitelabel_id, and user_id columns
I have both relationships set up using ->belongsToMany() (with the inverse).
I am using a pivot table because I don't have a whitelabel_id on the users table (and won't be putting one in) so its defined as a many-to-many, but really it's one-to-many. I just get the first() whitelabel as there'll only be one for each user.
With this in mind. How do I select * users with the currently authenticated user's whitelabel?
I have this, it works, but is this the "Laravel" way? I feel it's slightly over engineered and Laravel would have a shorthand method.
$user->when(auth()->user()->whitelabel->first(), function ($query) {
return
$query->whereIn('id', auth()->user()->whitelabel->first()->users->pluck('id'));
})
This checks if the auth user has a whitelabel, and then gets all users with the same whitelabel.
Does Laravel have a quick shorthand for this?
Models:
class Whitelabel
{
public function users()
{
return $this->belongsToMany(User::class);
}
}
class User
{
public function whitelabels()
{
return $this->belongsToMany(Whitelabel::class);
}
}
you can retrieve records for simple
$user = User::find(Auth::id())
$user->whitelabels
if you want return all user in Auth user's whitelabel
foreach($user->whitelabels as $whitelabel){
$whitelabel->users
}
I need to clear my doubt about Eloquent Relationships. I have 2 models User (which comes with Laravel) and Other is Role which I created.
in migration, I added role_id as an additional column as I want every user must have a role now I want to retrieve a user role based on user's id so, I created a public function named roles inside the User Model.
public function roles(){
return $this->belongsTo(Role::class);
}
now when i try to run the query like this.
App\User::find(1)->roles;
it won't return any result, just empty screen but when I change it to
public function role(){
return $this->belongsTo(Role::class);
}
after that i run code
App\User::find(1)->role;
now it returns the exact row where the user with id 1 has a proper role. it's confusing why with the roles() function it's not working but with the role() function it's working.
Sorry, If this question is already posted you can redirect me to that question.
Thank You!
You have to specify the foreign key
public function roles()
{
return $this->belongsTo(Role::class, 'role_id');
}
However, calling it role() is more accurate, since your are assuming that a User can only have one role.
I have four tables. User, Conversations, Message and Conversation_Participants.
(I hope you don't find a relationship error in this image )
I tried to add a function
public function conversations(){
return $this->belongsToMany(Conversation::class, 'conversation_participants', 'user_id', 'conversation_id');
}
to User::class but if i call User::with('conversations')->get() i only get all existing Users. What am I doing wrong? First i want to get all conversations the current user participates in and second I want to get all receivers of the conversations.
I also tried
public function participants()
{
return $this->belongsToMany(User::class, 'conversation_participants', 'user_id', 'conversation_id');
}
in the Conversation::class but Conversation::with('participants')->get() gets me all Conversation even those the user isn't participating in.
I'm really confused atm :/
Add the following in your User model:
public function conversations() {
return $this->belongsToMany(Conversation::class, 'conversation_participants', 'users_id', 'conversations_id');
}
And this to your Conversation model:
public function participants() {
return $this->belongsToMany(User::class, 'conversation_participants', 'conversation_id', 'users_id');
}
If you want to link your tables easier, read up on conventions.
To get all the conversations a user is participating in, run the following (assuming you've loaded the user): $user->conversations() to get all the conversations a user is in.
If you want all users, with all their conversations, with all the participants connected, do the following: $users = Users::with('conversations.participants')->get(). You can now loop through this as follows:
foreach($users as $user) {
foreach($user->conversations as $conversation) {
foreach($conversations->participants as $participant) {
// do fancy stuff here
}
}
}
Notice that the user from which you start is also a participant, so maybe you need to filter that one out again.
If you want to get even more fancy (but use more resources) you could even query all the messages a conversation has too!
User::with('conversations.participants', 'conversations.messages.user')->get()
But this only works when you set up a second set of relationships along the upper table in your image (conversations <-> messages <-> users)
Edit
In the comments, OP asked if it was possible to limit the amount of messages retrieved from the database, which is possible to my knowledge, but I don't now if this is the best way:
Remove the messages from the first eager loading part:
User::with('conversations.participants')
After that, when looping through the conversations, lazy load the messages:
$conversation->load(['messages' => function($query){
$query->take(5);
}, 'users']);
Access them after that with $conversation->messages.
Note
I think this could be done more easily in one go, but I don't have a setup right now to test this for you.
Edit 2
After Ronon added another comment, here's what I came up with:
Add a new relationship in the Conversation model:
public function last_messages() {
return $this->hasMany(Message::class, 'conversation_id', 'id')->latest()->limit(2);
}
Because of that, you now can do this:
User::with('conversations.participants', 'conversations.last_messages.users')->get()
If you still want all the messages, you can use the messages relationship. If you want less, use the last_messages one.
Calling User::with('conversations')->get() does not specify a User. I might be misreading but I think you are looking for something like the following:
$user = User::with('conversations')->find(1);
$userConversations = $user->conversations;
which will provide you with a user and their conversations.