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

Add combined dictionary #53

Closed
wants to merge 2 commits into from
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
## Added
- Support for many tags (filter dictionaries)
- The registry can now be autowired
- Implement CombinedDictionary. Allow to merge different dictionaries into one

## Changed
- Category is now called tag #34
Expand Down
126 changes: 126 additions & 0 deletions spec/Knp/DictionaryBundle/Dictionary/CombinedSpec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
<?php

declare(strict_types=1);

namespace spec\Knp\DictionaryBundle\Dictionary;

use ArrayIterator;
use Knp\DictionaryBundle\Dictionary;
use PhpSpec\ObjectBehavior;

class CombinedSpec extends ObjectBehavior
{
function let(Dictionary $dictionary1, Dictionary $dictionary2, Dictionary $dictionary3)
{
$this->beConstructedWith('combined_dictionary', [
$dictionary1,
$dictionary2,
$dictionary3,
]);
}

function it_is_initializable()
{
$this->shouldHaveType(Dictionary\Combined::class);
}

function it_is_a_dictionary()
{
$this->shouldImplement(Dictionary::class);
}

function it_access_to_value_like_an_array($dictionary1, $dictionary2, $dictionary3)
{
$dictionary1->getIterator()->willReturn(new ArrayIterator(['foo1' => 'foo10']));

$dictionary2->getIterator()->willReturn(new ArrayIterator(['bar1' => 'bar10']));

$dictionary3->getIterator()->willReturn(new ArrayIterator(['baz1' => 'baz10']));

$this['foo1']->shouldBe('foo10');
$this['bar1']->shouldBe('bar10');
$this['baz1']->shouldBe('baz10');
}

function it_getvalues_should_return_dictionaries_values($dictionary1, $dictionary2, $dictionary3)
{
$dictionary1->getIterator()->willReturn(new ArrayIterator([
'foo1' => 'foo10',
'foo2' => 'foo20',
]));

$dictionary2->getIterator()->willReturn(new ArrayIterator([
'bar1' => 'bar10',
'bar2' => 'bar20',
]));

$dictionary3->getIterator()->willReturn(new ArrayIterator([
'foo1' => 'baz10',
'bar2' => 'baz20',
]));

$this->getKeys()->shouldReturn([
'foo1',
'foo2',
'bar1',
'bar2',
]);
$this->getValues()->shouldReturn([
'foo1' => 'baz10',
'foo2' => 'foo20',
'bar1' => 'bar10',
'bar2' => 'baz20',
]);
}

function it_can_iterate_over_dictionaries($dictionary1, $dictionary2, $dictionary3)
{
$dictionary1->getIterator()->willReturn(new ArrayIterator([
'foo1' => 'foo10',
'foo2' => 'foo20',
]));

$dictionary2->getIterator()->willReturn(new ArrayIterator([
'bar1' => 'bar10',
'bar2' => 'bar20',
]));

$dictionary3->getIterator()->willReturn(new ArrayIterator([
'foo2' => 'baz20',
'bar2' => 'baz20',
]));

$this->shouldIterateLike([
'foo1' => 'foo10',
'foo2' => 'baz20',
'bar1' => 'bar10',
'bar2' => 'baz20',
]);
}

function it_sums_the_count_of_elements($dictionary1, $dictionary2, $dictionary3)
{
$dictionary1->getIterator()->willReturn(new ArrayIterator([
'foo1' => 'foo10',
]));

$dictionary2->getIterator()->willReturn(new ArrayIterator([
'bar1' => 'bar10',
'bar2' => 'bar20',
]));

$dictionary3->getIterator()->willReturn(new ArrayIterator([
'baz1' => 'baz10',
'baz2' => 'baz20',
'baz3' => 'baz30',
'baz4' => 'baz40',
]));

$this->count()->shouldReturn(7);
}

function its_getname_should_return_dictionary_name()
{
$this->getName()->shouldReturn('combined_dictionary');
}
}
68 changes: 68 additions & 0 deletions spec/Knp/DictionaryBundle/Dictionary/Factory/CombinedSpec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

declare(strict_types=1);

namespace spec\Knp\DictionaryBundle\Dictionary\Factory;

use ArrayIterator;
use Knp\DictionaryBundle\Dictionary;
use PhpSpec\ObjectBehavior;

class CombinedSpec extends ObjectBehavior
{
function let()
{
$this->beConstructedWith(new Dictionary\DictionaryRegistry());
}

function it_is_initializable()
{
$this->shouldHaveType(Dictionary\Factory\Combined::class);
}

function it_is_a_factory()
{
$this->shouldHaveType(Dictionary\Factory::class);
}

function it_supports_specific_config()
{
$this->supports(['type' => 'combined'])->shouldReturn(true);
}

function it_creates_a_dictionary(Dictionary $dictionary1, Dictionary $dictionary2, Dictionary $dictionary3)
{
$dictionary1->getIterator()->willReturn(new ArrayIterator(['foo1' => 'foo10', 'foo2' => 'foo20']));
$dictionary1->getName()->willReturn('dictionary1');

$dictionary2->getIterator()->willReturn(new ArrayIterator(['bar1' => 'bar10', 'bar2' => 'bar20']));
$dictionary2->getName()->willReturn('dictionary2');

$dictionary3->getIterator()->willReturn(new ArrayIterator(['foo2' => 'baz20', 'bar2' => 'baz20']));
$dictionary3->getName()->willReturn('dictionary3');

$registry = new Dictionary\DictionaryRegistry();
$registry->add($dictionary1->getWrappedObject());
$registry->add($dictionary2->getWrappedObject());
$registry->add($dictionary3->getWrappedObject());
$this->beConstructedWith($registry);

$config = [
'type' => 'combined',
'dictionaries' => [
'dictionary1',
'dictionary2',
'dictionary3',
],
];

$dictionary = $this->create('combined_dictionary', $config);

$dictionary->getValues()->shouldReturn([
'foo1' => 'foo10',
'foo2' => 'baz20',
'bar1' => 'bar10',
'bar2' => 'baz20',
]);
}
}
90 changes: 90 additions & 0 deletions src/Knp/DictionaryBundle/Dictionary/Combined.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<?php

declare(strict_types=1);

namespace Knp\DictionaryBundle\Dictionary;

use Knp\DictionaryBundle\Dictionary;

final class Combined implements Dictionary
{
/**
* @var CallableDictionary
*/
private $dictionary;

public function __construct(string $name, array $dictionaries)
{
$this->dictionary = new CallableDictionary($name, function () use ($dictionaries) {
$data = [];

foreach ($dictionaries as $dictionary) {
$data = $this->merge($data, iterator_to_array($dictionary));
}

return $data;
});
}

public function getName(): string
{
return $this->dictionary->getName();
}

public function getValues(): array
{
return $this->dictionary->getValues();
}

public function getKeys(): array
{
return $this->dictionary->getKeys();
}

public function offsetExists($offset)
{
return $this->dictionary->offsetExists($offset);
}

public function offsetGet($offset)
{
return $this->dictionary->offsetGet($offset);
}

public function offsetSet($offset, $value): void
{
$this->dictionary->offsetSet($offset, $value);
}

public function offsetUnset($offset): void
{
$this->dictionary->offsetUnset($offset);
}

public function count(): int
{
return \count($this->dictionary->getValues());
}

public function getIterator()
{
return $this->dictionary->getIterator();
}

private function merge(array $array1, array $array2): array
{
if ($array1 === array_values($array1) && $array2 === array_values($array2)) {
return array_merge($array1, $array2);
}

$data = [];

foreach ([$array1, $array2] as $array) {
foreach ($array as $key => $value) {
$data[$key] = $value;
}
}

return $data;
}
}
51 changes: 51 additions & 0 deletions src/Knp/DictionaryBundle/Dictionary/Factory/Combined.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

declare(strict_types=1);

namespace Knp\DictionaryBundle\Dictionary\Factory;

use InvalidArgumentException;
use Knp\DictionaryBundle\Dictionary;
use Knp\DictionaryBundle\Dictionary\DictionaryRegistry;

class Combined implements Dictionary\Factory
{
const TYPE = 'combined';

/**
* @var Dictionary\DictionaryRegistry
*/
private $dictionaries;

public function __construct(DictionaryRegistry $dictionaries)
{
$this->dictionaries = $dictionaries;
}

/**
* {@inheritdoc}
*/
public function create($name, array $config): Dictionary
{
if (!isset($config['dictionaries'])) {
throw new InvalidArgumentException(sprintf(
'Dictionary of type %s must contains a key "dictionaries".',
self::TYPE
));
}

$dictionaries = array_map(function ($name) {
return $this->dictionaries[$name];
}, $config['dictionaries']);

return new Dictionary\Combined($name, $dictionaries);
}

/**
* {@inheritdoc}
*/
public function supports(array $config): bool
{
return isset($config['type']) ? self::TYPE === $config['type'] : false;
}
}