Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new array helper: array_flatten_with_dots #4184

Merged
merged 2 commits into from
Jan 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 38 additions & 8 deletions system/Helpers/array_helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -133,13 +133,11 @@ function array_deep_search($key, array $array)
* Both arrays of objects and arrays of array can be sorted.
*
* Example:
* array_sort_by_multiple_keys($players,
* [
* 'team.hierarchy' => SORT_ASC,
* 'position' => SORT_ASC,
* 'name' => SORT_STRING,
* ]
* );
* array_sort_by_multiple_keys($players, [
* 'team.hierarchy' => SORT_ASC,
* 'position' => SORT_ASC,
* 'name' => SORT_STRING,
* ]);
*
* The '.' dot operator in the column name indicates a deeper array or
* object level. In principle, any number of sublevels could be used,
Expand Down Expand Up @@ -180,7 +178,7 @@ function array_sort_by_multiple_keys(array &$array, array $sortColumns): bool
{
$carry[$index] = $object->$keySegment;
}

continue;
}

Expand All @@ -200,3 +198,35 @@ function array_sort_by_multiple_keys(array &$array, array $sortColumns): bool
return array_multisort(...$tempArray);
}
}

if (! function_exists('array_flatten_with_dots'))
{
/**
* Flatten a multidimensional array using dots as separators.
*
* @param iterable $array The multi-dimensional array
* @param string $id Something to initially prepend to the flattened keys
*
* @return array The flattened array
*/
function array_flatten_with_dots(iterable $array, string $id = ''): array
{
$flattened = [];

foreach ($array as $key => $value)
{
$newKey = $id . $key;

if (is_array($value))
{
$flattened = array_merge($flattened, array_flatten_with_dots($value, $newKey . '.'));
}
else
{
$flattened[$newKey] = $value;
}
}

return $flattened;
}
}
11 changes: 6 additions & 5 deletions system/Validation/Validation.php
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ public function check($value, string $rule, array $errors = []): bool
* @param string|null $label
* @param string|array $value Value to be validated, can be a string or an array
* @param array|null $rules
* @param array $data // All of the fields to check.
* @param array $data All of the fields to check.
*
* @return boolean
*/
Expand All @@ -213,15 +213,16 @@ protected function processRules(string $field, string $label = null, $value, $ru
throw new InvalidArgumentException('You must supply the parameter: data.');
}

// If the if_exist rule is defined...
if (in_array('if_exist', $rules, true))
{
// and the current field does not exists in the input data
// we can return true. Ignoring all other rules to this field.
if (! array_key_exists($field, $data))
// If the if_exist rule is defined
// and the current field does not exist in the input data
// we can return true, ignoring all other rules to this field.
if (! array_key_exists($field, array_flatten_with_dots($data)))
{
return true;
}

// Otherwise remove the if_exist rule and continue the process
$rules = array_diff($rules, ['if_exist']);
}
Expand Down
111 changes: 104 additions & 7 deletions tests/system/Helpers/ArrayHelperTest.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
<?php namespace CodeIgniter\Helpers;
<?php

class ArrayHelperTest extends \CodeIgniter\Test\CIUnitTestCase
namespace CodeIgniter\Helpers;

use CodeIgniter\Test\CIUnitTestCase;

class ArrayHelperTest extends CIUnitTestCase
{
protected function setUp(): void
{
Expand Down Expand Up @@ -182,7 +186,8 @@ public function testArraySortByMultipleKeysWithArray($data, $sortColumns, $expec
public function testArraySortByMultipleKeysWithObjects($data, $sortColumns, $expected)
{
// Morph to objects
foreach($data as $index => $dataSet){
foreach ($data as $index => $dataSet)
{
$data[$index] = (object) $dataSet;
}

Expand Down Expand Up @@ -228,19 +233,17 @@ public function testArraySortByMultipleKeysFailsInconsistentArraySizes($data)
{
$this->expectException('ValueError');
}

$this->expectExceptionMessage('Array sizes are inconsistent');

$sortColumns = [
'team.orders' => SORT_ASC,
'positions' => SORT_ASC,
];

$success = array_sort_by_multiple_keys($data, $sortColumns);
array_sort_by_multiple_keys($data, $sortColumns);
}

//--------------------------------------------------------------------

public static function deepSearchProvider()
{
return [
Expand Down Expand Up @@ -319,4 +322,98 @@ public static function sortByMultipleKeysProvider()
],
];
}

/**
* @dataProvider arrayFlattenProvider
*
* @param iterable $input
* @param iterable $expected
*
* @return void
*/
public function testArrayFlattening($input, $expected): void
{
$this->assertSame($expected, array_flatten_with_dots($input));
}

public function arrayFlattenProvider(): iterable
{
yield 'normal' => [
[
'id' => '12',
'user' => [
'first_name' => 'john',
'last_name' => 'smith',
'age' => '26 years',
],
],
[
'id' => '12',
'user.first_name' => 'john',
'user.last_name' => 'smith',
'user.age' => '26 years',
],
];

yield 'many-levels' => [
[
'foo' => 1,
'bar' => [
'bax' => [
'baz' => 2,
'biz' => 3,
],
],
'baz' => [
'fizz' => 4,
],
],
[
'foo' => 1,
'bar.bax.baz' => 2,
'bar.bax.biz' => 3,
'baz.fizz' => 4,
],
];

yield 'with-empty-arrays' => [
[
'foo' => 'bar',
'baz' => [],
'bar' => [
'fizz' => 'buzz',
'nope' => 'yeah',
'why' => [],
],
],
[
'foo' => 'bar',
'bar.fizz' => 'buzz',
'bar.nope' => 'yeah',
],
];

yield 'with-mixed-empty' => [
[
'foo' => 1,
'' => [
'bar' => 2,
'baz' => 3,
],
0 => [
'fizz' => 4,
],
1 => [
'buzz' => 5,
],
],
[
'foo' => 1,
'.bar' => 2,
'.baz' => 3,
'0.fizz' => 4,
'1.buzz' => 5,
],
];
}
}
41 changes: 31 additions & 10 deletions tests/system/Validation/RulesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,17 @@ public function ifExistProvider()
[],
true,
],
// Testing for multi-dimensional data
[
['foo.bar' => 'if_exist|required'],
['foo' => ['bar' => '']],
false,
],
[
['foo.bar' => 'if_exist|required'],
['foo' => []],
true,
],
];
}

Expand Down Expand Up @@ -1496,10 +1507,15 @@ public function inListProvider()
public function testRequiredWith($field, $check, $expected = false)
{
$data = [
'foo' => 'bar',
'bar' => 'something',
'baz' => null,
'array' => ['nonEmptyField1'=>'value1','nonEmptyField2'=>'value2', 'emptyField1'=>null, 'emptyField2'=>null],
'foo' => 'bar',
'bar' => 'something',
'baz' => null,
'array' => [
'nonEmptyField1' => 'value1',
'nonEmptyField2' => 'value2',
'emptyField1' => null,
'emptyField2' => null,
],
];

$this->validation->setRules([
Expand Down Expand Up @@ -1578,10 +1594,15 @@ public function requiredWithProvider()
public function testRequiredWithout($field, $check, $expected = false)
{
$data = [
'foo' => 'bar',
'bar' => 'something',
'baz' => null,
'array' => ['nonEmptyField1'=>'value1','nonEmptyField2'=>'value2', 'emptyField1'=>null, 'emptyField2'=>null],
'foo' => 'bar',
'bar' => 'something',
'baz' => null,
'array' => [
'nonEmptyField1' => 'value1',
'nonEmptyField2' => 'value2',
'emptyField1' => null,
'emptyField2' => null,
],
];

$this->validation->setRules([
Expand Down Expand Up @@ -1636,11 +1657,11 @@ public function requiredWithoutProvider()
'array.nonEmptyField2',
true,
],
[
[
'array.nonEmptyField1',
'array.nonEmptyField2',
true,
],
],
];
}

Expand Down
Loading