Laravel 4 Eager Loading with ::find() - laravel-4

Say I have two tables shifts and users. Each shift would have a user associated with it. I wanted to get the JSON return of the shift and the user. I tried the following command:
return Shift::find($id)->with('User')->get()
and this doesn't work. However, this does:
return Shift::where('id', '=', $id)->with('User')->get()
Why? How can I use the first command?

get and find are both execution methods. That is, they will fire the query and return a result. Basically, you shouldn't be using two of them in a single call. It's either find, get, or first, not a combination of them.
find will return a single model and create a condition based on the primary key of the model, usually where('id', '=', $id)
getwill return a collection of models.
first will return a single model.
It sounds like you need:
return Shift::with('user')->find($id);
Where user is the relationship method on your Shift model.
public function user()
{
return $this->belongsTo('User');
}

Related

Call to a member function first() on null

When I try to fetch my user data, I receive the error
Call to a member function first() on null
public function show($id) {
$user=User::findOrFail($id);
$employee = $user->employees->first();
return view('admin.profile')
->with(['employee' => $employee , 'user' => $user]);
}
The problem is probably in your User model.
Check that you have declared the employees relationship:
public function employees()
{
return $this->hasMany(Employee::class); // I'm assuming you have a Employee model with expected column names, but feel free to replace everything with what you actually have in your app
}
If the problem persists, edit your question with your tables structure and your models.
It's very useful to understand the difference between $user->employees and $user->employees().
$user->employees: returns a Collection of employee models, or null if none are found.
$user->employees(): returns a query builder instance that you can chain additional conditions to (where's, etc).
Both options have a first() option available to them, but one is using a Collection method, where the other is using the query builder method.
Some have already suggested this, and I will as well - the safer and simplest solution to your problem is to use the query builder version of the relationship, since there is no risk of the employees() result being null. It also has the added benefit of not needing to load the entire relationship into a collection just to get the first result.
In short: $user->employees()->first(); is the best way to go.

What is difference between $this->Products and $this->Products() in laravel model?

I got different result from getReward1 and getReward2:
Model:
class User extends Authenticatable
{
public function Products()
{
return $this->hasMany('App\Product', 'user_id');
}
public function getReward1()
{
return $this
->Products
->where('reward', '>', 0)
->where('status', 0)
->sum('reward'); // sum = 7,690,000
}
public function getReward2()
{
return $this
->Products()
->where('reward', '>', 0)
->where('status', 0)
->sum('reward'); // sum = 7,470,000
}
}
getReward1 return 7,690,000 and getReward2 return 7,470,000 (Two different values)
What is difference between $this->Products and $this->Products() ?
$this->products;
// Returns a Collection
$this->products();
// Returns a Relation instance, which is a query builder and can be of type HasMany, BelongsTo...
$this->products()->get();
// Is EXACTLY like doing $this->products for the first time.
The main difference is that products() is just a query that hasn't been executed yet, whereas products are the actual results of this query.
Honestly, even if the name is the same and can be confusing, there are no other similarities between them.
A simple analogy:
DB::table('products')->where('user_id', 18); //could be the $user->products()
DB::table('products')->where('user_id', 18)->get(); //could be $user->products
It's just an analogy, it's not exactly like this internally, but you get the point.
To add more confusion on top of it, Collection methods are ofter similar to those you find in queries; both have where(), first()...
The main thing to remember is that with parentheses, you are still building a query. Until you call get or first, you remain in a query builder.
Without, you already have your results, you are in a Collection (https://laravel.com/docs/8.x/collections).
About the difference you get between getReward1 and getReward2, it's hard to tell exactly what's happening without seeing your database structure.
It can be a lot of things, but when you are calling the sum method, you are calling it on a Collection instance in getReward1 and on a query builder in getReward2 (you are actually executing a query with SELECT SUM(reward)...).
$this->Products() will return an instance of the query builder. The subsequence where clauses will constrain the DB query and then return only the product that you want. These will not be stored in the model instance.
$this->Products will get all of the products from the DB and store them in the model instance as an Eloquent Collection. The subsequent where clauses will then be performed on the Eloquent Collection.
Essentially, the method is doing everything in the DB, whereas, the property is fetching all of the rows and then limiting it with PHP.

Prevent duplicate queries and N+1 problem in Laravel collection

I'm currently working on a simple Laravel project where I need to get the posts of the users I'm following. With the code below, I can get the posts but I also add a lot of duplicate queries and an N+1 issue on the Authenticated user. So it's becoming sort of a head scratcher. I've looked though other similar scenarios online but I haven't been able to pinpoint what I'm doing wrong. Perhaps there is a better way. Currently, I have on the User model:
public function usersImFollowing()
{
return $this->belongsToMany(User::class, 'follow_user', 'user_id', 'following_id')
->withPivot('is_following', 'is_blocked')
->wherePivot('is_following', true)
->wherePivot('is_blocked', false)
->paginate(3);
}
public function userPosts()
{
return $this->hasMany(Post::class, 'postable_id', 'id')
->where('postable_type', User::class);
}
As you can see, I am using two booleans to determine if a user is following or is blocked. Also, the Post model is a polymorphic model. There are several things I've tried, among them, I tried a hasManyThrough, without using the hasMany Posts relationship above. It got the posts for each user but since I'm using the booleans above, I couldn't use them in the hasManyThrough, it simply got the posts based on the following_id, whether or not the user was following or was blocked became irrelevant.
Then in a separate service class, I tried the methods below (I'm using a separate class to maintain the code easier). They both get the posts for each user but add an N+1 problem and 12 duplicate queries based on 5 posts from 2 users. I will also need to filter the results based on some conditions, so it will probably add more queries. Additionally, I'm using a Laravel resource collection that would pull other items for each post, such as images, comments, etc., so the amount of queries would increase even more. Not sure, perhaps I'm doing too much and there is an easier way:
Either:
$following = $request->user()->usersImFollowing();
$posts = $following->map(function($user){
return $user->userPosts()->get();
})->flatten(1);
return $posts;
Or
$postsfromfollowing = [];
$following = $request->user()->usersImFollowing()->each(function($user) use (&$postsfromfollowing){
array_push($postsfromfollowing,$user->userPosts);
});
$posts = Arr::flatten($postsfromfollowing);
return $posts;
Maybe you could use scopes to do little celanup of code and generated sql.
In User model something like
public function scopeIsFollowedBy(Builder $query, int $followerId) {
return $query->where('following_id', '=', $followerId);
}
And in Post model
public function scopeIsFollowedBy(Builder $query, int $followerId) {
return $query->whereHas('user', function($q) use ($followerId) {
$q->isFollowedBy($followerId);
});
}
You can use it then in coltroller like any other condition like this:
Post::isFollowedBy($followerId)->...otherConditions...->get();
The SQL generated won't go through foreach but only add one IF EXISTS select (generated by whereHas part of the code)
More on local scopes in Laravel is here https://laravel.com/docs/8.x/eloquent#local-scopes

Why does groupBy() work but Count() does not in laravel eloquent model function?

I need to get counts of all the records based on belongsToMany relationship. normally I can use groupBy() in a function inside the model. but if I use count() or withCount() inside a model function, i get the error as followed:
function code:
public function TaskCount(){
return $this->belongsToMany(User::class)->count();
}
Error message:
Symfony\Component\Debug\Exception\FatalThrowableError: Call to a member function addEagerConstraints() on int in file /Users/dragonar/Dev/iyw/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php on line 560
If I do the following...
public function TaskCount(){
return $this->belongsToMany(User::class)->Count();
}
//expected record is 4(int)
//output is 4(array) user records.
...it gives me data but like 4 records of the user instead of a number 4. The user data is useless. The only thing needed is totalCount for those records.
Relationship methods have to return Relation type objects. You are returning the result of a query, count() returns a number not the Relation object / Builder. Remove the count from that statement you are returning. Renamed the relationship tasks here.
public function tasks()
{
return $this->belongsToMany(User::class);
// this returns a Relation type object a BelongsToMany object
}
Where you need to use that relationship you can then use count:
$something->tasks()->count();
Or you can load the count of the relationship using loadCount:
$something->loadCount('tasks');
$something->tasks_count;
Or via eager loading for a collection:
$results = Something::withCount('tasks')->get();
foreach ($results as $model) {
echo $model->tasks_count;
}
If you really wanted to you could create an accessor to get the count as well, you just may want to avoid the N+1 issue by preloading the relationship and using the dynamic property to access it in the accessor.
These relation objects are Builders. When you called groupBy on it previously that is returning the Builder, it isn't executing the query. You can add where conditions and order by statements because they are just building the query, not executing it, they return the builder you are calling the method on.
Laravel 6.x Docs - Eloquent - Relationships - Counting Related Models withCount loadCount
Why not use: Task::all()->count(); ?
you can use the withCount method while calling relation like this
User::withCount('images')->get();
You can add get the data and just count it.
public function TaskCount(){
return $this->belongsToMany(User::class)->get()->count();
}
You can call it like
$taskCount = $task->TaskCount();

Method on user model to check variable on another table

I have a user model, and I want to check if a user has been assigned a mentor.
public function mentorapplication()
{
return $this->hasMany(mentorApplication::class, 'user_id');
}
public function mentorAssigned()
{
return ($this->mentorapplication()->status == "counsellorAssigned");
}
when I call $user->mentorAssigned() in tinker I get an undefined property for status.
Any ideas?
A couple of things here. Firstly, your relationship is has-many, which is one-to-many. When you try to use the relationship you should expect a Collection to be returned, rather than a single entity. Therefore trying to access the ->status property on returned collection won't work.
Secondly, when you're trying to access the relationship like this:
$this->mentorapplication()
You will get a HasMany instance, which would allow you to chain on more constraints to the query, just like ->where(...), or ->orderBy(...). You would need to use something like ->get() or ->first() to actually run the query and get the results you're after. You can omit the () here and Laravel will load the relation for you, returning your collection:
$this->mentorapplication
Now, I don't know exactly what it is you're trying to achieve, but say for instance you wanted to see if at least one of the mentorApplication objects assigned to the user has a status of counsellorAssigned, you could do something like this:
public function mentorAssigned()
{
foreach ($this->mentorapplication as $mentorapplication) {
if ($mentorapplication->status == 'counsellorAssigned') {
return true;
}
}
return false;
}
Or you could use Laravel's Collection methods to help you rather than just doing a loop here. There are many approaches you could take so I will just leave it there.

Resources