Laravel Eloquent Relationship get one column from other table - laravel

I have question about Laravel Eloquent.
I have following tables
Users
-id
-email
Money
-id
-user_id
-amount
-total
User has many Money
I want to use something like $user->current_money
then i want to get the total from the last Money
i need this because i want to show all user's current money in table
#foreach($users as $user)
<tr>
<td>{{$user->email}}</td>
<td>{{$user->current_money}}</td>
</td>
#endforeach
is there good practice for this?
Thanks

I like to use appends in Laravel Eloquent in order to achieve this.
in your Users model get add the $appends array. like this
protected $appends = ['current_money'];
this will look for a method getCurrentMoneyAttribute() in the User model. it should look like this.
public function getCurrentMoneyAttribute()
{
return 0.00;
}
with the implication of the you have implanted the relationship between User and Money tables. your method should look like this,
public function getCurrentMoneyAttribute()
{
return $this->money()->latest()->first()->total;
}
and when when ever you call the $user->current_money laravel execute the query and it will get the last row of the Money that is relevant to that user.

You can use Eloquent Relationships as you are asking about and a single query to accomplish this. Using append is a slippery slope because now it will add it to all user queries and may cause bloat as the app grows. As well appending it will have 2 queries versus a single one.
In the user model you define the relationship like so:
/**
* Get money for a user
*/
public function money()
{
return $this->hasMany('App\Money', 'user_id', 'id');
}
Then you can query the user like this in a single query for performance:
$user = User::where('id', $id)->with(['money'])->first();
or
$user = User::with('money')->findOrFail($id);
or now you can eager load the money as well since the relationship is now defined in the user model.
$user = Auth::user(); // or however you are pulling the user from the DB.
$user->money;
Then loop through the different money's.

Related

Invalid argument supplied for foreach() in laravel when use Eloquent: Relationships

I wanna get the class name of a user via the user ID. When I input the ID of a user so I will wanna get the class name. I have three tables such as users table, classes table, and class_users table. The class_users table is born from two users table and classes table.
A users table has an id, name, email, password.
A classes table has an id, class_code, class_name.
A class_users table has an id, class_id, user_id
And this problem relates to Eloquent Relationships.
Thank you for help.
My Route:
Route::get('/find_classes/{id}','StudentController#find_classes');
My Controller function:
public function find_classes($id)
{
$users = User::find($id);
foreach($users->classes as $class)
{
echo $class->name . '<br';
dd($class);
}
}
My User Model:
public function classes()
{
return $this->belongsTo('App\Classes','class_users','user_id','class_id');
}
Looks like you might have the wrong relationship set up on your User model. You have a one to many set up, but your DB is setup to handle a many to many. I suggest you change your User model relationship:
public function classes()
{
return $this->belongsToMany('App\Classes');
}
Note, you may need to name the FK on that relation, as I see you have class_id on the table, but your actual class is named 'Classes'. Check through your relationships to ensure this is explicit on the FK where it doesn't follow Laravel convention exactly.
With this relationship, your foreach loop should work. It would be a good idea for efficiency, as mare96 noted, to eager load the classes on the $users collection when you query:
$users = User::with('classes')->find($id);

How to pluck unique relationships from query builder?

I need to pluck unique set of relation which are present in query set of models. I have model User with table 'users' and model Role with table 'roles', User hasMany Role.
//User.php
public function roles()
{
return $this->belongsToMany(Role::class);
}
//Role.php
public function users()
{
return $this->belongsToMany(User::class);
}
I managed it using collections, but it needs to run such a big query, what slowing whole request
//Controller
...
$users = User::query()->someChainOfScopes();
$uniqueRoles = $users->get()->pluck('roles')->flatten(1)->unique('id')->values();
...
This code returns collection which I need, but I would like to implement it using query builder to pluck unique roles for speed improvement
There are a few different ways you could do this using Laravel,
Joins could be an option to join the 2 tables in a DB::table() query and then find the users using the parameters you require and return the role_id.
To speed up users
$user_ids = DB::table('users')->select('id')->someChainOfScopes()->get();
The chained scopes you will have to change to standard where functions etc.
Then use this array to query the role_user table direct
$role_ids = DB::table('role_user')
->select('role_id')
->whereIn('user_id', $user_ids)
->distinct()
->get();
The problem with collections and plucking from collections is it has to iterate over the whole array of objects just to pull a field out. When that is a large collection then iis very costly.
I've not tested but hopefully it will get you going in the right direction.

Is there an Eloquent way of doing a leftjoin in Laravel 5.4

Is there a eloquent way to do a left join in Laravel?
We'd like to get all games and fill in the progress for each one if it exists in user_games.
Right now we've written the following solution, however this isn't eloquent at all, which we like it to be.
public function usergames($user_id) {
return DB::table('games')->leftJoin('user_games', function ($join) use ($user_id) {
$join->on('user_games.game_id', '=', 'games.id')->where('user_games.user_id', '=', $user_id); })->get();
}
DB model:
Thanks in advance!
A way to do this without you actually writing a left/inner join is to use the eloquent relationships.
In your case you will have 2 model classes: User and Game
class User extends Model {
public function games() {
return $this->belongsToMany(App\Game::class);
}
}
Now, you can access the user's games like so:
$user = App\User::find($user_id);
$usergames = $user->games; // Illuminate\Support\Collection
If you want to get a list of users with games, then look into eager loading. That would look something like this:
User::with('games')->get();
This way, Eloquent will know to lazy load the relationship meaning it will only run 2 queries. One to grab the users. and one to grab the games associated with the user, and then make them available for you in the 'games' property of the user object.

Laravel Eloquent Lazy Eager Load Count

I'm ideally looking for a function like
load('relationship')
but which loads a count in the same way
withCount('relationship')
works for eager loading.
I'm thinking it is going to be called loadCount('relationship')
loadCount() is available since Laravel 5.8
$post->loadCount('comments');
$post->comments_count;
Docs
As of Laravel 5.2, this functionality is built-in.
Provided you have a hasMany relationship between Post and Comment, do:
<?php
$posts = App\Post::withCount('comments')->get();
foreach ($posts as $post) {
echo $post->comments_count;
}
You can even eager load relationships count by default by declaring this in your model:
<?php
// Post model
protected $withCount = ['comments'];
This solution works great for me:
Create a new hasOne relationship on the related model and add a raw select to the query for the count. For example, if you want to eager load the number of tasks for a given user, add this to the User Model:
public function taskCount()
{
return $this->hasOne('App\Task')
->selectRaw('user_id, count(*) as count)
->groupBy('user_id');
}
And then eager load the count like this:
$user = User::where('email', $email)->with('taskCount')->first();
And access the count like this:
$taskCount = $user->task_count->count;

get posts by self and posts by all follows with attached user using with() or joins and order by post created_at

I have a follow system setup from this tutorial.
Creating the Twitter following model in Laravel 4
It works for getting follows and followers and for saving them. But I want to list all of my posts and all posts of the people I follow, along with the related user object for each one and order them all by the posts created_at column.
Rather than try to pick some code to show what I have tried, lets just say I have spent two days trying every combination of join(), leftJoin(), nested joins, where(), orWhere(), nested wheres, with(), joins and wheres nested in with() that I can think of and I just can't figure it out.
For the follows I have a pivot table with user_id and follow_id. Here are the relationships in my User model.
/**
* User following relationship
*/
public function follows()
{
return $this->belongsToMany('User', 'user_follows', 'user_id', 'follow_id')
->withTimestamps();
}
/**
* User followers relationship
*/
public function followers()
{
return $this->belongsToMany('User', 'user_follows', 'follow_id', 'user_id');
}
Twit.php model. (Actually my posts are called twits but same concept)
class Twit extends Eloquent {
protected $fillable = ['twit', 'user_id'];
public function user()
{
return $this->belongsTo('User');
}
}
User.php model
class Twit extends Eloquent {
protected $fillable = ['twit', 'user_id'];
public function user()
{
return $this->belongsTo('User');
}
}
I've tried talking myself through this but none of the eloquent functions seem to do what I think they should do. To be clear, here is verbally what I need to happen.
Get each twit with its user and order by twits.created_at
only where user.id = Auth::user()->id
or where user.id is in Auth::user()->follows
Help writing this out as a raw query would work too.
Thanks for any help.
UPDATE: Deleted my own answer to save others from getting confused by it since it was way off and wasn't working 100%.
The selected answer works perfectly. Here is the selected answer by #philipbrown with added eager loading for the user and ordered by the twit created_at date
$twits = Twit::whereIn('user_id', function($query)
{
$query->select('follow_id')
->from('user_follows')
->where('user_id', Auth::user()->id);
})->orWhere('user_id', Auth::user()->id)
->with('user')
->orderBy('created_at', 'DESC')
->get();
And in the view
#foreach($twits as $twit)
<li>
<div class="twit-gravitar">
<img src="{{ getGravitar($twit->user->gravitar) }}">
</div>
<div class="twit">
<div class="twit-handle">
{{link_to('/twits/'.$twit->user->username, '#'.$twit->user->username) }}
</div>
<div class="twit-text">{{ $twit->twit }}</div>
</div>
</li>
<hr class="twit-separator">
#endforeach
I'll walk through step-by-step how I would solve this problem. I find it easier to get my head around the raw query before I convert that into ORM methods, so I'll write this out as I would work through it, rather than just give you the answer.
Writing the raw query
So first I would simply get all twits (I'm guessing it's twits?):
SELECT * from twits
Next I would refine this by only selecting the from the current user (using user_id 1 as an example):
SELECT * FROM twits WHERE user_id = 1
Next we can use an SQL subselect to find all the users that the current user follows:
SELECT * FROM twits WHERE user_id IN (SELECT follow_id FROM user_follows WHERE user_id = 1) OR user_id = 1
Now if you run that on your database and change the user_id you should get a stream of twits that you were expecting.
Converting to Eloquent
Now that we have the raw query sorted, we can convert it to use Eloquent so you are returned an Eloquent Collection.
Again, first start by simply getting all twits:
$twits = Twit::all();
Next we need to use the whereIn method:
$twits = Twit::whereIn('user_id', array(2, 3, 4))->get();
But instead of passing an array of user ids, we need to pass a Closure so we can do the subselect:
$twitss = Twit::whereIn('user_id', function($query)
{
$query->select('follow_id')
->from('user_follows')
->where('user_id', '1');
})->get();
And finally we can pass in the current user to include the current user's posts:
$twits = Twit::whereIn('user_id', function($query)
{
$query->select('follow_id')
->from('user_follows')
->where('user_id', '1');
})->orWhere('user_id', '1')->get();
Now you should be returned a Collection of twits from the current user and all the users that the current user follows.
And finally you would just replace the 1 with Auth::user()->id to find the current user.
Hope that helps! :)

Resources