I'm a beginner learning laravel and ran into a strange problem. I would like one table to reference the same lookup table twice. For example, if I had a messages table which stores messages between two users.
If my messages table structure is like:
id - unsigned (auto incrementing pk)
message - string
user1_id - unsigned
user2_id - unsigned
How should I set up the messages and users models so when displaying an index of the messages, it shows the user.email for user1 and user2 from the users table?
If there was just 1 user per message, I could name the column 'user_id' and then have a belongsTo relationship to the users table.
It seems like it might involve using the $appends property, but how should things be configured to retrieve the 2 users' email addresses when querying the messages table and send those along with the the message data to the view?
Someone helped me on the laracasts forum and I figured I'd post the answer here in case it's helpful.
The answer is to set up additional relationships in the message model. Like:
public function userOne() {
return $this->belongsTo('App\User', 'user1');
}
public function userTwo() {
return $this->belongsTo('App\User', 'user2');
}
Then in the view, assuming you passed the messages as $messages, in the foreach $messages as $message loop, use the optional() helper to get the data like this:
{{ optional( $message->userOne)->email }}
public function userOne() {
return $this->hasOne(User::class, 'id', 'user1_id');
}
public function userTwo() {
return $this->hasOne(User::class, 'id', 'user2_id');
}
Related
I am trying to get my Yajra Datatable working correctly but struggling.
Basically I want to get all clients appointments in which the client belongs to the logged in user. I then want to access the Client name and then the appointment data. I have used the following to get the appointment data
$user = User::find(Auth::user()->id);
$data = $user->clients()->with('appointments')->get()->pluck('appointments')->flatten();
return Datatables::of($data)
->make(true);
This allows me to show a row for each appointment that exists for the user-owned clients appointments. But how can I also access the clients name from this?
I have tried many different ways but if I use something like
$data = $user->clients()->with('appointments')->get();
I can access all of the data I need but it shows the existing clients in each row, not all appointments owned by the clients.
My setup is
User Model
public function clients(){
return $this->hasMany(Client::class);
}
public function appointments()
{
return $this->hasManyThrough(Appointment::class, Client::class);
}
Client Model
public function appointments(){
return $this->hasMany(Appointment::class);
}
public function users(){
return $this->belongsTo(User::class);
}
Appointment Model
public function client(){
return $this->belongsTo(Client::class);
}
Thank you
You might be looking for whereHas:
$appointments = Appointment::with('client')
->whereHas('client.users', function ($query) use ($user) {
$query->where('users.id', $user->id)
})
->get();
This translates to: "Give me all the appointments that belong to clients of a specific user".
Just a suggestion, it might be wise (not required at all) to alter your database structure to something more flexible. What if you for instance keep track in you appointment which user is present?
appointments
- user_id
- client_id
This would make your query a bit simpler
// In User
public function appointments()
{
return $this->hasMany(Appointment::class);
}
$user->appointments()->with('client')->get();
You could go even further by making the relation between User and Appointment, Client and Appointment many to many so you could have appointments with more than one user and/or client.
I have same table structure as mentioned in laravel document for HasManyThrough relationship hasManyThrough
countries
id - integer
name - string
users
id - integer
country_id - integer
name - string
posts
id - integer
user_id - integer
title - string
and define a relationship like same as in doc.
public function posts()
{
return $this->hasManyThrough(
'App\Post', 'App\User',
'country_id', 'user_id', 'id'
);
}
Now when I List posts of specific country. I need the information of user of the post too. I mean information from pivot table(users)
$posts = Country::find(2)->posts();
The above returns post data only..
What you need is to eager load the users alongside the posts, can be achieved via the with() method on the Builder:
$posts = Country::find(2)->posts()->with('user')->get();
If you're loading huge amounts of data and don't want the whole User instance loaded, you can even specify which fields to only be retrieved from the users table:
$posts = Country::find(2)->posts()->with('user:id,name')->get();
Then you can simply use $post->user->name or whatever you need when iterating your collection.
Refer to the documentation for more information.
Try this one:
$posts = Country::find(2)->posts()->with('user')->get();
I am using laravel eloquent. I have fetched data from two table using eloquent.
I have post table and chat table. For post table I have model Post.php and for chat table I have model Chat.php. Here is the the eloquent relation I have created to fetch chat for individual post for a user.
in Post.php
public function TeamMessage()
{
return $this->hasMany('App\Chat','post_id');
}
And in Chat.php
public function ChatRelation()
{
return $this->belongsTo('App\Post');
}
it is working perfect. But this relation fetch all messages for a specific post. I want to fetch all unread message from chat table. I have a column named unread in chat table.
Now my question is how I can fetch only unread message for a specific post.
While the other answers all work, they either depend on scopes (which are very useful in many circumstances) or on you having already instantiated an instance of $post, which doesn't let you eager load multiple posts with their messages.
The dynamic solution is this, which will let you fetch either 1 or more posts and eager load their messages with subquery:
$posts = Post::with(['TeamMessage' => function ($query) {
$query->where('unread', true); // This part applies to the TeamMessage query
}])->get();
See in documentation
Edit:
If you, however, want to filter the posts, to only show those that have unread messages, you need to use whereHas instead of with:
$posts = Post::whereHas(['TeamMessage' => function ($query) {
$query->where('unread', true); // This part applies to the TeamMessage query
}])->get();
More in the documentation.
You can also chain whereHas(...) with with(...).
For querying relationships, you have to call them as functions instead of properties, like this:
$unreadPosts = $post->TeamMessage()->where('unread', true)->get();
For more information on this you can take a look at the docs.
You need to create a local scope on your model, information on local scopes can be found here: https://laravel.com/docs/5.6/eloquent#local-scopes
public function scopeUnread($query)
{
return $query->where('unread', 1);
}
Then in your controller/view
$unread = $yourmodel->unread()
First I would change your relation names to the name of the entity in lower case:
in Post.php
public function chats()
{
return $this->hasMany('App\Chat','post_id');
}
And in Chat.php
public function post()
{
return $this->belongsTo('App\Post');
}
public function scopeUnread($query)
{
return $query->where('unread', 1);
}
Then you can use
$post->chats()->unread()->get();
I have the following table:
The table is called user_eggs and it stores the user eggs.
eggs are items with additional data (hatch_time)
As you can see, user 2 has 2 eggs, which are items 46 and 47.
My items table stores the item general information such as name, image, description, etc...
How I can return the user eggs using $user->eggs() including the item data in my items table of the egg item_id?
I tried:
User Model:
/**
* Get the eggs
*/
public function eggs()
{
return $this->belongsToMany(Egg::Class, 'user_eggs','user_id','item_id')
->withPivot('id','hatch_time');
}
but $user->eggs() returns an empty array.
Any ideas?
A simple approach will be:
in your UserEgg model define:
/**
* Get the user associated with egg.
*/
public function _user()
{
return $this->belongsTo('App\User','user_id');
}
/**
* Get the item associated with egg.
*/
public function item()
{
return $this->belongsTo('App\Item','item_id');
}
then in your controller:
use the model to extract everything like this:
$userEggs = UserEgg::where('user_id',2)->get();
foreach($userEggs as $userEgg){
$associateduser = $userEgg->_user;
$associatedItem = $userEgg->item;
}
Short answer
If you loop through the user's eggs:
foreach($user->eggs as $egg){
$item = Item::find($egg->pivot->item_id);
}
If you want to query:
$user->eggs()->wherePivot('item_id', 1)->get();
Long answer
From the Laravel Documentation
Retrieving Intermediate Table Columns
As you have already learned, working with many-to-many relations requires the presence of an intermediate table. Eloquent provides some very helpful ways of interacting with this table. For example, let's assume our User object has many Role objects that it is related to. After accessing this relationship, we may access the intermediate table using the pivot attribute on the models:
$user = App\User::find(1);
foreach ($user->roles as $role) {
echo $role->pivot->created_at;
}
Notice that each Role model we retrieve is automatically assigned a pivot attribute. This attribute contains a model representing the intermediate table, and may be used like any other Eloquent model.
By default, only the model keys will be present on the pivot object. If your pivot table contains extra attributes, you must specify them when defining the relationship:
return $this->belongsToMany('App\Role')->withPivot('column1', 'column2');
If you want your pivot table to have automatically maintained created_at and updated_at timestamps, use the withTimestamps method on the relationship definition:
return $this->belongsToMany('App\Role')->withTimestamps();
Filtering Relationships Via Intermediate Table Columns
You can also filter the results returned by belongsToMany using the wherePivot and wherePivotIn methods when defining the relationship:
return $this->belongsToMany('App\Role')->wherePivot('approved', 1);
return $this->belongsToMany('App\Role')->wherePivotIn('priority', [1, 2]);
In my user model I have added custom attribute get number of post user have created.
I can successfully get that but when I have try to order by it give error.
1054 Unknown column 'posts' in 'order clause'
Can you let us know what is the Eloquent query you're running? Also make sure that the name of the table you're using is correct.
EDIT:
Does the posts column exist in the users table? Or are you trying to get all the posts from the Post Model and order the posts by their name in descending order?
If that is what you want to do then you would need to go to your User Model and set up your relationship with posts.
In User Model create this method:
public function posts() {
return $this->hasMany('Post'); // Post would be the name of the Posts Model
}
In Post model do this:
public function user() {
return $this->belongsTo('User'); // Where User is the name of the Users Model
}
Then according to the Laravel documentation you could do someonething like this with the query:
$users = User::with(array('posts' => function($query)
{
$query->orderBy('name', 'desc'); // assuming that 'name' would be the column in the posts table you want to sort by
}))->take(5)->get();
I hope I am correct and this helps and that solves your issue.