About the laravel eloquent model - laravel

The laravel API document: Illuminate\Database\Eloquent\Builder::first() description that:
Model|Builder|null first(array $columns = array('*'))
Execute the query and get the first result.
Parameters
array $columns
Return Value
Model|Builder|null
I can't understand the mean of the return value, In which case it will return Model and In which case it will return Builder?

When you have used eloquent model for retrieving first record it return response in Model, and not sure about builder but when you retrieve records using builder it return builder object.
for example, consider we have states table and we are going to retrieve first record using two different method
1) Query Builder
$state_builder = DB::table("states")->first();
2) Eloquent Model
$state_eloquent = State::first();
you can check the difference between both response, and when no record found it will return null.

Related

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();

Laravel using find() and get() together

I have a table 'tour2s' with 2 rows and when I do:
$tour = Tour2::find(1);
dd($tour);
it returns the tour with 'id' = 1. And it's Object.
I want to turn the object to collection of only attributes of the model, nothing else. And I know that when I use ->get() it returns collection.
But when I am trying:
$tour = Tour2::find(1)->get();
dd($tour);
It returns a collection but of all 2 tour objects (full objects, not only attributes):
I did it like:
$tour = Tour2::find(1);
$tour = collect($tour);
dd($tour);
and now it's what i what - it return a collection of only model attributes (WHAT I WANTED):
SO, my question is why when I used $tour=Tour2::find(1)->get() it returned all tours not only the one with 'id'=1 ?
Passing an array to find() will return a collection.
$tour = Tour2::find([1]);
However, it will be a collection of Tour2 objects, not only the attributes.
Then, if you want only the attributes, you could use $tour->toArray()
You could also do $tour = collect(Tour2::find(1));
And to answer your question, when you use $tour=Tour2::find(1)->get(), Laravel fetch the first tour, and then calling get() on $tour will fetch all other records, so return two tours in your case.
Ok, the main question, as i understand is: "Why when i wrote Tour2::find(1)->get() i receives collection of all records".
when you wrote Tour2::find(1) it assumes that you receive instanse of model Tour2. So we can simple write $tourInstanse->get()
If you go to \Illuminate\Database\Eloquent\Model you can see that here is no method called get() but we have a magic method __call. Look at his implementation:
public function __call($method, $parameters)
{
if (in_array($method, ['increment', 'decrement'])) {
return $this->$method(...$parameters);
}
return $this->newQuery()->$method(...$parameters);
}
So, when you call get() method on a model instance you get model`s QueryBuilder (as described in last row) and call get() method on a QueryBuilder. As a result, you receiving all records of that model Class.

Laravel sortBy not having any affect

I have a custom attribute on my User model that's calculates the length of some other tables and returns an integer value:
public function GetCurrentQueueLengthAttribute()
{
// return int
}
I then have an API endpoint that returns a "Team" with all its users (simple Spark pivot)
public function show($teamId)
{
$query = Team::query();
$query->with('users')->where('id', $teamId);
$team = $query->first();
return $team->users->sortBy('currentQueueLength');
return $team;
}
The issue is that the returned data doesn't change order. There are no errors, just the same order of the users every time.
Is there something I'm missing?
The sortBy function is not to be mistaken by the orderBy function, the first one sorts a collection, the second one alters the sql of the query builder.
To be able to use the sortBy function one first needs to retrieve the collection. These functions can still be chained by using:
return $team->users()->sortBy('currentQueueLength');
optionally one could also use orderByRaw if you are willing to write a custom sql query for the sorting.

Laravel Query Relationship on One Model Instance

I am aware that I can use count() to query for Eloquent relationships in Laravel, like so:
if(count($question->answers()))
Where answers() is a hasMany relationship:
public function answers()
{
return $this->hasMany('App\Models\Answer', 'question_id');
}
My question is, how do I do this when $question is not an entire collection but one Model instance?
$question = Question::where('id',$key)->first();
How do I query the above question, and only that question, for a potential relationship using count()?
I always am getting a count() of greater than zero, even when the selected question has no associated answers, which means my if block always runs and returns unwarranted null values:
if(count($question->answers()))
{
//returns nulls
}
Since calling $question->answers() is returning a QueryBuilder instance, calling count() on that will most likely always return 1. If you access $question->answers (as a property and not a method), or use the full logic $question->answers()->get(); it should properly return a Collection, which count() will function correctly on:
$question = Question::where('id',$key)->first();
if(count($question->answers) > 0){
// Do something
}
// OR
if(count($question->answers()->get()) > 0){
...
}
As suggested by #maraboc, you could also eager load your $question with answers using a ->with() clause:
$question = Question::with(["answers"])->where('id',$key)->first();
But even in this case, $question->answers() would still be returning a QueryBuilder instance, so access it as a property for count() to function correctly.
As already pointed count($question->answers()) has no meaning because $question->answers() is a Relation instance, you can call dynamic query method on that but if you want to count elements you need a collection, i.e $question->answers.
So you have two choice:
count the collection: count($question->answers)
ask the database to do the count: $question->answers()->count()
Parentheses matters

Eloquent find() with joins and eager loading

I want to retrieve one record via the time-tested method of this URL:
public/api/laptop/1
hitting this route:
Route::get('laptop/{id}', 'LaptopController#getLaptop');
then this controller method:
$laptop = Laptop::find($id)->addJoins()->selectListCols()->with('earmarks', 'movements')->get();
return $laptop;
Problem is this doesn't work (it returns every record). To make it work I have to do this:
$laptop = Laptop::where('laptops.id', $id)->addJoins()->selectListCols()->with('earmarks', 'movements')->get();
return $laptop;
But I'm just wondering why find() doesn't work? earmarks and movements are Many-To-One models, by the way.
find() is just a shortcut for where()->first() so it will return an object and Query Builder methods will not work with it:
User::find(1); // Will return User object with ID = 1.
That's why you need to use where(), which returns Query Builder object, so you can use with() and other builder methods to build your query.

Resources