Evaluate existence of relationship as true or false - Laravel Relationships - php

I'm trying to create a policy like the following:
public function view(User $user, PersonalStatement $personalStatement)
{
return $user->applicant->id == $personalStatement->applicant_id || $user->mentor->exists();
}
I then test it with a User model where mentor relationship exists, but applicant relationship does not, and I get the error:
Trying to get property 'id' of non-object
I believe this is because the user->applicant returns null in this case.
What's the best way to evaluate if a model has a relationship where the response is either true or false?
Using exists() method returns null if the relationship does not exist.

To handle null case, you may use optional Laravel helper, it allows you to call any property/method even if your source is null, it returns null instead of throwing an exception.
public function view(User $user, PersonalStatement $personalStatement)
{
return optional($user->applicant)->id == $personalStatement->applicant_id || $user->mentor->exists();
}

To check the 'applicant" relationship of 'User' model, try
$user->applicant()->exists()
OR
$user->applicant()->count()
Note: Brackets after the relationship does really matter.

Related

Laravel 8 - Call to a member function addEagerConstraints() on null [duplicate]

This question already has answers here:
Call to a member function addEagerConstraints() on null
(5 answers)
Closed 1 year ago.
I have a user model that can be a client or a provider.
What I need is to load a different relation depending on what type of user it is
If the user is a Client, I must load their relationship with my client model, and exactly the same in the case of being a provider
To do this, I first get the personal data with the person relationship (which works as expected) and then I try to load the conditional relationship as shown below
$users = User::with('person')->get();
$users->load('entity');
But in my browser, i receive this error
Error Call to a member function addEagerConstraints() on null
This is the function of my relationship
public function entity() {
switch($this->type) {
case 'Client':
return $this->hasOne(Client::class,'id','client_id');
break;
case 'Provider':
return $this->hasOne(Provider::class,'id','provider_id');
break;
}
}
That's because some of your users are not of type Client or Provider, therefore and because of how you defined the entity() relationship, it may doesn't exist for some of them.
Avoid any conditional within your relationships and consider using polymorphic relationships for this scenario since your User model can be related to more than 1 Model class.
You could simplify your model and database by using entitable_id and indentitable_type within your users table and defining your relatiionship such as:
Entity.php model
public function entitable()
{
return $this->morphTo();
}
----------------------------
Users.php model
public function entity()
{
return $this->morphOne(Entity::class, 'entitable');
}
EDIT:
If you want to keep it this way, you can try something like mapping $users
$users = $users->map(function($user){
return $user->type == 'Client' || $user->type == 'Provider' ? $user->load('entity') : $user;
});

Laravel 5.8: Eloquent where clauses returns null but does not execute if..empty condition

I have a Controller method like this:
public function postNewWallet(Request $request, $user)
{
$find = UserWallet::where('user_id',$user)->where('wallet_id', $request->wallet_choose)->get();
// dd($find);
if(empty($find)){
UserWallet::create([
'user_id' => $user,
'wallet_id' => $request->wallet_choose,
'balance' => 0
]);
flash()->overlay('Submitted', 'Information submitted', 'success');
}else{
flash()->overlay('Warning!', 'User has the defined wallet', 'warning');
}
return redirect()->back();
}
So I have added two where clauses to check if any data exists with the user_id of $user AND wallet_id of $request->wallet_choose.
Then if $find returns empty, add a new record. Otherwise shows an alert warning message.
Now I test this for a user who does not have any record on user_wallet table but it returns the Warning message User has the defined wallet somehow.
However if I uncomment dd($find), I get this as result:
Illuminate\Database\Eloquent\Collection {#2551 ▼
#items: []
}
So it seems to be empty. But why does not add new record to the table?
empty($collection) will always return false. If you want to know if a Collection is empty you call isEmpty() on it, $collection->isEmpty(). You could also call first() instead of get() on the Query Builder to attempt to retrieve the first record instead of all records; first() returns the Model instance or null, which would work with your empty($result) check.
In your code here you are never actually using anything from this Collection though as you are just doing a check to see if records exist it seems. You should use the exists method on the Query Builder to do a query that checks for existence so you don't have to return records that you are never using.
$exists = UserWallet::where('user_id', $user)
->where('wallet_id', $request->wallet_choose)
->exists();
// $exists is a bool
Laravel 5.8 Docs - Collections - Available Methods isEmpty
Laravel 5.8 Docs - Query Builder - Aggregates - Determining If Records Exist exists

Avoid query to check if relation exists in Laravel

In Laravel I've 2 models Man and Car, and the Car model has a foreign key man_id.
Now I would like to check if a Car has an owner, so I can do
if($car->man != null)
I could also do:
if($car->man_id != null)
but I don't like this solution...
But in this way Laravel do a query to check if Man exists, is there a way to avoid this useless query?
You can create a new function on your Car model that checks for its owner.
Car.php
class Car extends Model {
public function hasOwner()
{
return empty($this->man_id); // returns boolean
}
}
Then, you can just do
$car = Car::first();
if ($car->hasOwner()) { ... }
UPDATE:
You can also use exists() method
$car->man()->exists(); // bool: true if there is at least one row
As of Laravel 5.5 or higher, you can also use doesntExist() which will determine if no rows exist for the current query.
$car->man()->doesntExist(); // bool: true if no row exists

Custom model method to get a relation in Laravel

I try to define a custom Model method in Laravel. I have a n:m relation between Subscription and Notification over SubscriptionNotification.
I already defined the default relations:
public function subscription_notifications() {
return $this->hasMany('App\SubscriptionNotification');
}
public function notifications() {
return $this->belongsToMany('App\Notification', 'subscription_notifications');
}
Now I want to define a method, which returns a collection of notifications. I collect the IDs of the notifications I want in an array and write the following method:
public function notifications_due() {
// Collect $notification_ids
return $this->belongsToMany('App\Notification', 'subscription_notifications')->whereIn('notifications.id', $notification_ids)->get();
}
But when I want to use the mothod by $subscription->notifications_due, I get the following error:
[LogicException]
Relationship method must return an object of type Illuminate\Database\Eloquent\Relations\Relation
I'm new to Laravel (I come from Rails). I don't know if this is in Laravel even possible. Maybe someone can help me. Thanks!
Remove the ->get() part in the method notifications_due. get() will return a Collection, but when calling the method as a property (or magic method), Laravel expects the method to return an instance of Relation. Laravel will then execute the query and transform it to a Collection automatically.
Also, you can use your already defined notifications() method:
public function notifications_due() {
// Collect $notification_ids
return $this->notifications()->whereIn('id', $notification_ids);
}
Remove the get call from your relationship method, for example:
public function notifications_due() {
return $this->belongsToMany(
'App\Notification',
'subscription_notifications
')->whereIn('notifications.id', $notification_ids);
}
Use it just same:
// It'll return a collection
$dues = $subscription->notifications_due;
To get all the ids from the collection you may try this:
$ids = $dues->pluck('id');
Also, you may add more constraints if you want if you use it like:the
$dues = $subscription->notifications_due()->where('some', 'thing')->get();
Or paginate:
$dues = $subscription->notifications_due()->where('some', 'thing')->paginate(10);

Laravel Check If Related Model Exists

I have an Eloquent model which has a related model:
public function option() {
return $this->hasOne('RepairOption', 'repair_item_id');
}
public function setOptionArrayAttribute($values)
{
$this->option->update($values);
}
When I create the model, it does not necessarily have a related model. When I update it, I might add an option, or not.
So I need to check if the related model exists, to either update it, or create it, respectively:
$model = RepairItem::find($id);
if (Input::has('option')) {
if (<related_model_exists>) {
$option = new RepairOption(Input::get('option'));
$option->repairItem()->associate($model);
$option->save();
$model->fill(Input::except('option');
} else {
$model->update(Input::all());
}
};
Where <related_model_exists> is the code I am looking for.
In php 7.2+ you can't use count on the relation object, so there's no one-fits-all method for all relations. Use query method instead as #tremby provided below:
$model->relation()->exists()
generic solution working on all the relation types (pre php 7.2):
if (count($model->relation))
{
// exists
}
This will work for every relation since dynamic properties return Model or Collection. Both implement ArrayAccess.
So it goes like this:
single relations: hasOne / belongsTo / morphTo / morphOne
// no related model
$model->relation; // null
count($model->relation); // 0 evaluates to false
// there is one
$model->relation; // Eloquent Model
count($model->relation); // 1 evaluates to true
to-many relations: hasMany / belongsToMany / morphMany / morphToMany / morphedByMany
// no related collection
$model->relation; // Collection with 0 items evaluates to true
count($model->relation); // 0 evaluates to false
// there are related models
$model->relation; // Collection with 1 or more items, evaluates to true as well
count($model->relation); // int > 0 that evaluates to true
A Relation object passes unknown method calls through to an Eloquent query Builder, which is set up to only select the related objects. That Builder in turn passes unknown method calls through to its underlying query Builder.
This means you can use the exists() or count() methods directly from a relation object:
$model->relation()->exists(); // bool: true if there is at least one row
$model->relation()->count(); // int: number of related rows
Note the parentheses after relation: ->relation() is a function call (getting the relation object), as opposed to ->relation which a magic property getter set up for you by Laravel (getting the related object/objects).
Using the count method on the relation object (that is, using the parentheses) will be much faster than doing $model->relation->count() or count($model->relation) (unless the relation has already been eager-loaded) since it runs a count query rather than pulling all of the data for any related objects from the database, just to count them. Likewise, using exists doesn't need to pull model data either.
Both exists() and count() work on all relation types I've tried, so at least belongsTo, hasOne, hasMany, and belongsToMany.
I prefer to use exists method:
RepairItem::find($id)->option()->exists()
to check if related model exists or not. It's working fine on Laravel 5.2
After Php 7.1, The accepted answer won't work for all types of relationships.
Because depending of type the relationship, Eloquent will return a Collection, a Model or Null. And in Php 7.1 count(null) will throw an error.
So, to check if the relation exist you can use:
For relationships single: For example hasOne and belongsTo
if(!is_null($model->relation)) {
....
}
For relationships multiple: For Example: hasMany and belongsToMany
if ($model->relation->isNotEmpty()) {
....
}
I use for single relationships: hasOne, belongsTo and morphs
if($model->relation){
....
}
Because if condition is null, this will be false.
For multiple relationships: hasMany, belongsToMany and morphs
if ($model->relation->isNotEmpty()) {
....
}
You can use the relationLoaded method on the model object. This saved my bacon so hopefully it helps someone else. I was given this suggestion when I asked the same question on Laracasts.
As Hemerson Varela already said in Php 7.1 count(null) will throw an error and hasOne returns null if no row exists. Since you have a hasOnerelation I would use the empty method to check:
$model = RepairItem::find($id);
if (!empty($temp = $request->input('option'))) {
$option = $model->option;
if(empty($option)){
$option = $model->option()->create();
}
$option->someAttribute = temp;
$option->save();
};
But this is superfluous. There is no need to check if the relation exists, to determine if you should do an update or a create call. Simply use the updateOrCreate method. This is equivalent to the above:
$model = RepairItem::find($id);
if (!empty($temp = $request->input('option'))) {
$model->option()
->updateOrCreate(['repair_item_id' => $model->id],
['option' => $temp]);
}
Not sure if this has changed in Laravel 5, but the accepted answer using count($data->$relation) didn't work for me, as the very act of accessing the relation property caused it to be loaded.
In the end, a straightforward isset($data->$relation) did the trick for me.
I had to completely refactor my code when I updated my PHP version to 7.2+ because of bad usage of the count($x) function. This is a real pain and its also extremely scary as there are hundreds usages, in different scenarios and there is no one rules fits all..
Rules I followed to refactor everything, examples:
$x = Auth::user()->posts->find(6); (check if user has a post id=6 using ->find())
[FAILS] if(count($x)) { return 'Found'; }
[GOOD] if($x) { return 'Found'; }
$x = Auth::user()->profile->departments; (check if profile has some departments, there can have many departments)
[FAILS] if(count($x)) { return 'Found'; }
[GOOD] if($x->count()) { return 'Found'; }
$x = Auth::user()->profile->get(); (check if user has a profile after using a ->get())
[FAILS] if(count($x)) { return 'Found'; }
[GOOD] if($x->count()) { return 'Found'; }
Hopes this can help, even 5 years after the question has been asked, this stackoverflow post has helped me a lot!
If you use the model class and use Eloquent ORM, then create a new method and return bool data. like
public function hasPosts(): bool
{
return $this->posts()->exists();
}
RelationLoaded method of Model class could be useful.
if ($this->relationLoaded('prices')) {
return;
}

Categories