Skip to content

Commit

Permalink
feat: override push, also write tests for pull/push
Browse files Browse the repository at this point in the history
  • Loading branch information
Artem Pakhomov committed Aug 28, 2021
1 parent 3a1f62c commit a869648
Show file tree
Hide file tree
Showing 5 changed files with 244 additions and 37 deletions.
File renamed without changes.
97 changes: 63 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Asterisk notation for array access in PHP. Update array access to the next level
```php
use Setnemo\Asterisk;

$items = new Asterisk([
$items = new \Setnemo\Asterisk([
'Europe' => [
'Ukraine' => [
'capital' => 'Kyiv',
Expand Down Expand Up @@ -58,44 +58,44 @@ composer require setnemo/asterisk-notation

## Usage

Create a new Asterisk object:
Create a new \Setnemo\Asterisk object:

```php
use Adbar\Dot;
use Setnemo\Asterisk;
$asterisk = new Asterisk;
$asterisk = new \Setnemo\Asterisk;
$array = ['first_one' => ['second' => 'value'], 'first_two' => ['second' => 'value'],];
// With existing array
$asterisk = new Asterisk($array);
$asterisk = new \Setnemo\Asterisk($array);
// With existing \Adbar\Dot
$dot = new Dot($array);
$asterisk = new Asterisk($dot);
$asterisk = new \Setnemo\Asterisk($dot);
// or existing Asterisk
$newAsterisk = new Asterisk($asterisk);
$newAsterisk = new \Setnemo\Asterisk($asterisk);
```

## Methods

Asterisk has the following methods:

- [add()](#add) // done, test included
- [all()](#all) // done, test included
- [clear()](#clear) // use Dot::clear(), test included because used set()
- [count()](#count) // use Dot::count(), test included because used get()
- [delete()](#delete) // done, test included
- [flatten()](#flatten) // use Dot::flatten()
- [get()](#get) // done, test included
- [has()](#has) // done, with 2 new parameters, test included
- [isEmpty()](#isempty) // use Dot::isEmpty(), test included because used get()
- [merge()](#merge) // use Dot::merge()
- [mergeRecursive()](#mergerecursive)// use Dot::mergeRecursive()
- [mergeRecursiveDistinct()](#mergerecursivedistinct)// use Dot::mergeRecursiveDistinct()
- [pull()](#pull) // use Dot::pull(), **need write tests**, because used get(), clear(), delete()
- [push()](#push) // use Dot::push(), **need write tests**, because used get(), set()
- [add()](#add)
- [all()](#all)
- [clear()](#clear)
- [count()](#count)
- [delete()](#delete)
- [flatten()](#flatten)
- [get()](#get)
- [has()](#has)
- [isEmpty()](#isempty)
- [merge()](#merge)
- [mergeRecursive()](#mergerecursive)
- [mergeRecursiveDistinct()](#mergerecursivedistinct)
- [pull()](#pull)
- [push()](#push)
- [replace()](#replace) // use Dot::replace(), **need write tests**, because used get(), set()
- [set()](#set) // done, test included
- [setArray()](#setarray) // use Dot::setarray()
- [setReference()](#setreference) // use Dot::setReference()
- [set()](#set)
- [setArray()](#setarray)
- [setReference()](#setreference)
- [toJson()](#tojson) // use Dot::toJson(), **need write tests**, because used get()

<a name="add"></a>
Expand Down Expand Up @@ -232,31 +232,31 @@ $asterisk->has([

With asterisk:
```php
$asterisk = new Asterisk(['1' => ['second' => 'value'],'2' => ['second' => 'value']]);
$asterisk = new \Setnemo\Asterisk(['1' => ['second' => 'value'],'2' => ['second' => 'value']]);
$asterisk->has('*.second'); // true
$asterisk = new Asterisk(['1' => ['first' => ['test' => 42]],'2' => ['second' => 'value'],'3' => ['third' => ['value' => 42]]]);
$asterisk = new \Setnemo\Asterisk(['1' => ['first' => ['test' => 42]],'2' => ['second' => 'value'],'3' => ['third' => ['value' => 42]]]);
$asterisk->has('*.*.value'); // true
$asterisk = new Asterisk(['user1' => ['name' => 'John', 'job' => 'warrior'], 'user2' => ['name' => 'Robin', 'job' => 'archer']);
$asterisk = new \Setnemo\Asterisk(['user1' => ['name' => 'John', 'job' => 'warrior'], 'user2' => ['name' => 'Robin', 'job' => 'archer']);
$asterisk->has('*.spouse'); // false
```

With asterisk and value:
```php
$asterisk = new Asterisk([]);
$asterisk = new \Setnemo\Asterisk([]);
$asterisk->has('*', false); // false
$asterisk = new Asterisk(['*' => ['second' => 'VALUE']]);
$asterisk = new \Setnemo\Asterisk(['*' => ['second' => 'VALUE']]);
$asterisk->has('*.second', 'VALUE'); // true
$asterisk->has('*.second', 'value'); // false because lowercase
$asterisk = new Asterisk(['*' => ['second' => 'value'], 0 => [0 => 0], 11 => 11]);
$asterisk = new \Setnemo\Asterisk(['*' => ['second' => 'value'], 0 => [0 => 0], 11 => 11]);
$asterisk->has('*.11', 11); // true
$asterisk = new Asterisk(['1' => ['second' => 'value'],'2' => ['second' => '-']]);
$asterisk = new \Setnemo\Asterisk(['1' => ['second' => 'value'],'2' => ['second' => '-']]);
$asterisk->has('*.second', 'value'); // false because 'second' => '-'
$asterisk = new Asterisk(['1' => ['second' => 'value'],'2' => ['second' => 'value']]);
$asterisk = new \Setnemo\Asterisk(['1' => ['second' => 'value'],'2' => ['second' => 'value']]);
$asterisk->has('*.second', 'value'); // true
```
With asterisk and value (non-strict mode):
```php
$asterisk = new Asterisk([
$asterisk = new \Setnemo\Asterisk([
'locations' => [
'Europe' => [
'Ukraine' => [
Expand Down Expand Up @@ -315,7 +315,7 @@ $asterisk->isEmpty();

Also works with asterisk in key:
```php
$asterisk = new Asterisk(['user1' => ['name' => 'John'], 'user2' => ['name' => 'Robin']);
$asterisk = new \Setnemo\Asterisk(['user1' => ['name' => 'John'], 'user2' => ['name' => 'Robin']);
$asterisk->isEmpty('*.name'); // false
$asterisk->isEmpty('*.spouse'); // true
```
Expand Down Expand Up @@ -355,12 +355,20 @@ Returns a given default value, if the given key doesn't exist:
```php
echo $asterisk->pull('user.name', 'some default value');
```
> Default value not work with asterisk in query!
Returns all the stored items as an array and clears the Dot object:
```php
$items = $asterisk->pull();
```

Key with asterisk:
```php
$asterisk = new \Setnemo\Asterisk([['user1' => ['name' => 'John', 'job' => 'warrior'], 'user2' => ['name' => 'Robin', 'job' => 'archer'],]]);
$result = $asterisk->pull('*.name'); // ['user1.name' => 'John', 'user2.name' => 'Robin']
$all = $asterisk->all(); // ['user1' => ['job' => 'warrior'], 'user2' => ['job' => 'archer'],]

```
<a name="push"></a>
### push()

Expand All @@ -380,6 +388,27 @@ $asterisk->push('John');
$array[] = 'John';
```

With asterisk:
```php
$asterisk = new \Setnemo\Asterisk([
'first_one' => ['second' => 'value'],
'first_two' => ['second' => 'value']
]);
$asterisk->push('*.second', 'John');

$asterisk->all();
/*
[
'first_one' => ['second' => ['value','VALUE']],
'first_two' => ['second' => ['value','VALUE']]
]
*/
```

Also work as Asterisk::set('key', 'value', true), where true is asterisk boolean.
See [Asterisk::set()](#set)


<a name="replace"></a>
### replace()

Expand Down Expand Up @@ -425,4 +454,4 @@ $asterisk->set([

## License

[MIT license](LICENSE)
[MIT license](LICENSE.txt)
40 changes: 37 additions & 3 deletions src/Asterisk.php
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,35 @@ public function set($keys, $value = null, bool $asterisk = true)
$items = $value;
}


/**
* Push a given value to the end of the array
* in a given key
*
* @param mixed $key
* @param mixed $value
*/
public function push($key, $value = null, bool $asterisk = true)
{
if (is_null($value)) {
$this->items[] = $key;

return;
}

$items = $this->get($key);
if ($asterisk && $this->keyHasStars($key)) {
$items = $this->prepareStarsAffectedKeys($key, $value);
foreach ($items as $key => $item) {
$changedItems = $this->get($key);
$this->set($key, array_merge((array)$changedItems, (array)$value), false);
}
} elseif (is_array($items) || is_null($items)) {
$items[] = $value;
$this->set($key, $items, $asterisk);
}
}

/**
* @param string $asteriskKey
* @param $value
Expand Down Expand Up @@ -188,6 +217,11 @@ protected function getAffectedAndFilterKeys(array &$keys): array
protected function getStarsAffectedKeys(string $asteriskKey, bool $withValues = true): ?array
{
$keys = $this->flatten();

if ([] === $keys) {
return null;
}

$result = [];
$pattern = '/' . strtr($asteriskKey, ['*' => '[^\.]+']) . '/';
foreach ($keys as $key => $v) {
Expand Down Expand Up @@ -233,12 +267,12 @@ protected function hasValueAndStrict(
}

/**
* @param $keys
* @param $key
* @return bool
*/
protected function keyHasStars($keys): bool
protected function keyHasStars($key): bool
{
$counter = array_count_values(explode('.', (string) $keys) ?? []);
$counter = array_count_values(explode('.', (string) $key) ?? []);

return (bool) ($counter['*'] ?? 0);
}
Expand Down
65 changes: 65 additions & 0 deletions tests/unit/PullTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

namespace Tests\Unit;

use Codeception\Test\Unit;
use Setnemo\Asterisk;

/**
* Class PullTest
*/
class PullTest extends Unit
{
/**
* @dataProvider pullWithStar()
*/
public function testGet(array $example): void
{
$asterisk = new Asterisk($example['constructor']);

$this->assertEquals($example['expected_pull'], $asterisk->pull($example['keys']));
$this->assertEquals($example['expected_all'], $asterisk->all());
}

public function pullWithStar(): array
{
return [
/* #0 */ [[
'constructor' => ['1' => ['second' => 'value'],'2' => ['second' => 'value']],
'expected_pull' => ['1.second' => 'value', '2.second' => 'value',],
'expected_all' => ['1' => [], '2' => [], ],
'keys' => '*.second',
]],
/* #1 */ [[
'constructor' => ['1' => ['first' => ['value' => 42]],'2' => ['second' => 'value'],'3' => ['third' => ['value' => 42]]],
'expected_pull' => ['1.first.value' => 42, '3.third.value' => 42,],
'expected_all' => ['1' => ['first' => []], '2' => ['second' => 'value'], '3' => ['third' => []], ],
'keys' => '*.*.value',
]],
/* #2 */ [[
'constructor' => ['1' => ['first' => ['value' => 42]],'2' => ['second' => 'value'],'3' => ['third' => ['value' => 42]]],
'expected_pull' => 42,
'expected_all' => ['1' => ['first' => ['value' => 42]],'2' => ['second' => 'value'],'3' => ['third' => []]],
'keys' => '3.third.value',
]],
/* #3 */ [[
'constructor' => ['user1' => ['name' => 'John', 'job' => 'warrior'], 'user2' => ['name' => 'Robin', 'job' => 'archer'],],
'expected_pull' => null,
'expected_all' => ['user1' => ['name' => 'John', 'job' => 'warrior'], 'user2' => ['name' => 'Robin', 'job' => 'archer'],],
'keys' => '3.third.value',
]],
/* #4 */ [[
'constructor' => ['user1' => ['name' => 'John', 'job' => 'warrior'], 'user2' => ['name' => 'Robin', 'job' => 'archer'],],
'expected_pull' => ['user1.name' => 'John', 'user2.name' => 'Robin'],
'expected_all' => ['user1' => ['job' => 'warrior'], 'user2' => ['job' => 'archer'],],
'keys' => '*.name',
]],
/* #5 */ [[
'constructor' => ['user1' => ['name' => 'John', 'job' => 'warrior'], 'user2' => ['name' => 'Robin', 'job' => 'archer'],],
'expected_pull' => null,
'expected_all' => ['user1' => ['name' => 'John', 'job' => 'warrior'], 'user2' => ['name' => 'Robin', 'job' => 'archer'],],
'keys' => '*.spouse',
]],
];
}
}
79 changes: 79 additions & 0 deletions tests/unit/PushTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php

namespace Tests\Unit;

use Codeception\Test\Unit;
use Setnemo\Asterisk;

/**
* Class PushTest
*/
class PushTest extends Unit
{
/**
* @dataProvider pushWithStar()
*/
public function testSet(array $example): void
{
$asterisk = new Asterisk($example['constructor']);
$asterisk->push($example['keys'], $example['value']);

$this->assertEquals($example['expected'], $asterisk->all());
}

public function pushWithStar(): array
{
return [
/* #0 */ [['constructor' => [], 'expected' => [
'*' => ['second' => ['VALUE']],
], 'keys' => '*.second', 'value' => 'VALUE']],
/* #1 */ [['constructor' => [
'first_one' => ['second' => 'value'],
'first_two' => ['second' => 'value']
], 'expected' => [
'first_one' => ['second' => ['value','VALUE']],
'first_two' => ['second' => ['value','VALUE']]
], 'keys' => '*.second', 'value' => 'VALUE']],
/* #2 */ [['constructor' => [
'first_one' => ['second2' => 'value'],
'first_two' => ['second' => 'value']
], 'expected' => [
'first_one' => ['second2' => 'value', 'second' => ['VALUE']],
'first_two' => ['second' => ['value','VALUE']]
], 'keys' => '*.second', 'value' => 'VALUE']],
/* #3 */ [['constructor' => [
'first_one' => ['second' => [
'third_one' => ['fourth' => ['value']],
'third_two' => ['fourth' => ['value']],
], 'second2' => [
'third_one' => ['fourth' => 'value'],
'third_two' => ['fourth' => 'value'],
],
],
'first_two' => ['second' => [
'third_one' => [],
'third_two' => ['fourth' => 'value'],
], 'second2' => [
'third_one' => ['fourth2' => 'value'],
],
],
], 'expected' => [
'first_one' => ['second' => [
'third_one' => ['fourth' => ['value','VALUE']],
'third_two' => ['fourth' => ['value','VALUE']],
], 'second2' => [
'third_one' => ['fourth' => 'value'],
'third_two' => ['fourth' => 'value'],
],
],
'first_two' => ['second' => [
'third_one' => [],
'third_two' => ['fourth' => ['value','VALUE']],
], 'second2' => [
'third_one' => ['fourth2' => 'value'],
],
],
], 'keys' => '*.second.*.fourth', 'value' => 'VALUE']],
];
}
}

0 comments on commit a869648

Please sign in to comment.