public function chats()
{
return $this->hasMany('App\Chat','sender_id')->orWhere('receiver_id',\Auth::id());
}
My requirement is that i want to fetch messages that being sent or received by User. how i can do it ?
The following code should do the trick:
public function chats()
{
return $this->hasMany('App\Chat','sender_id')->union($this->hasMany('App\Chat','receiver_id'));
}
This will return a relation that is a union of 2 queries - one that fetches chat messages where given user is the sender and another one where given user is the receiver.
You can now access user's chats with $user->chats.
You were very close. hasMany() accepts chained where() and orWhere() calls, so I would make that query like this:
public function chats()
{
return $this->hasMany('App\Chat')
->where('sender_id', \Auth::id())
->orWhere('receiver_id', \Auth::id());
}
Once this is correctly set up, you can use $users->chats to access a list of chats that were sent from OR to the current user.
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;
i'm using Laravel as my PHP framework. its a convention to put index show store ... functions in controllers.
i have 2 types of users(Admin & normal user). lets assume there is an Order(in restaurant) model and i want to implement index function for its controller.
a user can have more than one Order.
what i need is that this function:
if admin is calling this API: returns all Orders
if normal user is calling this API: returns just Orders owned by this user
i searched but i couldn't find anything(tbh i didn't know what to search).
once i did this as below which i didn't like because it looks two different functions gathered in one:
if ($user->role == admin) {
// fetch all orders
} else if ($user->role == normal_user) {
// just find user orders
}
so my question is what's best approach to achieve what i want?
Such a REST API endpoint is typically a search allowing multiple filters, sorting and pagination. If so it is completly fine to apply different defaults for filters and also restrict filters to roles.
I would auto apply a filter user=currentUser for missing admin role and return a forbidden if a user without the admin role tries to apply a user filter for a different user.
With this approach you give admins also the functionality to search for offers of a specific user and you only need one search api to be used by the controller.
Why don't use an if statement?
You could make a scope on the model but then you'll still have an if.
What about this?
if ($user->role == admin) {
Order::all();
} else if ($user->role == normal_user) {
$user->orders()->get();
}
Or make it an inline if
$user->role == admin ? Order::all() : $user->orders()->get();
IMO the best practice here is to make a different Admin/OrderController.php
Then with middleware check wat, the role of the user is, and then redirect them to the admin controllers.
Since you'll probably also want an update and delete, or other functions only accesible by an Admin
I had a similar question myself a while ago and ended up with this strange solution to avoid that if/else block.
Assumptions
I assumed the existence of an helper method in the User model called isNot($role) to verify the if the user's role matches or not the given one.
This is just an example to give the idea of the check, but you should implement the condition as you like.
Second assumption I made is that each order has a user_id field which will reference the owner of that order though his id (FK of 1:N among user and order).
Implementation
public function index(Request $request)
{
$orders = Order::query()
->when($request->user()->isNot('admin'), function ($query) use ($request) {
return $request->user()->orders();
// Or return $query->where('user_id', $request->user()->id);
})
->paginate();
return OrderResource::collection($orders);
}
The when method is the key here. Basically you call it like: when($value, $callback) and if $value is false the callback won't be executed, otherwise it will.
So for example, if the user is not an admin, you will end up executing this query:
Order::paginate();
that would fetch all the order with pagination (note that you could swap paginate with get.
Otherwise, the callback is gonna be executed and you will execute the paginate method on $request->user()->orders(); (orders called like a method is still a query builder object, so you can call paginate on it).
The query would be:
$request->user()->orders()->paginate();
If you instead opted for the second solution in the callback you would basically add a where condition (filtering on the user_id of the orders) to the main scope to get only the user's orders.
The query would be:
Order::query()->where('user_id', $request->user()->id)->paginate();
Finally, to better control what's sent back as response I use Laravel's API Resource (and I really suggest you to do so as well if you need to customize the responses).
NOTE: The code might have syntax and/or logical errors as it was just an on the fly edit from production code, and it hasn't been tested, but it should give an overall idea for a correct implementation.
it would be better to include the if/else in your order modal like this:
class Order extends Model {
....
static function fetchFor (User $user) : Collection
{
return $user->isAdmin() ? self::all() : self::where("user_id",$user->id);
}
}
then you can call this method on your controller
public function index()
{
return view('your-view')->with('orders',Order::fetchFor(Auth::user())->get())
}
You can create scope in Order class...
For example you have field user_id in Order, for detect user
class Order
{
...
public function scopeByRole($query)
{
if (!Auth::user()->isAdmin())
$query = $query->where('user_id', Auth::user()->id);
return $query;
}
}
in you controller just get all Orders with scope:
$orders = Order::byRole()->get();
it return you orders by you role
Also you need have in class User function for detect role, example
class User
{
public function isAdmin()
{
// you logic which return true or false
}
}
I have this notifications table, where it has a status column. The status might be, among other things, "ack" and "noack". So, the notifications belongs to a user. If I want to use the ORM to see the user's notifications I'd use a hasMany() in the User model like:
public function notificaciones()
{
return $this->hasMany('App\Notification', 'dest_id', 'id');
}
And this works perfectly. (The dest_id means who the notification is for, there's another origin_id which tells who caused the Notification, any ways, this works)
Now, I want to see only the unacknoledged (noack) notifications, I was thinking on:
public function notificaciones()
{
return $this->hasMany('App\Notificacion', 'dest_id', 'id')
->where('status', 'noack');
}
But this produces an empty collection.
How can this be accomplished?
You can do something like that,
You create a new function in the same model and call that core method with the condition to fetch noack notifications.
public function noack_notifications() {
return $this->notificaciones()->where('status','=', 'noack');
}
When you call this method from controller with find.
$user = User::find($id);
dd($user->noack_notifications()->get());
This should solve your problem.
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.
I am trying to make a twitter like feed in an application, I have a database called connections where inside there's user and follow and another database called feed containing owner which would equal to the follow column in connections.
What I could do if had every id of a follower statically is to use where('owner', '=' $follow) on each follower and return it.
I tried this approach but it wasn't ideal:
Get each follower inside connections;
foreach(follower) {
Get 10 of the latest posts orderBy "created_at";
Push into array;
}
shuffle array;
limit array to 15;
return array;
That also ended with the returned array not being ordered by created date.
How would I use eloquent to get the feed item only if the user follows the owner in the best/simplest way?
Are there any specific Laravel tools that can be used?
Also the database layout isn't fixed as it is, it can be altered if needed to better suite this.
You need to create a many-to-many relationship between the user and itself. See the laravel eloquent documentation http://laravel.com/docs/eloquent#relationships. I didn't test this code but it should be enough to get you started down the right track.
Create a pivot table called "user_following" with:
(int) id, (int) user_id, (int) following_id
Then do something like this:
<?php
// Model
class User extends Eloquent {
public function following()
{
return $this->belongsToMany('User', 'user_following', 'user_id', 'following_id');
}
public function tweets()
{
return $this->hasMany('Tweet')->orderBy('created_at', 'desc');
}
}
// controller
$tweetsOfWhoImFollowing = User::find($id)->following->tweets;