Check if Object already exists in Collection - Laravel - php

I am looking to add objects to a new collection as I loop through a series of different results.
The query:
$osRed = Item::where('category', 'Hardware')
->where(function ($query) {
$query->where('operating_system', 'xxx')
->orWhere('operating_system', 'xxx')
->orWhere('operating_system', 'xxx');
})
->orderBy('operating_system', 'ASC')
->get();
Then, loop through these results and add related objects to a new collection.
foreach ($osRed as $os) {
foreach ($os->services as $service) {
if (!($servicesImpacted->contains($service))) {
$servicesImpacted->push($service);
}
}
}
I then go on to another set of results and add the related services from those results to the collection.
However, it is not picking up that the object is already in the collection and I end up with a large number of (what appear to be) duplicates.
Ideally, I would like to match up on the name attribute of the $service object, but that doesn't seem to be supported by the contains method.
toString() must not throw an exception

You can use contains() with key and value defined:
if (!$servicesImpacted->contains('name', $service->name))
Or you could use where() and count() collection methods in this case, for example:
if ($servicesImpacted->where('name', $service->name)->count() === 0)

You can use the contains() method in Laravel for
Check some specific key of value exists or not.
If value available in the collection for the specific key then returns true.
if($collection->contains('product_id',1234))
{
echo "product id 1234 available in collection";
}

Related

Laravel - Property does not exist on this collection instance

On my Users model I have a query that gives me "Property [manager_id] does not exist on this collection instance" error. I suppose this is because the collection of expiringUsers does not have a single property manager_id, but I'm not sure how to loop through expiringUsers in the query.
What I'm trying to return is a collection of managers where their ID matches the expiring manager_id.
public function scopeManagers()
{
$expiringUsers = $this->ExpiringContractors(); // This returns a collection of users
return $mangers = $this->where(function($query) use ($expiringUsers){
$query->where('id', $expiringUsers->manager_id);
})->get();
}
You already said it yourself... If $expiringUsers is a Collection, you can't access manager_id without looping, but you actually don't have to in this case. If you want to query id for those that match any manager_id, you can do that via a whereIn() clause, combined with the pluck() method:
$query->whereIn('id', $expiringUsers->pluck('manager_id'));
And, your code can be simplified to:
public function scopeManagers(){
return $this->where(function($query){
$query->whereIn('id', $this->ExpiringContractors()->pluck('manager_id'));
})->get();
}
Note: ExpiringContractors should really be expiringContractors; function names are generally camelCase.

Laravel transform not working as intended on collection

I have a collection which is an eloquent query.
There is one column where I want to replace the value with another value.
I am using the transform function to do this however it is not working as intended.
Here is my query in the controller :
$articles = KnowledgeBaseArticle::getArticlesByDepartment($department)
->get()
->transform(function ($article) {
$article->category_id = KnowledgeBaseCategory::find($article->category_id)->name;
});
And the getArticlesByDepartment query from the model:
public function scopeGetArticlesByDepartment($query, $department){
return $query->where('department', $department)
->select('title', 'updated_at', 'department', 'id', 'category_id')
->orderBy('title', 'asc');
}
I want to return it so that all the rows with column category_id is replaced with the category name. You can see I am trying to do this by using $article->category_id by using find on the KnowledgeBaseCategory model to retrieve this. However this is not working at all and when I die and dump, I get an single column array full of nulls.
When I have died and dumped $article->category_id & find query inside the transform, it is returning the correct category name, it is just not replacing the category_id column with the category name.
I have also tried map instead of transform and got the same result.
If it matters, I am later on converting this data into JSON.
Where am I going wrong?
Transform, not unlike map, needs you to return the modified item of the collection, as this will replace the existing item.
transform(function ($article) {
$article->category_id = KnowledgeBaseCategory::find($article->category_id)->name;
return $article;
});
Since objects are mutable and passed by reference, you can just do this in an each() closure instead to save a line:
each(function ($article) {
$article->category_id = KnowledgeBaseCategory::find($article->category_id)->name;
});
Though, you really should have a relationship set up for category. There's no reason to be performing this find logic in your controller.

getting only one data from laravel collection seems difficult

I don't get this, I have a collection of items.
The collection contains in this case one and only one item at index 1 as it was filtered from a bigger collection.
The point is how do I get the only piece of data I need without having to reset the values of the index and then accessing at index 0 ??
This is a case where I will always have only ONE item in the collection but the index at could be different so I can't use the [0] index by default.
//returns all items with 'item_color_id" = 1
$item = Item::where(//some constraints);
$filtered = $item->filter(function ($i) {
return $i->item_color_id == 1;
});
if (count($filtered)) {
//need to access a single data inside the collection
// like for example 'item_brand_id'
//I can do it like this:
$filtered = $filtered->values();
$item_brand_id = $filtered[0]['item_brand_id'];
//but what sense does it have?? how can we access 'item_brand_id'
//from $filtered without resetting the indexes?
}
it doesn't make any sense to me that we don't have a method to access the data directly, or if we have it I missed it.
For example I coould use max() or min() on $filtered like this:
$max_brand_id = $filtered->max('item_brand_id');
It doesn't make any sense to find the max id in this case I know, but it shows that we can find the data in one passage.
I tried only('item_brand_id); but that returns empty while the data is there.
You still have eloquent collection so you can do just call first() function
$item_brand_id = $filtered->first()->item_brand_id;
filter() is used when you want a collection of matching elements, even if that only results in one element.
If you know that you only want one element that matches a truth test, use first() instead. It has the same signature.
Item::where(...)->get()->first(function ($item) {
return $item->item_color_id == 1;
})->get('item_brand_id');
You could change your database query to just return one element. If the result is always just one, there is no need to load an entire collection.
$item = Item::where(/*some constraints*/)->where('item_color_id', 1)->first();
if (isset($item))
echo $item->item_brand_id;

Check if collection contains a value

I have the relationships set up (correctly i think).. I have 3 tables, users, comments, and comments' likes table.
In my blade, I can access {{ $comment->commentLikes }} with this and it's returning me:
[{"id":85,"comment_id":12,"user_id":1,"action":1},
{"id":86,"comment_id":12,"user_id":3,"action":1},
{"id":87,"comment_id":12,"user_id":4,"action":1},
{"id":88,"comment_id":12,"user_id":6,"action":1},
{"id":89,"comment_id":12,"user_id":9,"action":1}]
user_id represents owner of the like.
Now I want to check if this collection has the authenticated user, in other words if the current user liked this comment or not.. Is there a way to do that rather than using a for loop? Maybe something like {{ $comment->commentLikes->owner }} so that I can use
'if (this) contains Auth::user->id()'...
One way to check this is using where() and first() collection methods:
if ($comment->commentLikes->where('user_id', auth()->user()->id)->first())
// This works if $comment->commentLikes returns a collection
$userLikes = $comment->commentLikes->where('user_id', Auth::user()->id)->all();
if ($userLikes->count() > 0) {
// Do stuff
}
Option 1
Using Laravel collection you could do this:
$comment->commentLikes->search(function ($item, $key) {
return $item->user_id == auth()->user()->id;
});
This will return false if none is found or index if the user is in the collection.
Making it pretty
But since you'll probably use this in a view I would package it in an accessor, something like this:
class Comment extends Model
{
public function commentLikes() {
// relationship defined
}
public function getHasLikedAttribute()
{
// use getRelationValue so it won't load again if it was loaded before
$comments = $this->getRelationValue('commentLikes');
$isOwner = $comment->commentLikes->search(function ($item, $key) {
return $item->user_id == auth()->user()->id;
});
return $isOwner === false ? false : true;
}
}
This way you can call $comment->has_liked which will return true if currently logged in user is in the relationship commentLikes or false if he isn't.
Use:
if ($comment->commentLikes()->where('user_id', auth()->user()->id)->count() > 0)
This is more efficient than other answers posted because both the where() and the count() applies on the query builder object instead of the collection

Laravel 4: Why can I access a model accessor when using first() but not take()?

I'm trying to return a number of recent posts from my database, ordered by date, and I then want to select and return the month the post was made in via my model's getMonthAttribute() accessor method. To accomplish this, I'm using scoped queries. This all works fine when I use first() to return just a single result, but when I use take(1) or take() with any valid numerical input, I receive the following error:
Undefined property: Illuminate\Database\Eloquent\Collection::$month
In my model, I have this month attribute accessor:
public function getMonthAttribute() {
return Carbon::createFromFormat('Y-m-d',$this->date)->format('F');
}
and my scoped query to return a variable number of recent posts (the portion of my code that is not working):
public function scopeRecent($query, $take = 1) {
// Replace take with first and I no longer receive the above error.
return $query->where('status', '=', '1')->orderBy('date', 'DESC')->get()->take($take);
}
Here is how I'm accessing my data in the view:
{{ $post->recent()->month }}
Any suggestions?
This is because ->first() returns an eloquent model. Using the get() method returns an eloquent collection (an array of eloquent models) instead. So you must run a foreach over the collection like so:
#foreach($post->recent() as $recent)
{{$recent->month }}
#endforeach

Categories