How to test array contains only objects with PHPUnit? - laravel

I'm looking for solution to test an array of objects with PHPUnit in my Laravel project.
This is my haystack array:
[
[
"id" => 10,
"name" => "Ten"
],
[
"id" => 5,
"name" => "Five"
]
]
And this is the needles array:
[
[
"id" => 5,
"name" => "Five"
],
[
"id" => 10,
"name" => "Ten"
]
]
The order of objects doesn't matter, also keys of objects doesn't matter. The only matter is we have two objects and all objects has exactly same keys and exactly same values.
What is the correct solution for this?

You can do this using the assertContainsEquals method like this:
$haystack = [
[
'id' => 10,
'name' => 'Ten'
],
[
'id' => 5,
'name' => 'Five'
]
];
$needles = [
[
'name' => 'Five',
'id' => 5
],
[
'id' => 10,
'name' => 'Ten'
]
];
foreach ($needles as $needle) {
$this->assertContainsEquals($needle, $haystack);
}
You could also a create your own assert method if you intend to perform the assertion more often:
public function assertContainsEqualsAll(array $needles, array $haystack): void
{
foreach ($needles as $needle) {
$this->assertContainsEquals($needle, $haystack);
}
}

Based on #Roj Vroemen's answer I implemented this solution for exact match asserting:
public function assertArrayContainsEqualsOnly(array $needles, array $haystack, string $context = ''): void
{
foreach ($needles as $index => $needle) {
$this->assertContainsEquals(
$needle,
$haystack,
($context ? $context . ': ' : '') . 'Object not found in array.'
);
unset($haystack[$index]);
}
$this->assertEmpty(
$haystack,
($context ? $context . ': ' : '') . 'Not exact match objects in array.'
);
}

Related

Using Http::post to get data from API

I tried to get data from API, when in Postman it's working fine but when i tried it in laravel it return an error
How do i get that data to laravel?
This is from my controller :
$response = Http::post('https://url.id/api/tryout/check', [
'category_id' => 0,
'platform' => 'platform',
'is_unique' => false,
'chapters' => [
'id' => 3,
'name' => "Aljabar",
'count' => 10,
],
]);
return $response;
But the code above will return an error :
Trying to access array offset on value of type int
you are passing your chapters data as array of object in your POSTMAN,
"chapters": [
{
"id": 1,
..
..
},
{
"id": 3,
..
..
}
]
then its a plain object in your Laravel HTTP request, which throwing an error because you are looping through id, name and count and not through an object containing those key which your loop expects to have
"chapters": {
"id": 3,
..
..
}
you need this part to be a list of objects
'chapters' => [
'id' => 3,
..
],
should be
'chapters' => [
[
'id' => 3,
..
]
],
You are sending 'category_id' => 0 I don’t believe that it exists in your database. You have to change it to 'category_id' => 1
And also you have to follow this answer too.

Laravel Query Builder Issue Remove Null Values

Hi all you smart individuals i'm hitting a solid wall here with someone else code that i'm trying to fix a issue.
The issue is that when this query builder has got all the results there seems to be some coming back with null values and i'm not sure how to remove them before I paginate the data, I know how to do it if it was a collection however maybe some of you might be-able to help me.
so currently the logic goes into this pagination
$return = $tld->paginate($request->get('limit'))->toArray();
$tld being the eloquent builder.
Which then gets sent into this function that was created..
$return = $this->makePagination($return);
public function makePagination($object = [], $filters = []) {
return [
'data' => [
'items' => $object['data'],
'pagination' => [
'from' => $object['from'],
'to' => $object['to'],
'total' => $object['total'],
'per_page' => $object['per_page'],
'first_page' => [
'number' => 1,
'url' => $object['first_page_url']
],
'last_page' => [
'number' => $object['last_page'],
'url' => $object['last_page_url']
],
'next_page' => [
'number' => $object['current_page'] + 1,
'url' => $object['next_page_url']
],
'prev_page' => [
'number' => $object['current_page'] - 1,
'url' => $object['prev_page_url']
]
],
'params' => $filters
]
];
}
But then i'm getting a response like this with Null values and I would like to remove them before any of this pagination happens
{
"data": {
"items": [
{
"id": 13771,
},
null,
{
"id": 4125,
},
Side note if I run $tld->get() I can see all the results and there are null values in there so if anyone can show me how to remove the null values that would be a great help <3 you all if you can help me ...
Update
Ive also tried $tld->get()->filter(); and thats also not removing the null values I still get this response
[
{
"id": 13771,
},
null,
{
"id": 789,
}
]
I think I fixed it with a little hack
$filtered = collect(array_values(array_filter($tld->get()->toArray())));
return $this->paginate($filtered, $request->get('limit') ?? 15 , $page = null, $options = []);
and then created a collection paginator
public function paginate($items, $perPage = 15, $page = null, $options = [])
{
$page = $page ?: (Paginator::resolveCurrentPage() ?: 1);
$items = $items instanceof Collection ? $items : Collection::make($items);
return new LengthAwarePaginator($items->forPage($page, $perPage), $items->count(), $perPage, $page, $options);
}
#Bob Hiller Please use whereNotNull in query to remove null entries.
$tld=$query->get();
$tld->filter(function ($value) { return !is_null($value); });
$return = $tld->paginate($request->get('limit'))->toArray();

Concatenating two arrays and sum with keys via Laravel Collection

I'm having a set of array in something like this format:
[
{"GA_1":"1","GA_2":null,"GA_3":null,"GA_4":null},
{"SA_1":null,"SA_2":"2","SA_3":null,"SA_4":null},
{"RA_1":"1","RA_2":null,"RA_3":null,"RA_4":null}
]
I'm storing this in my mysql text column in json_decoded format. I want to call all the eloquent models and merge these arrays with sum of the each keys on object. For example
1st row contains:
[
{"GA_1":"1","GA_2":null,"GA_3":null,"GA_4":null},
{"SA_1":null,"SA_2":"2","SA_3":null,"SA_4":null},
{"RA_1":"1","RA_2":null,"RA_3":null,"RA_4":null}
]
2nd row contains:
[
{"GA_1":null,"GA_2":"1","GA_3":"2","GA_4":null},
{"SA_1":"1","SA_2":null,"SA_3":"3","SA_4":null},
{"RA_1":null,"RA_2":"2","RA_3":null,"RA_4":"5"}
]
3rd row contains:
[
{"GA_1":"1","GA_2":null,"GA_3":null,"GA_4":null},
{"SA_1":null,"SA_2":"2","SA_3":null,"SA_4":null},
{"RA_1":"1","RA_2":null,"RA_3":null,"RA_4":null}
]
so my final output should be:
[
{"GA_1":"2","GA_2":"1","GA_3":"2","GA_4":null},
{"SA_1":"1","SA_2":"4","SA_3":null,"SA_4":null},
{"RA_1":"1","RA_2":"2","RA_3":null,"RA_4":"5"}
]
I'm stuck in how can I achieve this:
$games = Game::get();
$grid = [];
foreach ($games as $game) {
$grid[] = collect($game->grid_values);
}
dd(collect('$grid')->flatten());
I'm getting this output:
Any suggestions are welcome. Thanks.
Well I tried something like this:
$games = Game::get();
$d = collect($games)->map(function($item) {
return json_decode($item->grid_values);
})->flatten();
$x = collect([
[
'GA_1' => $d->sum('GA_1'),
'GA_2' => $d->sum('GA_2'),
'GA_3' => $d->sum('GA_3'),
'GA_4' => $d->sum('GA_4'),
'GA_5' => $d->sum('GA_5'),
'GA_6' => $d->sum('GA_6'),
'GA_7' => $d->sum('GA_7'),
'GA_8' => $d->sum('GA_8'),
'GA_9' => $d->sum('GA_9'),
'GA_0' => $d->sum('GA_0'),
],
[
'SA_1' => $d->sum('SA_1'),
'SA_2' => $d->sum('SA_2'),
'SA_3' => $d->sum('SA_3'),
'SA_4' => $d->sum('SA_4'),
'SA_5' => $d->sum('SA_5'),
'SA_6' => $d->sum('SA_6'),
'SA_7' => $d->sum('SA_7'),
'SA_8' => $d->sum('SA_8'),
'SA_9' => $d->sum('SA_9'),
'SA_0' => $d->sum('SA_0'),
],
[
'RA_1' => $d->sum('RA_1'),
'RA_2' => $d->sum('RA_2'),
'RA_3' => $d->sum('RA_3'),
'RA_4' => $d->sum('RA_4'),
'RA_5' => $d->sum('RA_5'),
'RA_6' => $d->sum('RA_6'),
'RA_7' => $d->sum('RA_7'),
'RA_8' => $d->sum('RA_8'),
'RA_9' => $d->sum('RA_9'),
'RA_0' => $d->sum('RA_0'),
],
]);
return response()->json(['data' => $x], 200);
And I got expected result, any improvisation please suggest.

How to access data from an array when it's inside an std object? Error: Illegal string offset 'id'

How to access this array object in Laravel 6, using Eloquent?
[line_items] => Array
(
[0] => stdClass Object
(
[id] => 4088662196333
[variant_id] => 29653605285997
$external = DB::table('orders')->pluck('import_order_data');
...
foreach ($external as $key => $val) {
...
Cart::updateOrCreate([
'line_item_id' => ['line_item_id' => $val['id']],
],
ErrorException
Illegal string offset 'id'
If I change it to:
'line_item_id' => ['line_item_id' => $val->id],
I get error:
ErrorException
Trying to get property 'id' of non-object
If I change it to:
'line_item_id' => ['line_item_id' => $val['line_items']->id],
I get error:
Illegal string offset 'line_items'
EDIT:
The problem was:
protected $casts = [
'import_order_data' => 'array',
];
Now I can access it like this:
dd($val['line_items'][0]['id']);
Which provides:
4092309209197
or
dd($val['line_items']);
which provides:
array:1 [▼
0 => array:26 [▼
"id" => 4092309209197
"sku" => "1605"
"name" => "Printer Ink"
Any better options on accessing the data?
EDIT:
Answer:
foreach ($val['line_items'] as $index => $lineItem) {
dd($lineItem['id']);
Which provides:
4092309209197
Is this a reasonable way to do it?
If I understand correctly, your array looks like this:
$arr = [
[
"id" => 4092309209197,
"sku" => "1605",
"name" => "Printer Ink",
....etc.....
]
];
Fine. First, since the outer array only has one item, you can do this:
$innerArr = $arr[0];
Now you've got this:
$innerArr = [
"id" => 4092309209197,
"sku" => "1605",
"name" => "Printer Ink",
....etc.....
]
And you can access it like this:
echo $innerArr["id"];
echo $innerArr["sku"];
....etc....
Or like this:
foreach($innerArr as $key => $val){
echo $key.": ".$val."\r\n";
}

Map array values to collection of items

How would one do the following elegantly with laravel collections ?
Map the values of the $baseMap as keys to the collection.
The baseMap :
$baseMap = [
'name' => 'new_name',
'year' => 'new_year',
];
The collection :
$items = collect([
[
'name' => 'name1',
'year' => '1000',
'not_in_basemap' => 'foo'
],
[
'name' => 'name2',
'year' => '2000',
'not_in_basemap' => 'foo'
],
//...
]);
The end result :
$result =[
[
'new_name' => 'name1',
'new_year' => '1000',
],
[
'new_name'=> 'name2',
'new_year' => '2000',
],
];
I know how to do it in plain php , just wondering what a nice collection version would be. Thanks!
I tried to find collection methods, or php functions, but without success. Some dirty code that works with different keys from both sides (items and basemap).
$result = $items->map(function($item) use ($baseMap) {
$array = [];
foreach($baseMap as $oldKey => $newKey){
if(isset($item[$oldKey])){
$array[$newKey] = $item[$oldKey];
}
}
return $array;
});
$result = $result->toArray();
Thanks to #vivek_23 and #IndianCoding for giving me idea's I ended up with the following :
I made a small edit to make sure the mapping and the items keys lined up.
so you don't have to worry of misalignment and all in laravel collection !
$baseMap = collect($baseMap)->sortKeys();
$result = $items->map(function ($item) use ($baseMap) {
return $baseMap->values()
->combine(
collect($item)->sortKeys()->intersectByKeys($baseMap)
)
->all();
});
Use intersectByKeys to filter your baseMap keys with $items values.
$result = $items->map(function($item,$key) use ($baseMap){
return array_combine(array_values($baseMap),collect($item)->intersectByKeys($baseMap)->all());
});
dd($result);
Update:
In a pure collection way,
$baseMapCollect = collect($baseMap);
$result = $items->map(function($item,$key) use ($baseMapCollect){
return $baseMapCollect->values()->combine(collect($item)->intersectByKeys($baseMapCollect->all())->values())->all();
});
dd($result);
Here are my two cents, using map. Don't know how dynamic your collection should be, but knowing the keys I would do the following:
$baseMap = [
'name' => 'new_name',
'year' => 'new_year',
];
$items = collect([
[
'name' => 'name1',
'year' => '1000',
'not_in_basemap' => 'foo'
],
[
'name' => 'name2',
'year' => '2000',
'not_in_basemap' => 'foo'
],
])->map(function($item, $key) use ($baseMap) {
return [
$baseMap['name'] => $item['name'],
$baseMap['year'] => $item['year']
];
});

Resources