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

support customize dot flag of array keys #31

Closed
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,5 @@ Temporary Items
.apdisk
/.phpunit.result.cache
/.phpunit.cache

dev.sh
2 changes: 1 addition & 1 deletion src/Attribute.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public function key(): string

public function isUsingDotNotation(): bool
{
return str_contains($this->key(), '.');
return str_contains($this->key(), $this->validation->getKeyDot());
}

public function parent(): ?Attribute
Expand Down
4 changes: 2 additions & 2 deletions src/DataBag.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,9 @@ public function flatten(): self
return new self(Helper::arrayDot($this->data));
}

public function get(?string $key, mixed $default = null): mixed
public function get(?string $key, mixed $default = null, string $delimiter = '.'): mixed
{
return Helper::arrayGet($this->data, $key, $default);
return Helper::arrayGet($this->data, $key, $default, $delimiter);
}

public function has(string $key): bool
Expand Down
33 changes: 19 additions & 14 deletions src/Helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,17 @@ class Helper
*
* @param array $array
* @param string $key
* @param string $delimiter
*
* @return bool
*/
public static function arrayHas(array $array, string $key): bool
public static function arrayHas(array $array, string $key, string $delimiter = '.'): bool
{
if (array_key_exists($key, $array)) {
return true;
}

foreach (explode('.', $key) as $segment) {
foreach (explode($delimiter, $key) as $segment) {
if (is_array($array) && array_key_exists($segment, $array)) {
$array = $array[$segment];
} else {
Expand All @@ -44,10 +45,11 @@ public static function arrayHas(array $array, string $key): bool
* @param array $array
* @param string|null $key
* @param mixed $default
* @param string $delimiter
*
* @return mixed
*/
public static function arrayGet(array $array, mixed $key, mixed $default = null): mixed
public static function arrayGet(array $array, mixed $key, mixed $default = null, string $delimiter = '.'): mixed
{
if (is_null($key)) {
return $array;
Expand All @@ -57,7 +59,7 @@ public static function arrayGet(array $array, mixed $key, mixed $default = null)
return $array[$key];
}

foreach (explode('.', $key) as $segment) {
foreach (explode($delimiter, $key) as $segment) {
if (is_array($array) && array_key_exists($segment, $array)) {
$array = $array[$segment];
} else {
Expand All @@ -74,16 +76,17 @@ public static function arrayGet(array $array, mixed $key, mixed $default = null)
*
* @param array $array
* @param string $prepend
* @param string $glue
*
* @return array
*/
public static function arrayDot(array $array, string $prepend = ''): array
public static function arrayDot(array $array, string $prepend = '', string $glue = '.'): array
{
$results = [];

foreach ($array as $key => $value) {
if (is_array($value) && !empty($value)) {
$results = array_merge($results, static::arrayDot($value, $prepend . $key . '.'));
$results = array_merge($results, static::arrayDot($value, $prepend . $key . $glue, $glue));
} else {
$results[$prepend . $key] = $value;
}
Expand All @@ -100,10 +103,11 @@ public static function arrayDot(array $array, string $prepend = ''): array
* @param string|array|null $key
* @param mixed $value
* @param bool $overwrite
* @param string $delimiter
*
* @return mixed
*/
public static function arraySet(mixed &$target, mixed $key, mixed $value, bool $overwrite = true): array
public static function arraySet(mixed &$target, mixed $key, mixed $value, bool $overwrite = true, string $delimiter = '.'): array
{
if (is_null($key)) {
if ($overwrite) {
Expand All @@ -113,7 +117,7 @@ public static function arraySet(mixed &$target, mixed $key, mixed $value, bool $
return $target = array_merge($value, $target);
}

$segments = is_array($key) ? $key : explode('.', $key);
$segments = is_array($key) ? $key : explode($delimiter, $key);

if (($segment = array_shift($segments)) === '*') {
if (!is_array($target)) {
Expand All @@ -122,7 +126,7 @@ public static function arraySet(mixed &$target, mixed $key, mixed $value, bool $

if ($segments) {
foreach ($target as &$inner) {
static::arraySet($inner, $segments, $value, $overwrite);
static::arraySet($inner, $segments, $value, $overwrite, $delimiter);
}
} elseif ($overwrite) {
foreach ($target as &$inner) {
Expand All @@ -135,15 +139,15 @@ public static function arraySet(mixed &$target, mixed $key, mixed $value, bool $
$target[$segment] = [];
}

static::arraySet($target[$segment], $segments, $value, $overwrite);
static::arraySet($target[$segment], $segments, $value, $overwrite, $delimiter);
} elseif ($overwrite || !array_key_exists($segment, $target)) {
$target[$segment] = $value;
}
} else {
$target = [];

if ($segments) {
static::arraySet($target[$segment], $segments, $value, $overwrite);
static::arraySet($target[$segment], $segments, $value, $overwrite, $delimiter);
} elseif ($overwrite) {
$target[$segment] = $value;
}
Expand All @@ -157,23 +161,24 @@ public static function arraySet(mixed &$target, mixed $key, mixed $value, bool $
*
* @param mixed $target
* @param string|array $key
* @param string $delimiter
*
* @return mixed
*/
public static function arrayUnset(mixed &$target, string|array $key): mixed
public static function arrayUnset(mixed &$target, string|array $key, string $delimiter = '.'): mixed
{
if (!is_array($target)) {
return $target;
}

$segments = is_array($key) ? $key : explode('.', $key);
$segments = is_array($key) ? $key : explode($delimiter, $key);
$segment = array_shift($segments);

if ($segment == '*') {
$target = [];
} elseif ($segments) {
if (array_key_exists($segment, $target)) {
static::arrayUnset($target[$segment], $segments);
static::arrayUnset($target[$segment], $segments, $delimiter);
}
} elseif (array_key_exists($segment, $target)) {
unset($target[$segment]);
Expand Down
39 changes: 28 additions & 11 deletions src/Validation.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class Validation
private array $invalidData = [];
private string $separator = ':';
private ?string $lang = null;
private string $keyDot = '.';

public function __construct(Factory $factory, array $inputs, array $rules)
{
Expand Down Expand Up @@ -223,8 +224,8 @@ protected function isArrayAttribute(Attribute $attribute): bool
protected function parseArrayAttribute(Attribute $attribute): array
{
$attributeKey = $attribute->key();
$data = Helper::arrayDot($this->initializeAttributeOnData($attributeKey));
$pattern = str_replace('\*', '([^\.]+)', preg_quote($attributeKey));
$data = Helper::arrayDot($this->initializeAttributeOnData($attributeKey), glue: $this->keyDot);
$pattern = str_replace('\*', '([^('. preg_quote($this->keyDot) .')]+)', preg_quote($attributeKey));

$data = array_merge($data, $this->extractValuesForWildcards(
$data,
Expand Down Expand Up @@ -262,7 +263,7 @@ protected function initializeAttributeOnData(string $attributeKey): array
return $data;
}

return Helper::arraySet($data, $attributeKey, null);
return Helper::arraySet($data, $attributeKey, null, delimiter: $this->keyDot);
}

/**
Expand All @@ -276,7 +277,11 @@ protected function initializeAttributeOnData(string $attributeKey): array
*/
protected function getLeadingExplicitAttributePath(string $attributeKey): ?string
{
return rtrim(explode('*', $attributeKey)[0], '.') ?: null;
return preg_replace(
'/('. preg_quote($this->keyDot) .')*$/',
'',
explode('*', $attributeKey)[0],
) ?: null;
}

/**
Expand All @@ -290,10 +295,10 @@ protected function extractDataFromPath(?string $attributeKey): array
{
$results = [];

$value = $this->input->get($attributeKey, '__missing__');
$value = $this->input->get($attributeKey, '__missing__', $this->keyDot);

if ($value != '__missing__') {
Helper::arraySet($results, $attributeKey, $value);
Helper::arraySet($results, $attributeKey, $value, delimiter: $this->keyDot);
}

return $results;
Expand All @@ -308,7 +313,7 @@ private function extractValuesForWildcards(array $data, string $attributeKey): a
{
$keys = [];

$pattern = str_replace('\*', '[^\.]+', preg_quote($attributeKey));
$pattern = str_replace('\*', '[^\('. preg_quote($this->keyDot) .')]+', preg_quote($attributeKey));

foreach ($data as $key => $value) {
if (preg_match('/^' . $pattern . '/', $key, $matches)) {
Expand Down Expand Up @@ -427,8 +432,8 @@ protected function setValidData(Attribute $attribute, $value): void
$key = $attribute->key();

if ($attribute->isArrayAttribute() || $attribute->isUsingDotNotation()) {
Helper::arraySet($this->validData, $key, $value);
Helper::arrayUnset($this->invalidData, $key);
Helper::arraySet($this->validData, $key, $value, delimiter: $this->keyDot);
Helper::arrayUnset($this->invalidData, $key, delimiter: $this->keyDot);
} else {
$this->validData[$key] = $value;
}
Expand All @@ -444,10 +449,22 @@ protected function setInvalidData(Attribute $attribute, $value): void
$key = $attribute->key();

if ($attribute->isArrayAttribute() || $attribute->isUsingDotNotation()) {
Helper::arraySet($this->invalidData, $key, $value);
Helper::arrayUnset($this->validData, $key);
Helper::arraySet($this->invalidData, $key, $value, delimiter: $this->keyDot);
Helper::arrayUnset($this->validData, $key, delimiter: $this->keyDot);
} else {
$this->invalidData[$key] = $value;
}
}

public function setKeyDot(string $keyDot): static
{
$this->keyDot = $keyDot;

return $this;
}

public function getKeyDot(): string
{
return $this->keyDot;
}
}
22 changes: 22 additions & 0 deletions tests/FactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -750,4 +750,26 @@ public function testLoadMessagesFromFile()

$this->assertEquals('yar, number neigh thar', $validation->errors()->first('number'));
}

public function testCustomizedDelimiterValidation()
{
$inputs = [
'foo' => [
'bar' => 'text',
'baz' => 123,
'qux' => 'long text'
],
'foo.bar' => 999,
'foo/jaz' => 'jaz_text',
];
$rules = [
'foo/*' => 'required|max:1',
'foo.bar' => 'required|integer',
'foo/jaz' => 'required',
];
$validation = $this->validator->make($inputs, $rules);
$validation->setKeyDot('/');

$this->assertTrue($validation->passes());
}
}
34 changes: 31 additions & 3 deletions tests/HelperTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@ public function testArrayHas()
$this->assertTrue(Helper::arrayHas($array, 'foo.bar'));
$this->assertTrue(Helper::arrayHas($array, 'foo.bar.baz'));
$this->assertTrue(Helper::arrayHas($array, 'one.two.three'));
$this->assertTrue(Helper::arrayHas($array, 'foo/bar/baz', '/'));

$this->assertFalse(Helper::arrayHas($array, 'foo.baz'));
$this->assertFalse(Helper::arrayHas($array, 'bar.baz'));
$this->assertFalse(Helper::arrayHas($array, 'foo.bar.qux'));
$this->assertFalse(Helper::arrayHas($array, 'one.two'));
$this->assertFalse(Helper::arrayHas($array, 'one/two', '/'));
}

public function testArrayGet()
Expand All @@ -48,6 +50,10 @@ public function testArrayGet()

$this->assertNull(Helper::arrayGet($array, 'foo.bar.baz.qux'));
$this->assertNull(Helper::arrayGet($array, 'one.two'));

$this->assertEquals(Helper::arrayGet($array, 'foo', delimiter: '/'), $array['foo']);
$this->assertEquals(Helper::arrayGet($array, 'foo/bar', delimiter: '/'), $array['foo']['bar']);
$this->assertEquals(123, Helper::arrayGet($array, 'one.two.three', delimiter: '/'));
}

public function testArrayDot()
Expand Down Expand Up @@ -78,6 +84,18 @@ public function testArrayDot()
'comments.2.text' => 'baz',
'one.two.three' => 789
], Helper::arrayDot($array));

$this->assertEquals([
'foo/bar/baz' => 123,
'foo/bar/qux' => 456,
'comments/0/id' => 1,
'comments/0/text' => 'foo',
'comments/1/id' => 2,
'comments/1/text' => 'bar',
'comments/2/id' => 3,
'comments/2/text' => 'baz',
'one.two.three' => 789
], Helper::arrayDot($array, glue: '/'));
}

public function testArraySet()
Expand All @@ -87,17 +105,20 @@ public function testArraySet()
['text' => 'foo'],
['id' => 2, 'text' => 'bar'],
['id' => 3, 'text' => 'baz'],
['bar' => 'jax'],
]
];

Helper::arraySet($array, 'comments.*.id', null, false);
Helper::arraySet($array, 'comments.*.x.y', 1, false);
Helper::arraySet($array, 'comments/*/bar', 'flux', true, '/');

$this->assertEquals([
'comments' => [
['id' => null, 'text' => 'foo', 'x' => ['y' => 1]],
['id' => 2, 'text' => 'bar', 'x' => ['y' => 1]],
['id' => 3, 'text' => 'baz', 'x' => ['y' => 1]],
['id' => null, 'text' => 'foo', 'x' => ['y' => 1], 'bar' => 'flux'],
['id' => 2, 'text' => 'bar', 'x' => ['y' => 1], 'bar' => 'flux'],
['id' => 3, 'text' => 'baz', 'x' => ['y' => 1], 'bar' => 'flux'],
['id' => null, 'x' => ['y' => 1], 'bar' => 'flux'],
]
], $array);
}
Expand Down Expand Up @@ -130,6 +151,13 @@ public function testArrayUnset()
'stuffs' => [],
'message' => "lorem ipsum",
], $array);

Helper::arrayUnset($array, 'users/*', '/');
$this->assertEquals([
'users' => [],
'stuffs' => [],
'message' => "lorem ipsum",
], $array);
}

public function testJoin()
Expand Down
2 changes: 1 addition & 1 deletion tests/UploadedFileTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public static function getSamplesMissingKeyFromUploadedFileValue(): array
foreach ($validUploadedFile as $key => $value) {
$uploadedFile = $validUploadedFile;
unset($uploadedFile[$key]);
$samples[] = $uploadedFile;
$samples[] = [$uploadedFile];
}

return $samples;
Expand Down