How to check if collection's column matches an array - laravel

Model CustomerDocuments holds customer docs, doc_id is a column in this model that signifies which document is there.
I have an array of needed document IDs, say $neededDocs = [1,2,3].
How do I check if my collection CustomerDocuments contains $neededDocs in each item of this collection.
if all $neededDocsare present in each CustomerDocuments.doc_id then return true, else, false.
I would like to preform this with collection->contains as follows:
CustomersDocuments::where('customer_id', $customer->id)
->contains('doc_id',$required_onboarding_docs)
->get();
Yet this syntax is wrong

I think you could probably achieve this with something like the following:
$documents = CustomersDocuments::where('customer_id', $customer->id)
->get()
->pluck('doc_id')
->toArray();
The above will output an array with all the doc_id.
return $documents == $neededDocs;
You could then just compare it with your $neededDocs array.
Edit: If you, however, want to check that each row in your collection contains a doc_id that is present in $neededDocs, you could do it like this:
$collection = CustomersDocuments::where('customer_id', $customer->id)->get();
return $collection->contains(function($value, $key) use ($neededDocs) {
return in_array($value->doc_id, $neededDocs);
});
I am not entirely sure which one you want, but these should do the trick.

Related

Get last few rows in ascending order in Laravel

What is the best way (Laravel way) to get last few rows of a database table in ascending order? I want the result to be an array of objects as like:
[{"name1":"value1"}, {"name2":"value2"}]
My recent code is like:
$users = User::take(2)->latest()->get()->reverse();
return $users;
Output looks like:
{"1":{"id":582, "name":"name1"}, "0":{"id":583, "name":"name2"}}
But I want the output to be like:
[{"id":582, "name":"name1"}, {"id":583, "name":"name2"}]
When you use the reverse() method, it modifies the Collection, but doesn't re-index it, meaning the Collection is now [1, 0]. JS/JSON treats this as an Object instead of an array, hence the difference:
{"1":{"id":582, "name":"name1"}, "0":{"id":583, "name":"name2"}}
// VS
[{"id":582, "name":"name1"}, {"id":583, "name":"name2"}]
To solve this, use the values() method to re-index the array before return:
$users = User::take(2)->latest()->get()->reverse();
return $users->values();
// [{"id":582, "name":"name1"}, {"id":583, "name":"name2"}]
If you want them in the other order, don't use reverse(), and you shouldn't need values() either:
$users = User::take(2)->latest()->get();
return $users;
// [{"id":583, "name":"name2"}, {"id":582, "name":"name1"}]

Sort Collection By id set

I have an array of ids that i want to sort a given column by them in the collection.
For example,
$suppliersOrder = [8,7,5,1,3,4];
$items = Items::get()->sortBy(function($model) use ($suppliersOrder) {
return array_search($model->supplier_id, $suppliersOrder);
})->values();
This acts like ordering items as [1,3,4,5,7,8] instead of the given order. And if I try sortByDesc, likewise [8,7,5,4,3,1] but I couldn't figure out the way to actually sort them as my given array's order.
My ultimate goal is then running $items->groupBy('supplier.name') so I can have my desired order.
What Alexander Villalobos suggested in the comments, I changed my code like this:
$items = Items::get()->sortBy(function($model) use ($suppliersOrder) {
return rsort($model->supplier_id, $suppliersOrder);
});
Indirect modification of overloaded property App\Item::$supplier_id has no effect
$suppliersOrder = [8,7,5,1,3,4];
$items = Items::get()->sortBy(function($row,$key) use ($suppliersOrder) {
return array_search($row->supplier_id, $suppliersOrder);
});
This should give you sorted collection of items by the order you described in $suppliersOrder. As per Laravel docs, the parameters to the callback function include one being the row for the collection and another being the key of that row in the collection.

select certain columns from eloquent collection after the query has executed

Using Laravel 5.3, I have a model with the following function
public function myData() {
return $this->hasMany(MyData::class);
}
and in my collection I have the following
$my_data = MyModel->myData()->get();
All good so far. If I return $my_data I get an eloquent collection with three items.
What I need now though is to create a duplicate of that collection but containing only three of the fields.
I have tried several different things, each of which return an error. The following is the closest I have got, but this returns an empty array - I assume because the fields are located one level deeper than the collection object.
$new_collection = $my_data->only(['field_1', 'field_2', 'field_3']);
What would be the correct way to create a new collection containing all three items, each with only the three selected fields?
Thanks for your help
You could use map:
$slimmed_down = $collection->map(function ($item, $key) {
return [
'field_1' => $item->field_1,
'field_2' => $item->field_2,
'field_3' => $item->field_3
];
});
This will return a new Collection with just the values you want. As far as I know there isn't any other method that does what you want, so iterating over every item and selecting the fields this way is one of the few solutions.
The advantage of using map instead of a standard foreach loop is that when you use map it returns a new instance of Collection.
Edit:
After some thoughts and research about this, the problem you'll have created is that the all the values in the Collection aren't instances of anything anymore. If you don't mind this effect, an even prettier and faster way would be to do this:
$slimmed_down = $collection->toArray()->only(['field_1', 'field_2', 'field_3']);
This basically has the same result.
Using Laravel 9, I just had the same issue :
$my_data->only(['field_1', 'field_2', 'field_3']);
returning an empty array.
I solved it with :
$my_data->map->only(['field_1', 'field_2', 'field_3']);

How to get unique year values from created_at?

I have a collection called $products, each instance of which has a field created_at. The latter obviously has a format ('Y-m-d H:i:s') in the DB. Now, it's easy to get instances with unique created_at. However, I'd like to retrieve unique year values of created_at in one single expression (that I can write in my view). What I am looking for is:
$products->unique( year value ('Y' ONLY) of 'created_at' )
This expression should evaluate to something like this: ['2012', '2013', '2016'].
Building on the comment that gives you a collection with unique timestamps, you can map out the year and sort with the following code.
$years = $products->unique(function($item){
return $item['created_at']->year;
})->map(function($item){
return $item['created_at']->year;
})->sort()->toArray();
In addition to Tim result
$products = DB::table('products')->get();
$years = $products->unique(function($item){
return $item['created_at']->year;
})->map(function($item){
return $item['created_at']->year;
})->sort()->toArray();
You can use toArray or values() to get the result of collection

laravel database query Does `where` always need `first()`?

I am new to laravel and confused about some query methods.
find($id) is useful and returns a nice array, but sometimes I need to select by other fields rather than id.
The Laravel document said I could use where('field', '=', 'value') and return a bunch of data, which is fine.
What I can't understand is why I need to add ->first() every time, even if I am pretty sure there is only one single row matches the query.
It goes like this:
$query->where(..)->orderBy(..)->limit(..) etc.
// you can chain the methods as you like, and finally you need one of:
->get($columns); // returns Eloquent Collection of Models or array of stdObjects
->first($columns); // returns single row (Eloquent Model or stdClass)
->find($id); // returns single row (Eloquent Model or stdClass)
->find($ids); // returns Eloquent Collection
// those are examples, there are many more like firstOrFail, findMany etc, check the api
$columns is an array of fields to retrieve, default array('*')
$id is a single primary key value
$ids is an array of PKs, this works in find method only for Eloquent Builder
// or aggregate functions:
->count()
->avg()
->aggregate()
// just examples here too
So the method depends on what you want to retrieve (array/collection or single object)
Also the return objects depend on the builder you are using (Eloquent Builder or Query Builder):
User::get(); // Eloquent Colleciton
DB::table('users')->get(); // array of stdObjects
even if I am pretty sure there is only one single row matches the query.
Well Laravel cant read your mind - so you need to tell it what you want to do.
You can do either
User::where('field', '=', 'value')->get()
Which will return all objects that match that search. Sometimes it might be one, but sometimes it might be 2 or 3...
If you are sure there is only one (or you only want the first) you can do
User::where('field', '=', 'value')->first()
get() returns an array of objects (multiple rows)
while
first() returns a single object (a row)
You can of course use get() when you know it will return only one row, but you need to keep that in mind when addressing the result:
using get()
$rez = \DB::table('table')->where('sec_id','=','5')->get();
//will return one row in an array with one item, but will be addressed as:
$myfieldvalue = $rez[0]->fieldname;
using first()
$rez = \DB::table('table')->where('sec_id','=','5')->first();
// will also return one row but without the array, so
$myfieldvalue = $rez->fieldname;
So it depends on how you want to access the result of the query: as an object or as an array, and also depends on what "you know" the query will return.
first() is the equivalent of LIMIT 1 at the end of your SELECT statement. Even if your query would return multiple rows, if you use first() it will only return the first row

Resources