Call to a member function first() on null - laravel

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.

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

Overriding Laravel get and first methods

I need to override above mentioned methods to skip some database records. Using where is not an option since I would have to use it every single time as there are records in database that I do not need most of the time and I am not allowed to delete them from DB. Here is my attempt of doing this:
class SomeTable extends BaseModel {
public static function first() {
$query = static::query();
$data = $query->first();
if($data && $data->type == 'migration_type') return null;
return $data;
}
public static function get() {
$query = static::query();
$data = $query->get();
foreach($data as $key => $item) {
if($item->type == 'migration_type') unset($data[$key]);
}
return $data;
}
}
The problem with this code is that it works only when direct called on model. If I am using some other functions, like where, before get or first methods, it just skips my overridden method.
What would be the right way to do this and should I put this code within model?
My question is not duplicate as in the answer from mentioned question it is said:
all queries made from Models extending your CustomModel will get this new methods
And I need to override those two functions only for specific model, not for each one in application as not all tables have type column. That's the reason why I have written them within model class.
I need to override above mentioned methods to skip some database records.
Consider a global query scope on the model.
https://laravel.com/docs/5.8/eloquent#global-scopes
Global scopes allow you to add constraints to all queries for a given model. Laravel's own soft delete functionality utilizes global scopes to only pull "non-deleted" models from the database. Writing your own global scopes can provide a convenient, easy way to make sure every query for a given model receives certain constraints.
The issue here is that the where() method on the model returns a QueryBuilder instance where get() will return a Collection instance.
You should be able to override collection's default methods by adding a macro in it's place and can be done like so...
Collection::macro('toUpper', function () {
return $this->map(function ($value) {
return Str::upper($value);
});
});
Extending the query builder instance is not so easy but a good tutorial exists here and involves overriding the application's default connection class, which is not great when it comes to future upgrades.
Because after calling where you're dealing with the database builder and theses methods inside your model aren't being called .. about the issue you might overcome it by using select instead of first directly so will deal with the builder ..
example:
SomeTable::select('col1','col2')->take(1)->get();
another thing overriding these kind of methods is not a good idea if you're working with other developer on the same project.
good luck

Can I send a variable into laravel model?

So I'm trying to add get a specific data from a related table using the below method, but I don't know if that is the correct way to do it. here is what it looks like.
public function transspecific($lid){
return $this->belongsTo('raplet\Keepertrans')->where("lang_id", $lid);
}
and then I try to get data from it
dd($akeeper->transspecific($akeeper->id));
it doesn't act like there is anything but when I type dd("hello") inside the model, it works. so clearly I have something wrong with my relationship context
What are you are trying to do is adding a [dynamic scope in laravel][1] model, which is totally fine. Except you need to declare the scope seperated from relationship method.
Relationship:
public function keepertrans(){
return $this->belongsTo('raplet\Keepertrans');
}
Scoped:
public function transspecific($lid){
return $this->keepertrans()->where("lang_id", $lid);
}
Then you can call the scope with a get() to execute the query builder:
(SomeOtherRelatedModel::first())->transspecific($someId)->get();
The methods available in Eloquent model for relationship are different than what you need. Whenever you need to add a custom function which internally adds some filters to your query (builder), you need to use scopes
The generic rule of scope function is scope + yourfunction
In your case you will need to create scopeTranspecific function.
Each scope gets the first argument as builder which you update inside the function. The later arguments are optional.
public function scopeTranspecific($query, $lid){
return $query->keepertrans()->where("lang_id", $lid);
}
And then you can use it :
Model::where('column1' , 'value')->transpecific($id)->get()
If you just dump it without ->get() you will get the query builder instance. You will have to do ->get() to get the data

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.

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

Resources