Call to a member function addEagerConstraints() on integer - laravel

I tried to eager load a relation:
$tournaments = Tournament::with('numCompetitors')->latest()->paginate(config('constants.PAGINATION'));
My relation in Tournament returns an integer:
public function numCompetitors()
{
return $this->competitors()->count(); // it returns 24
}
With that I get:
Call to a member function addEagerConstraints() on integer
I don't understand why is it failing.

You're doing it wrong. If you want to count relationship, use withCount() with properly defined relationship:
Tournament::withCount('competitors')->latest()->paginate(config('constants.PAGINATION'));
If you want to count the number of results from a relationship without actually loading them you may use the withCount method, which will place a {relation}_count column on your resulting models.
https://laravel.com/docs/5.4/eloquent-relationships#counting-related-models

Related

How to access one relation inside another relation in laravel?

I have a query in which I have eagar loaded two models using with function like this:
ModelA::with(['relationB', 'relationC.relationC.A'])->where(condition)->get();
So, ModelA has two relations like this:
public function B(){ return $this->blongsTo(B::class);}
public function C(){ return $this->blongsTo(C::class);}
Now, my requirement is that I want to add a condition in B() function like this:
public function C() {
if($this->B->status) {
return $this->blongsTo(C::class)->withTrashed();
}
return $this->blongsTo(C::class);
}
But it return null on line this statement:
if($this->B->status)
Here is the error message
Trying to get property 'status' of non-object
My ultimate requirement is that using one relation function I want to fetch deleted records and non deleted based on the condition, but somehow it is not working.
My laravel application version is 7.30.4.
A relational function (such as your public function C()) works a bit of magic under the hood. This is because really it is designed to be called in a query way like you show already with the ::with(['relationB', ...]).
However, because of this, if you were to eager load C, then $this is not yet loaded as the full model, and therefore B is not defined (this is assuming that modelA always has a B relation). If you were to dd($this) while performing your query, you'd see that the result would be a model without any attributes.
Getting this to work from within a relational function (with the goal of eager loading) is very difficult. You're probably better off doing the logic elsewhere, with a second query for example. This is because within the relational function, there is no way to know who or what the potential target is. However, if you only use it after modelA is loaded, then it works without issues.
You can do some things with a whereHas, but then you'd still have to do 2 queries, or you can try and see if you can get it done with an SQL IF statement, but that will not result in a relation.

Undefined relationship error in sorting of existence and non existence relationship using Eager Load in Laravel eloquent model

I have a Student model and a corresponding one to one mapping relationship to Result model.
I have an eligibleList array containing a list of student id whose marks are to be displayed. Some student have results while some does not have but i need to display all of them from the list.
I am able to retrieve and display students using the following:
$students = Student::with('result:student_id,marks')->whereIn('students.id', $eligibleList)->get();
foreach($students as student) {
if ($student->result != null)
Log::debug($student->result->marks)
else
Log::debug("-1") //-1 indicate no results
}
The above has no issue until i need to sort the list (ascending or descending) by the marks. I tried the following:
$students = Student::with(['result:student_id,marks' => function ($query) {
$query->orderBy('marks','DESC');
}])->whereIn('student.id', $eligibleList)->get();
It throws me a "Call to undefined relationship" error. Is there anyway to sort from the query ? I avoid sorting the collection as it can get very slow for thousands of records. Somehow eloquent early loading encounter some error when sorting with non existence relationship.
you should use 'Subquery Ordering', ordering inside 'with' will not sort the overall result.
$students = Student::with(['result:student_id,marks'])->whereIn('student.id', $eligibleList)
->orderByDesc(Result::select('marks')->whereColumn('student_id','students.id'))
->get();
https://laravel.com/docs/7.x/eloquent#advanced-subqueries
if you use laravel 5 you have to use 'join':
Student::with(['result:student_id,marks'])->whereIn('student.id', $eligibleList)
->join('result','result.student_id','student.id')
->select('user.*,result.marks')->orderBy('result.marks')->get();
'join' use table name not the relation name, so please be careful about table name in previous 'join' and 'select' statements

In Laravel Eloquent, how to check for multiple relationship existence in one "has"?

So, in order to check the existence of a relationship on a model, we use the has function on the relationship like model1->has('relationship1').
While it is possible to supply the model1->with() function with an array of relations to eager load them all, both has and whereHas functions do not accept arrays as parameters. How to check for the existence of multiple relationships?
Right now, I am running multiple has functions on the same model (The relations are not nested):
model1->has('relationship1')
->has('relationship2')
->has('relationship3')
But that is tedious and error-prone. Solution anyone?
There unfortunately isn't a way to pass an array of relationships to has() or whereHas(), but you can use a QueryScope instead. On your Model, define the following:
public function scopeCheckRelationships($query){
return $query->has("relationship1")->has("relationship2")->has("relationship3");
}
Then, when querying your Model in a Controller, simply run:
$result = Model::checkRelationships()->get();
The function name to use a Scope is the name of the function, minus the word scope, so scopeCheckRelationships() is used as checkRelationships().
Also, it's actually possible to pass the relationships you want to query as a param:
public function scopeCheckRelationships($query, $relationships = []){
foreach($relationships AS $relationship){
$query->has($relationship);
// Might need to be `$query = $query->has(...);`, but I don't think so.
}
return $query;
}
...
$result = Model::checkRelationships(["relationship1", "relationship2", "relationship3"])->get();
In case you need this to be dynamic.
Here's the documentation for Query Scopes if you need more info: https://laravel.com/docs/5.8/eloquent#query-scopes

Laravel Eloquent - sync() on a Morph relationship

It seems like there is no sync() function for morph tables on Laravel.
I have two tables avails and questions. questions is a morphMany table. I want to use the sync command, and this is what I did:
Avail::find($id)->questions()->sync($some_ids);
This gives me the following error:
Call to undefined method Illuminate\Database\Query\Builder::sync()
So is there a way to get sync to work or am I just not doing this right?
morphMany is a 1-to-many, not many-to-many relation, so there is no sync method.
Use saveMany/save instead, and associate for the other way around.
to mimic sync behaviour for this kind of relation you can do this:
$questionsOld = $avail->questions()->get();
$questionsOld->each(function ($question) {
// appropriate fields here:
// 1*
$question->FOREIGN_ID = null;
$question->FOREIGN_TYPE = null;
$question->save();
});
$questionsNew = Question::whereIn('id', $someIds)->get();
// *2
$avail->questions()->saveMany($questionsNew->getDictionary());
Now:
*1 you can't use dissociate and must explicitly set relation_id and relation_type to null, since morphTo doesn't override this method, so it wouldn't work as expected.
*2 getDictionary() returns simple array of models, instead of collection. It's required becase saveMany typehints its parameter as array.

Using Laravel's ORM - what's the difference between the longhand and the shorthand?

For instance:
return Product::first()->BaseProduct->Products
works, whereas
return Product::first()->BaseProduct()->Products()
does not, and I get a BadMethodCallException.
I understand that there's a notable difference, then, between those two lines, but what is difference, and how does it work?
I'm assuming that BaseProduct() and Products() are both relationships in your models? Calling Products() won't return eloquent objects, it'll return a hasMany or belongsToMany (children of Relation) object.
Calling Products instead of Products() triggers a magic get method. This magic get calls the getResults() method on the Relation object. This way you actually get back a collection of the Product models. This is typically the way that you should work with relationships.
In other words:
BaseProduct::first()->Products == BaseProduct::first()->Products()->getResults()
I suggest that you have a look at the source code

Resources