diff --git a/README.md b/README.md index cd76884..9db62b0 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ return new SizeSorter([ /* * Returns: * - * array: [ + * Illuminate\Support\Collection([ * 'XXS', * 'XL', * 'XXL', @@ -64,7 +64,7 @@ return new SizeSorter([ * '28', * '54', * 'ONE SIZE', - * ] + * ]) */ ``` @@ -72,29 +72,21 @@ return new SizeSorter([ use DragonCode\SizeSorter\SizeSorter; // Laravel models collection -$items = Size::query()->get(); +$items = Size::get(); return new SizeSorter($items) ->column('title') ->sort(); ``` +The static `items` method is also available: + ```php use DragonCode\SizeSorter\SizeSorter; -// Laravel collection -$items = collect([...]); - -return new SizeSorter($items) - ->sort(); - -/* - * Returns: - * - * Collection: [ - * // ... - * ] - */ +return SizeSorter::items([ + // ... +])->sort(); ``` ### Groups Order @@ -137,7 +129,10 @@ use DragonCode\SizeSorter\Enum\Group; use DragonCode\SizeSorter\SizeSorter; return new SizeSorter($items) - ->orderBy([Group::BraSize, Group::OtherSizes]) + ->orderBy([ + Group::BraSize, + Group::OtherSizes, + ]) ->sort(); ``` @@ -148,12 +143,98 @@ ascending order: 3 - 5 - 1 - 2 - 4 ``` +### Custom Column + +When working with an array of objects, you can specify which value can be used for sorting. + +```php +use DragonCode\SizeSorter\SizeSorter; + +return new SizeSorter($items) + ->column('foo') + ->sort(); +``` + +You can also use "dotted" notation: + +```php +use DragonCode\SizeSorter\SizeSorter; + +$items = [ + [ + 'foo' => [ + 'bar' => [ + 'baz' => 'Some value', + ] + ] + ] +]; + +return new SizeSorter($items) + ->column('foo.bar.baz') + ->sort(); +``` + +And you can use the callback function in the same way: + +```php +use DragonCode\SizeSorter\SizeSorter; + +class Foo +{ + public function __construct( + public int $number, + public string $value1, + public string $value2, + ) {} +} + +$items = [ + new Foo(1, 'first 1', 'first 2'), + new Foo(2, 'second 1', 'second 2'), +]; + +return new SizeSorter($items) + ->column(function (Foo $item) { + return $item->number % 2 === 0 + ? $item->value1 + : $item->value2; + }) + ->sort(); +``` + +And this is also possible: + +```php +use DragonCode\SizeSorter\SizeSorter; + +$items = [ + ['foo' => 'XS'], + ['foo' => '2XS'], + ['foo' => '3XL'], +]; + +return new SizeSorter($items) + ->column(function (array $item) { + return match ($item['foo']) { + '2XS' => 'XXS', + '3XL' => 'XXXL', + default => $item['foo'] + }; + }) + ->sort(); +``` + +## Upgrade Guide + +You can find the upgrade documentation [here](UPGRADE.md). + ## License This package is licensed under the [MIT License](LICENSE). -[badge_build]: https://img.shields.io/github/actions/workflow/status/TheDragonCode/size-sorter/phpunit.yml?style=flat-square +[badge_build]: https://img.shields.io/github/actions/workflow/status/TheDragonCode/size-sorter/tests.yml?style=flat-square [badge_downloads]: https://img.shields.io/packagist/dt/dragon-code/size-sorter.svg?style=flat-square diff --git a/UPGRADE.md b/UPGRADE.md new file mode 100644 index 0000000..4056dde --- /dev/null +++ b/UPGRADE.md @@ -0,0 +1,67 @@ +# Upgrade Guide + +## To 2.x from 1.0 + +### Updating Dependencies + +You should update the following dependencies in your application's `composer.json` file: + +- `dragon-code/size-sorter` to `^2.0` + +### Changing Namespace + +Namespace should be changed: + +You should update the namespace and method name to call: + +```diff +-DragonCode\SizeSorter\Sorter::sort() ++DragonCode\SizeSorter\SizeSorter::items() + +-use DragonCode\SizeSorter\Enum\Group; ++use DragonCode\SizeSorter\Enums\GroupEnum; +``` + +### Changing parameters and call methods + +```diff +-use DragonCode\SizeSorter\Sorter; +-use DragonCode\SizeSorter\Enum\Group; ++use DragonCode\SizeSorter\SizeSorter; ++use DragonCode\SizeSorter\Enums\GroupEnum; + +-$sort1 = Sorter::sort($items, 'column.name', [1, 2]); +-$sort2 = Sorter::sort($items, 'column.name', [Group::GROUP_1, Group::GROUP_2]); ++ ++$sort = SizeSorter::items($items) ++ ->column('column.name') ++ ->orderBy([ ++ GroupEnum::LetterClothingSize, ++ GroupEnum::ClothesAndShoes, ++ ]) ++ ->sort(); +``` + +### Groups + +> [!WARNING] +> +> The array to sort must now consist of GroupEnum objects. +> Integers are no longer accepted. + +'Group::GROUP_1' replaced by `GroupEnum::LetterClothingSize` +'Group::GROUP_2' replaced by `GroupEnum::ClothesAndShoes` +'Group::GROUP_3' replaced by `GroupEnum::BraSize` +'Group::GROUP_4' replaced by `GroupEnum::OverallDimensions` +'Group::GROUP_5' replaced by `GroupEnum::OtherSizes` + +```php +enum GroupEnum: int +{ + case LetterClothingSize = 1; + case ClothesAndShoes = 2; + case BraSize = 3; + case OverallDimensions = 4; + case OtherSizes = 5; +} +``` diff --git a/src/Contracts/GroupMatcher.php b/src/Contracts/GroupMatcher.php deleted file mode 100644 index ff5899b..0000000 --- a/src/Contracts/GroupMatcher.php +++ /dev/null @@ -1,10 +0,0 @@ -squish() - ->trim() - ->replace('\\', '/') - ->explode('/') - ->map(static fn (string $value) => static::compact($value)) - ->implode('/') - ->toString(); - } - - protected static function compact(string $value): string - { - return Str::of($value) - ->slug() - ->explode('-') - ->map(static fn (string $value) => Resolver::size($value)) - ->implode('-') - ->toString(); - } -} diff --git a/src/Enum/Group.php b/src/Enum/Group.php deleted file mode 100644 index e476868..0000000 --- a/src/Enum/Group.php +++ /dev/null @@ -1,30 +0,0 @@ -value, $key, static::prepare($value)]); + } + + public static function value(string $key): Stringable + { + return Str::of($key)->afterLast(static::Delimiter); + } + + protected static function prepare(mixed $value): string + { + return $value; + } +} diff --git a/src/Groups/LetterClothingGroup.php b/src/Groups/LetterClothingGroup.php new file mode 100644 index 0000000..fc7fa2c --- /dev/null +++ b/src/Groups/LetterClothingGroup.php @@ -0,0 +1,67 @@ + 100, + 'm' => 1000, + 'l' => 10000, + ]; + + protected static function prepare(mixed $value): string + { + return Str::of($value) + ->explode('_') + ->map(static fn (mixed $value) => static::convert($value)) + ->implode('_'); + } + + protected static function convert(mixed $value): string + { + if (in_array($value, ['s', 'm', 'l'])) { + return (string) static::multiply($value, 1); + } + + if (preg_match('/^(x+)?s$/', $value, $matches)) { + $count = isset($matches[1]) ? strlen($matches[1]) : 1; + + return (string) static::multiply('s', $count); + } + + if (preg_match('/^(\d+)xs$/', $value, $matches)) { + return (string) static::multiply('s', (int) $matches[1], 1); + } + + if (preg_match('/^(x+)l$/', $value, $matches)) { + $count = strlen($matches[1]); + + return (string) static::multiply('l', $count); + } + + if (preg_match('/^(\d+)xl$/', $value, $matches)) { + return (string) static::multiply('l', (int) $matches[1], 1); + } + + return '0'; + } + + protected static function multiply(string $key, int $count, int $plus = 0): int + { + return static::$multiplier[$key] * $count + $plus; + } +} diff --git a/src/Groups/OtherGroup.php b/src/Groups/OtherGroup.php new file mode 100644 index 0000000..c294420 --- /dev/null +++ b/src/Groups/OtherGroup.php @@ -0,0 +1,17 @@ + Str::of($value) + ->replaceMatches('/[\W_\s]+/u', ' ') + ->trim() + ->slug('_') + ->toString() + ); + } +} diff --git a/src/Normalizers/Normalizer.php b/src/Normalizers/Normalizer.php new file mode 100644 index 0000000..586a0c3 --- /dev/null +++ b/src/Normalizers/Normalizer.php @@ -0,0 +1,10 @@ +each(static function (mixed $value, mixed $key) use (&$result) { - if (! $value instanceof IC) { - $result->put($key, $value); - - return; - } - - $result = static::merge($result, static::flatten($value)); - }); - - return $result; - } - - public static function merge(IC $result, IC $second): IC - { - $second->each( - static fn (mixed $value, mixed $key) => $result->put($key, $value) - ); - - return $result; - } -} diff --git a/src/Services/GroupsDetector.php b/src/Services/GroupsDetector.php deleted file mode 100644 index 3157b1e..0000000 --- a/src/Services/GroupsDetector.php +++ /dev/null @@ -1,36 +0,0 @@ - */ - protected static array $detectors = [ - Group1::class => Group::LetterClothingSize, - Group2::class => Group::ClothesAndShoes, - Group3::class => Group::BraSize, - Group4::class => Group::OverallDimensions, - ]; - - protected static Group $default = Group::OtherSizes; - - public static function detect(string $value): int - { - foreach (static::$detectors as $detector => $group) { - if ($detector::detect($value)) { - return $group->value; - } - } - - return static::$default->value; - } -} diff --git a/src/Services/MainLogic.php b/src/Services/MainLogic.php deleted file mode 100644 index 6e35aed..0000000 --- a/src/Services/MainLogic.php +++ /dev/null @@ -1,124 +0,0 @@ -has($key)) { - $result->put($key, $items->get($key)); - } - } - - return $result; - } - - protected static function sorting(IC $items, string $column = 'value'): IC - { - return $items - ->groupBy(static fn (mixed $size) => static::detectGroup( - static::resolveValue($size, $column)->toString() - ), true) - ->map(static fn (IC $items, int $group) => static::sortByGroup($items, $group, $column)); - } - - protected static function sortByGroup(IC $items, int $group, string $column): IC - { - return match ($group) { - Group::LetterClothingSize->value => static::sortChars($items, $column), - Group::ClothesAndShoes->value => static::sortNumbers($items, $column), - default => static::sortArrows($items, $column) - }; - } - - protected static function sortChars(IC $values, string $column): IC - { - return $values->groupBy( - static fn (mixed $size) => static::resolveValue($size, $column) - ->match('/(s|m|l)/') - ->toString(), - true - ) - ->sortKeysDesc() - ->map( - static fn (IC $values, string $group) => $group === 's' - ? static::sortSmallSizes($values, $column) - : static::sortArrows($values, $column) - ); - } - - protected static function sortSmallSizes(IC $values, string $column): IC - { - return $values->sort( - ArrowSorter::get($column, -1) - ) - ->groupBy(static fn (mixed $size) => static::resolveValue($size, $column)->toString(), true) - ->map(static fn (IC $values) => static::sortSpecialChars($values, $column)); - } - - protected static function sortSpecialChars(IC $values, string $column): IC - { - return $values->sort( - CharSorter::get($column) - ); - } - - protected static function sortArrows(IC $values, string $column): IC - { - return $values->sort( - ArrowSorter::get($column) - ); - } - - protected static function sortNumbers(IC $items, string $column): IC - { - return $items->sort( - NumberSorter::get($column) - ); - } - - protected static function detectGroup(string $value): int - { - return GroupsDetector::detect($value); - } - - protected static function collect(): IC - { - return Collection::make(); - } - - protected static function flatten(IC $items): IC - { - return Collection::flatten($items); - } - - protected static function resolveValue(mixed $value, string $column): Stringable - { - return Resolver::value($value, $column); - } -} diff --git a/src/Services/Order.php b/src/Services/Order.php deleted file mode 100644 index f445016..0000000 --- a/src/Services/Order.php +++ /dev/null @@ -1,68 +0,0 @@ -fill($groups) - : $this->groups(); - } - - protected function fill(array $groups): array - { - return $this->fillDefault( - $this->fillCustom($groups) - ); - } - - protected function fillCustom(array $groups): array - { - $result = []; - - foreach ($groups as $group) { - $value = $this->resolveValue($group); - - if ($this->existGroup($value)) { - $result[] = $value; - } - } - - return $result; - } - - protected function fillDefault(array $groups): array - { - foreach ($this->groups() as $group) { - if (! in_array($group, $groups, true)) { - $groups[] = $group; - } - } - - return $groups; - } - - protected function existGroup(int $value): bool - { - return Group::exists($value); - } - - /** - * @return Group[] - */ - protected function groups(): array - { - return Group::values(); - } - - protected function resolveValue(Group|int $group): int - { - return $group->value ?? $group; - } -} diff --git a/src/Services/Processor.php b/src/Services/Processor.php new file mode 100644 index 0000000..108de1b --- /dev/null +++ b/src/Services/Processor.php @@ -0,0 +1,61 @@ +sortItems($items, $column); + + $map = $this->sortGroups($sorted, $orderBy); + + return Map::apply($items, $map); + } + + protected function sortItems(Collection $items, Closure $column): Collection + { + return $this->map($items, $column) + ->groupBy(fn (mixed $value, string $key) => $this->detectGroup($key), true) + ->map(fn (Collection $values, int $group) => $this->sortPerGroup($values, $group)); + } + + protected function sortGroups(Collection $items, array $orderBy): Collection + { + return Collection::make($orderBy)->map( + fn (GroupEnum $group) => $items->get($group->value) + )->collapseWithKeys(); + } + + protected function sortPerGroup(Collection $items, int $group): Collection + { + return match ($group) { + GroupEnum::LetterClothingSize->value, + GroupEnum::ClothesAndShoes->value => $this->sort->byNumber($items), + default => $this->sort->byAlphabet($items), + }; + } + + protected function detectGroup(string $key): int + { + return (int) Str::before($key, Group::Delimiter); + } + + protected function map(Collection $items, Closure $column): Collection + { + return Map::make($items, $column); + } +} diff --git a/src/Services/Resolver.php b/src/Services/Resolver.php deleted file mode 100644 index 40fc9bc..0000000 --- a/src/Services/Resolver.php +++ /dev/null @@ -1,82 +0,0 @@ - self::fromEnum($value), - $value instanceof ArrayAccess, - is_array($value) => self::fromArray($value, $column), - $value instanceof BaseStringable => self::fromStringable($value), - default => $value->{$column} ?? $value - }; - } - - public static function value(mixed $value, string $column): Stringable - { - return static::prepare($value, $column) - ->replace(['\\', '-'], '/') - ->before('/') - ->lower(); - } - - public static function number(mixed $value, string $column): array - { - return static::prepare($value, $column) - ->replace(['\\', '-'], '/') - ->explode('/') - ->map(static fn (mixed $val) => (int) $val) - ->toArray(); - } - - public static function size(string $value): string - { - if (Str::match($value, '/(\d+x)/')) { - return Str::replace('x', '', $value); - } - - if ($count = Str::count($value)) { - return Str::replace($value, Str::pad($count), $count); - } - - return Str::replace($value, ['s', 'm', 'l'], ['0s', '0m', '0l']); - } - - public static function callback(callable $callback, mixed ...$parameters): mixed - { - return $callback(...$parameters); - } - - protected static function prepare(mixed $value, string $column): Stringable - { - return Str::of( - static::key($value, $column) - )->squish()->trim(); - } - - protected static function fromEnum(BackedEnum $item): mixed - { - return $item->value ?? $item->name; - } - - protected static function fromStringable(BaseStringable $item): mixed - { - return method_exists($item, 'toString') ? $item->toString() : (string) $item; - } - - protected static function fromArray(mixed $item, string $column): mixed - { - return Arr::get($item, $column, $item); - } -} diff --git a/src/Services/Sort.php b/src/Services/Sort.php new file mode 100644 index 0000000..7cd5139 --- /dev/null +++ b/src/Services/Sort.php @@ -0,0 +1,23 @@ +sortKeysUsing( + NumberSorter::callback() + ); + } + + public function byAlphabet(Collection $items): Collection + { + return $items->sortKeys(); + } +} diff --git a/src/Services/Str.php b/src/Services/Str.php deleted file mode 100644 index cff4a1e..0000000 --- a/src/Services/Str.php +++ /dev/null @@ -1,46 +0,0 @@ -column = $name; @@ -33,25 +36,49 @@ public function column(string $name): static } /** - * @param Group[]|null $order + * @param GroupEnum[]|null $order * @return $this */ - public function orderBy(?array $order): static + public function orderBy(?iterable $order): static { - Validator::ensure($order, Group::class); + if (empty($order)) { + return $this; + } - $this->orderBy = $this->order->resolve($order); + Validator::ensure($order, GroupEnum::class); + + $this->orderBy = $order; return $this; } - public function sort(): iterable + public function sort(): Collection + { + return $this->processor->run( + $this->getItems(), + $this->getColumn(), + $this->getOrderBy() + ); + } + + protected function getItems(): Collection { - return MainLogic::sort($this->items, $this->column, $this->getOrderBy()); + return new Collection($this->items); + } + + protected function getColumn(): Closure + { + if ($this->column instanceof Closure) { + return $this->column; + } + + return fn (mixed $item) => Resolve::value($item, $this->column); } - protected function getOrderBy(): ?array + protected function getOrderBy(): array { - return $this->orderBy ?? $this->order->resolve(); + return GroupOrder::get( + $this->orderBy + ); } } diff --git a/src/Sorters/ArrowSorter.php b/src/Sorters/ArrowSorter.php index 77f57a4..ea1e301 100644 --- a/src/Sorters/ArrowSorter.php +++ b/src/Sorters/ArrowSorter.php @@ -4,14 +4,13 @@ namespace DragonCode\SizeSorter\Sorters; -class ArrowSorter extends BaseSorter +use Closure; + +class ArrowSorter extends Sorter { - public static function get(string $column, int $arrow = 1): callable + public static function callback(int $arrow = 1): Closure { - return static function (mixed $a, mixed $b) use ($arrow, $column) { - $a = static::key($a, $column); - $b = static::key($b, $column); - + return static function (mixed $a, mixed $b) use ($arrow) { if ($a === $b) { return 0; } diff --git a/src/Sorters/BaseSorter.php b/src/Sorters/BaseSorter.php deleted file mode 100644 index b916578..0000000 --- a/src/Sorters/BaseSorter.php +++ /dev/null @@ -1,16 +0,0 @@ -afterLast(Group::Delimiter) + ->explode('_') + ->map(static fn (int|string $value) => (int) $value) + ->all(); } } diff --git a/src/Sorters/Sorter.php b/src/Sorters/Sorter.php new file mode 100644 index 0000000..0fcab34 --- /dev/null +++ b/src/Sorters/Sorter.php @@ -0,0 +1,12 @@ +all(); + } +} diff --git a/src/Support/Map.php b/src/Support/Map.php new file mode 100644 index 0000000..25d7934 --- /dev/null +++ b/src/Support/Map.php @@ -0,0 +1,58 @@ + $value) { + $normalized = static::normalize($column($value)); + + $name = static::group($normalized, $key); + + $result[$name] = $key; + } + + return new Collection($result); + } + + public static function apply(Collection $items, Collection $map): Collection + { + Validator::mapCount($items, $map); + + return $map->mapWithKeys(fn (int|string $index) => [ + $index => $items->get($index), + ]); + } + + protected static function group(int|string $value, int|string $key): string + { + return match (true) { + LetterClothingGroup::detect($value) => LetterClothingGroup::normalize($value, $key), + ClothesAndShoesGroup::detect($value) => ClothesAndShoesGroup::normalize($value, $key), + BraGroup::detect($value) => BraGroup::normalize($value, $key), + OverallDimensionsGroup::detect($value) => OverallDimensionsGroup::normalize($value, $key), + default => OtherGroup::normalize($value, $key) + }; + } + + protected static function normalize(float|int|string|Stringable $value): string + { + return KeyNormalizer::normalize($value); + } +} diff --git a/src/Support/Resolve.php b/src/Support/Resolve.php new file mode 100644 index 0000000..046c29a --- /dev/null +++ b/src/Support/Resolve.php @@ -0,0 +1,41 @@ +value; + } + + if ($item instanceof UnitEnum) { + return $item->name; + } + + if ($item instanceof Stringable) { + return (string) $item; + } + + if (is_object($item)) { + return $item->{$column}; + } + + if (is_array($item)) { + return Arr::get($item, $column); + } + + return $item; + } +} diff --git a/src/Support/Validator.php b/src/Support/Validator.php index c42ce28..dad44bc 100644 --- a/src/Support/Validator.php +++ b/src/Support/Validator.php @@ -5,11 +5,51 @@ namespace DragonCode\SizeSorter\Support; use Illuminate\Support\Collection; +use LengthException; +use UnexpectedValueException; + +use function get_debug_type; class Validator { - public static function ensure(?iterable $items, string $class): void + /** + * @template TEnsureOfType + * + * @param class-string|array>|'string'|'int'|'float'|'bool'|'array'|'null' $class + */ + public static function ensure(iterable $items, string $class): void + { + foreach ($items as $index => $value) { + if ($value instanceof $class) { + continue; + } + + $type = get_debug_type($value); + + throw new UnexpectedValueException( + vsprintf("Collection should only include [%s] items, but '%s' found at position %d.", [ + $class, + $type, + $index, + ]) + ); + } + } + + public static function mapCount(Collection $items, Collection $map): void { - (new Collection($items))->ensure($class); + $itemsCount = $items->count(); + $mapCount = $map->count(); + + if ($itemsCount === $mapCount) { + return; + } + + throw new LengthException( + vsprintf('The count of items in the map (%d) and collection (%d) should be the same.', [ + $mapCount, + $itemsCount, + ]) + ); } } diff --git a/tests/Fixtures/Enums/IntegerEnum.php b/tests/Fixtures/Enums/IntegerEnum.php new file mode 100644 index 0000000..d12a3d7 --- /dev/null +++ b/tests/Fixtures/Enums/IntegerEnum.php @@ -0,0 +1,14 @@ + 'ONE SIZE', - 105 => 'XXS', - 106 => '2', - 110 => 'M', - 113 => 'XL/2XL', - 116 => '80B', - 118 => '70B', - 130 => '44-46', - 131 => 'some', - 132 => '1', - 133 => '30', - 136 => '44/46', - 137 => 'XXS/XS', - 139 => '52-56', - 149 => '21', - 150 => '3', - 155 => '41х38х15 см', - 156 => '39х38х15 см', - ]; - - $this->assertSame( - [ - // 1 - 105 => 'XXS', - 137 => 'XXS/XS', - 110 => 'M', - 113 => 'XL/2XL', - // 2 - 132 => '1', - 106 => '2', - 150 => '3', - 149 => '21', - 133 => '30', - 130 => '44-46', - 136 => '44/46', - 139 => '52-56', - // 3 - 118 => '70B', - 116 => '80B', - // 4 - 156 => '39х38х15 см', - 155 => '41х38х15 см', - // 5 - 104 => 'ONE SIZE', - 131 => 'some', - ], - SizeSorter::items($items)->sort()->toArray() - ); - } - - public function testStringable(): void - { - $items = [ - 104 => new Str('ONE SIZE'), - 105 => new Str('XXS'), - 106 => new Str('2'), - 110 => new Str('M'), - 113 => new Str('XL/2XL'), - 116 => new Str('80B'), - 118 => new Str('70B'), - 130 => new Str('44-46'), - 131 => new Str('some'), - 132 => new Str('1'), - 133 => new Str('30'), - 136 => new Str('44/46'), - 137 => new Str('XXS/XS'), - 139 => new Str('52-56'), - 149 => new Str('21'), - 150 => new Str('3'), - 155 => new Str('41х38х15 см'), - 156 => new Str('39х38х15 см'), - ]; - - $this->assertSame( - [ - // 1 - 105 => $items[105], - 137 => $items[137], - 110 => $items[110], - 113 => $items[113], - // 2 - 132 => $items[132], - 106 => $items[106], - 150 => $items[150], - 149 => $items[149], - 133 => $items[133], - 130 => $items[130], - 136 => $items[136], - 139 => $items[139], - // 3 - 118 => $items[118], - 116 => $items[116], - // 4 - 156 => $items[156], - 155 => $items[155], - // 5 - 104 => $items[104], - 131 => $items[131], - ], - SizeSorter::items($items)->sort()->toArray() - ); - } - - public function testInteger(): void - { - $items = [ - 106 => 2, - 132 => 1, - 133 => 30, - 149 => 21, - 150 => 3, - ]; - - $this->assertSame( - [ - // 2 - 132 => 1, - 106 => 2, - 150 => 3, - 149 => 21, - 133 => 30, - ], - SizeSorter::items($items)->sort()->toArray() - ); - } - - public function testFloat(): void - { - $items = [ - 106 => 2.2, - 132 => 1.3, - 133 => 30.5, - 149 => 21.8, - 150 => 3.0, - ]; - - $this->assertSame( - [ - // 2 - 132 => 1.3, - 106 => 2.2, - 150 => 3.0, - 149 => 21.8, - 133 => 30.5, - ], - SizeSorter::items($items)->sort()->toArray() - ); - } - - public function testEnumString(): void - { - $items = [ - 104 => StringValue::VALUE_ONE_SIZE, - 105 => StringValue::VALUE_XXS, - 106 => StringValue::VALUE_2, - 110 => StringValue::VALUE_M, - 113 => StringValue::VALUE_XL_2XL, - 116 => StringValue::VALUE_80B, - 118 => StringValue::VALUE_70B, - 130 => StringValue::VALUE_44_46, - 131 => StringValue::VALUE_SOME, - 132 => StringValue::VALUE_1, - 133 => StringValue::VALUE_30, - 137 => StringValue::VALUE_XXS_XS, - 139 => StringValue::VALUE_52_56, - 149 => StringValue::VALUE_21, - 150 => StringValue::VALUE_3, - 155 => StringValue::VALUE_41_38_15, - 156 => StringValue::VALUE_39_38_15, - ]; - - $this->assertSame( - [ - // 1 - 105 => StringValue::VALUE_XXS, - 137 => StringValue::VALUE_XXS_XS, - 110 => StringValue::VALUE_M, - 113 => StringValue::VALUE_XL_2XL, - // 2 - 132 => StringValue::VALUE_1, - 106 => StringValue::VALUE_2, - 150 => StringValue::VALUE_3, - 149 => StringValue::VALUE_21, - 133 => StringValue::VALUE_30, - 130 => StringValue::VALUE_44_46, - 139 => StringValue::VALUE_52_56, - // 3 - 118 => StringValue::VALUE_70B, - 116 => StringValue::VALUE_80B, - // 4 - 156 => StringValue::VALUE_39_38_15, - 155 => StringValue::VALUE_41_38_15, - // 5 - 104 => StringValue::VALUE_ONE_SIZE, - 131 => StringValue::VALUE_SOME, - ], - SizeSorter::items($items)->sort()->toArray() - ); - } - - public function testEnumInteger(): void - { - $items = [ - 106 => IntegerValue::VALUE_2, - 132 => IntegerValue::VALUE_1, - 133 => IntegerValue::VALUE_30, - 149 => IntegerValue::VALUE_21, - 150 => IntegerValue::VALUE_3, - ]; - - $this->assertSame( - [ - // 2 - 132 => IntegerValue::VALUE_1, - 106 => IntegerValue::VALUE_2, - 150 => IntegerValue::VALUE_3, - 149 => IntegerValue::VALUE_21, - 133 => IntegerValue::VALUE_30, - ], - SizeSorter::items($items)->sort()->toArray() - ); - } - - public function testNestedArray(): void - { - $items = [ - 104 => ['foo' => 'Foo', 'bar' => ['some' => ['nested' => 'ONE SIZE']]], - 105 => ['foo' => 'Foo', 'bar' => ['some' => ['nested' => 'XXS']]], - 106 => ['foo' => 'Foo', 'bar' => ['some' => ['nested' => '2']]], - 110 => ['foo' => 'Foo', 'bar' => ['some' => ['nested' => 'M']]], - 113 => ['foo' => 'Foo', 'bar' => ['some' => ['nested' => 'XL/2XL']]], - 116 => ['foo' => 'Foo', 'bar' => ['some' => ['nested' => '80B']]], - 118 => ['foo' => 'Foo', 'bar' => ['some' => ['nested' => '70B']]], - 130 => ['foo' => 'Foo', 'bar' => ['some' => ['nested' => '44-46']]], - 131 => ['foo' => 'Foo', 'bar' => ['some' => ['nested' => 'some']]], - 132 => ['foo' => 'Foo', 'bar' => ['some' => ['nested' => '1']]], - 133 => ['foo' => 'Foo', 'bar' => ['some' => ['nested' => '30']]], - 136 => ['foo' => 'Foo', 'bar' => ['some' => ['nested' => '44/46']]], - 137 => ['foo' => 'Foo', 'bar' => ['some' => ['nested' => 'XXS/XS']]], - 139 => ['foo' => 'Foo', 'bar' => ['some' => ['nested' => '52-56']]], - 149 => ['foo' => 'Foo', 'bar' => ['some' => ['nested' => '21']]], - 150 => ['foo' => 'Foo', 'bar' => ['some' => ['nested' => '3']]], - 155 => ['foo' => 'Foo', 'bar' => ['some' => ['nested' => '41х38х15 см']]], - 156 => ['foo' => 'Foo', 'bar' => ['some' => ['nested' => '39х38х15 см']]], - ]; - - $this->assertSame( - [ - // 1 - 105 => ['foo' => 'Foo', 'bar' => ['some' => ['nested' => 'XXS']]], - 137 => ['foo' => 'Foo', 'bar' => ['some' => ['nested' => 'XXS/XS']]], - 110 => ['foo' => 'Foo', 'bar' => ['some' => ['nested' => 'M']]], - 113 => ['foo' => 'Foo', 'bar' => ['some' => ['nested' => 'XL/2XL']]], - // 2 - 132 => ['foo' => 'Foo', 'bar' => ['some' => ['nested' => '1']]], - 106 => ['foo' => 'Foo', 'bar' => ['some' => ['nested' => '2']]], - 150 => ['foo' => 'Foo', 'bar' => ['some' => ['nested' => '3']]], - 149 => ['foo' => 'Foo', 'bar' => ['some' => ['nested' => '21']]], - 133 => ['foo' => 'Foo', 'bar' => ['some' => ['nested' => '30']]], - 130 => ['foo' => 'Foo', 'bar' => ['some' => ['nested' => '44-46']]], - 136 => ['foo' => 'Foo', 'bar' => ['some' => ['nested' => '44/46']]], - 139 => ['foo' => 'Foo', 'bar' => ['some' => ['nested' => '52-56']]], - // 3 - 118 => ['foo' => 'Foo', 'bar' => ['some' => ['nested' => '70B']]], - 116 => ['foo' => 'Foo', 'bar' => ['some' => ['nested' => '80B']]], - // 4 - 156 => ['foo' => 'Foo', 'bar' => ['some' => ['nested' => '39х38х15 см']]], - 155 => ['foo' => 'Foo', 'bar' => ['some' => ['nested' => '41х38х15 см']]], - // 5 - 104 => ['foo' => 'Foo', 'bar' => ['some' => ['nested' => 'ONE SIZE']]], - 131 => ['foo' => 'Foo', 'bar' => ['some' => ['nested' => 'some']]], - ], - SizeSorter::items($items)->column('bar.some.nested')->sort()->toArray() - ); - } - - public function testLaravelModels(): void - { - $items = Collection::make([ - 104 => 'ONE SIZE', - 105 => 'XXS', - 106 => '2', - 110 => 'M', - 113 => 'XL/2XL', - 116 => '80B', - 118 => '70B', - 130 => '44-46', - 131 => 'some', - 132 => '1', - 133 => '30', - 136 => '44/46', - 137 => 'XXS/XS', - 139 => '52-56', - 149 => '21', - 150 => '3', - 155 => '41х38х15 см', - 156 => '39х38х15 см', - ])->map(function (string $value, int $id) { - return new Model(compact('id', 'value')); - }); - - $this->assertSame( - [ - // 1 - 105 => 'XXS', - 137 => 'XXS/XS', - 110 => 'M', - 113 => 'XL/2XL', - // 2 - 132 => '1', - 106 => '2', - 150 => '3', - 149 => '21', - 133 => '30', - 130 => '44-46', - 136 => '44/46', - 139 => '52-56', - // 3 - 118 => '70B', - 116 => '80B', - // 4 - 156 => '39х38х15 см', - 155 => '41х38х15 см', - // 5 - 104 => 'ONE SIZE', - 131 => 'some', - ], - SizeSorter::items($items)->sort()->pluck('value', 'id')->toArray() - ); - } - - public function testCollection(): void - { - $items = collect([ - 104 => 'ONE SIZE', - 105 => 'XXS', - 106 => '2', - 110 => 'M', - 113 => 'XL/2XL', - 116 => '80B', - 118 => '70B', - 130 => '44-46', - 131 => 'some', - 132 => '1', - 133 => '30', - 136 => '44/46', - 137 => 'XXS/XS', - 139 => '52-56', - 149 => '21', - 150 => '3', - 155 => '41х38х15 см', - 156 => '39х38х15 см', - ]); - - $this->assertSame( - [ - // 1 - 105 => 'XXS', - 137 => 'XXS/XS', - 110 => 'M', - 113 => 'XL/2XL', - // 2 - 132 => '1', - 106 => '2', - 150 => '3', - 149 => '21', - 133 => '30', - 130 => '44-46', - 136 => '44/46', - 139 => '52-56', - // 3 - 118 => '70B', - 116 => '80B', - // 4 - 156 => '39х38х15 см', - 155 => '41х38х15 см', - // 5 - 104 => 'ONE SIZE', - 131 => 'some', - ], - SizeSorter::items($items)->sort()->toArray() - ); - } -} diff --git a/tests/TestCase.php b/tests/TestCase.php index 0358aa8..4f480f0 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -66,5 +66,8 @@ abstract class TestCase extends BaseTestCase 154 => '40х38х15 см', 155 => '41х38х15 см', 156 => '39х38х15 см', + 157 => '3xl', + 158 => '2xl', + 159 => '2xs', ]; } diff --git a/tests/Unit/Detectors/BraTest.php b/tests/Unit/Detectors/BraTest.php new file mode 100644 index 0000000..6cf6145 --- /dev/null +++ b/tests/Unit/Detectors/BraTest.php @@ -0,0 +1,41 @@ +detector::detect( + $this->normalize($value) + ); + + $this->assertSame($expected, $actual); + } + + abstract public static function valuesData(): array; + + protected function normalize(int|string $value): string + { + return KeyNormalizer::normalize($value); + } +} diff --git a/tests/Unit/Detectors/LetterClothingTest.php b/tests/Unit/Detectors/LetterClothingTest.php new file mode 100644 index 0000000..4bdcbca --- /dev/null +++ b/tests/Unit/Detectors/LetterClothingTest.php @@ -0,0 +1,77 @@ +assertSame($expected, KeyNormalizer::normalize($input)); + } + + public static function valuesData(): array + { + return [ + [ + 'expected' => 'foo', + 'input' => 'foo', + ], + [ + 'expected' => 'foo', + 'input' => 'FOO', + ], + [ + 'expected' => 'foo', + 'input' => 'FoO', + ], + [ + 'expected' => '123', + 'input' => 123, + ], + [ + 'expected' => '123.45', + 'input' => 123.45, + ], + [ + 'expected' => 'true', + 'input' => true, + ], + [ + 'expected' => 'false', + 'input' => false, + ], + [ + 'expected' => 'null', + 'input' => null, + ], + [ + 'expected' => 'foo_bar', + 'input' => 'foo bar', + ], + [ + 'expected' => 'foo_bar', + 'input' => 'foo-bar', + ], + [ + 'expected' => 'foo_bar', + 'input' => 'foo_bar', + ], + [ + 'expected' => 'foo_bar', + 'input' => 'foo/bar', + ], + [ + 'expected' => 'foo_bar', + 'input' => 'foo\\bar', + ], + [ + 'expected' => 'foo_bar', + 'input' => 'foo+bar', + ], + [ + 'expected' => 'foo_bar', + 'input' => 'foo=bar', + ], + [ + 'expected' => 'foo_bar', + 'input' => 'foo(bar', + ], + [ + 'expected' => 'foo_bar', + 'input' => 'foo)bar', + ], + [ + 'expected' => 'foo_bar', + 'input' => 'foo*bar', + ], + [ + 'expected' => 'foo_bar_123', + 'input' => 'foo bar 123', + ], + [ + 'expected' => '40x38x19_sm', + 'input' => '40х38х19 sm', + ], + [ + 'expected' => '40x38x15_sm', + 'input' => '40х38х15 см', + ], + ]; + } +} diff --git a/tests/Sorters/SortTest.php b/tests/Unit/SorterTest.php similarity index 72% rename from tests/Sorters/SortTest.php rename to tests/Unit/SorterTest.php index 02fb37f..91d1f26 100644 --- a/tests/Sorters/SortTest.php +++ b/tests/Unit/SorterTest.php @@ -2,107 +2,49 @@ declare(strict_types=1); -namespace Tests\Sorters; +namespace Tests\Unit; -use DragonCode\SizeSorter\Enum\Group; +use DragonCode\SizeSorter\Enums\GroupEnum; use DragonCode\SizeSorter\SizeSorter; use PHPUnit\Framework\Attributes\DataProvider; use Tests\TestCase; -class SortTest extends TestCase +class SorterTest extends TestCase { - #[DataProvider('sorterData')] - public function testArray(?array $order, array $expected): void + #[DataProvider('sortData')] + public function testDefault(array $expected, ?array $groups = null): void { $actual = SizeSorter::items($this->values) - ->orderBy($order) + ->orderBy($groups) ->sort() - ->toArray(); + ->all(); $this->assertSame($expected, $actual); } - #[DataProvider('sorterData')] - public function testObjects(?array $order, array $expected): void - { - $items = collect($this->values)->map(fn (mixed $value, int $key) => (object) [ - 'id' => $key, - 'value' => $value, - 'active' => true, - ]); - - $actual = SizeSorter::items($items) - ->orderBy($order) - ->sort() - ->pluck('value', 'id') - ->toArray(); - - $this->assertSame($expected, $actual); - } - - #[DataProvider('sorterData')] - public function testCustomColumn(?array $order, array $expected): void - { - $items = collect($this->values)->map(fn (mixed $value, int $key) => (object) [ - 'id' => $key, - 'some' => $value, - ]); - - $actual = SizeSorter::items($items) - ->orderBy($order) - ->column('some') - ->sort() - ->pluck('some', 'id') - ->toArray(); - - $this->assertSame($expected, $actual); - } - - #[DataProvider('sorterData')] - public function testWithSaveKeys(?array $order, array $expected): void - { - $values = [ - 840 => 'XL', - 506 => 'XS', - ]; - - $expected = [ - 506 => 'XS', - 840 => 'XL', - ]; - - $actual = SizeSorter::items($values) - ->orderBy($order) - ->sort() - ->toArray(); - - $this->assertSame($expected, $actual); - } - - public static function sorterData(): array + public static function sortData(): array { return [ - 'simple' => [ - 'order' => null, - + 'default' => [ 'expected' => [ - // 1 - 105 => 'XXS', - 138 => 'XXS', - 137 => 'XXS/XS', - 122 => 'XXS-XS', 108 => 'XS', - 114 => 'XS/S', 109 => 'S', + 114 => 'XS/S', 127 => 'S/M', + 105 => 'XXS', + 122 => 'XXS-XS', + 137 => 'XXS/XS', + 138 => 'XXS', + 159 => '2xs', 110 => 'M', 115 => 'M/L', + 103 => 'XL', 111 => 'L', 112 => 'L/XL', - 103 => 'XL', 113 => 'XL/2XL', 100 => 'XXL', - // 2 + 158 => '2xl', + 157 => '3xl', 132 => '1', 106 => '2', 150 => '3', @@ -130,56 +72,57 @@ public static function sorterData(): array 143 => '106', 145 => '110-112', 144 => '110-114', - // 3 + 116 => '80B', + 117 => '75C', 118 => '70B', - 129 => '70C', 119 => '75A', 120 => '75B', - 117 => '75C', - 116 => '80B', - // 4 - 156 => '39х38х15 см', + 129 => '70C', + 128 => '40х38х19 см', + 151 => '40х38х19 sm', 152 => '40х37х19 см', 153 => '40х37х20 см', 154 => '40х38х15 см', - 151 => '40х38х19 sm', - 128 => '40х38х19 см', 155 => '41х38х15 см', - // 5 + 156 => '39х38х15 см', 104 => 'ONE SIZE', 131 => 'some', ], ], - 'full groups' => [ - 'order' => [ - Group::BraSize, - Group::OtherSizes, - Group::OverallDimensions, - Group::ClothesAndShoes, - Group::LetterClothingSize, + 'partial groups' => [ + 'groups' => [ + GroupEnum::OtherSizes, + GroupEnum::BraSize, ], 'expected' => [ - // 3 + 104 => 'ONE SIZE', + 131 => 'some', + 116 => '80B', + 117 => '75C', 118 => '70B', - 129 => '70C', 119 => '75A', 120 => '75B', - 117 => '75C', - 116 => '80B', - // 5 - 104 => 'ONE SIZE', - 131 => 'some', - // 4 - 156 => '39х38х15 см', - 152 => '40х37х19 см', - 153 => '40х37х20 см', - 154 => '40х38х15 см', - 151 => '40х38х19 sm', - 128 => '40х38х19 см', - 155 => '41х38х15 см', - // 2 + 129 => '70C', + 108 => 'XS', + 109 => 'S', + 114 => 'XS/S', + 127 => 'S/M', + 105 => 'XXS', + 122 => 'XXS-XS', + 137 => 'XXS/XS', + 138 => 'XXS', + 159 => '2xs', + 110 => 'M', + 115 => 'M/L', + 103 => 'XL', + 111 => 'L', + 112 => 'L/XL', + 113 => 'XL/2XL', + 100 => 'XXL', + 158 => '2xl', + 157 => '3xl', 132 => '1', 106 => '2', 150 => '3', @@ -207,59 +150,59 @@ public static function sorterData(): array 143 => '106', 145 => '110-112', 144 => '110-114', - // 1 - 105 => 'XXS', - 138 => 'XXS', - 137 => 'XXS/XS', - 122 => 'XXS-XS', - 108 => 'XS', - 114 => 'XS/S', - 109 => 'S', - 127 => 'S/M', - 110 => 'M', - 115 => 'M/L', - 111 => 'L', - 112 => 'L/XL', - 103 => 'XL', - 113 => 'XL/2XL', - 100 => 'XXL', + 128 => '40х38х19 см', + 151 => '40х38х19 sm', + 152 => '40х37х19 см', + 153 => '40х37х20 см', + 154 => '40х38х15 см', + 155 => '41х38х15 см', + 156 => '39х38х15 см', ], ], - 'partial groups' => [ - 'order' => [ - Group::BraSize, - Group::OtherSizes, + 'full groups' => [ + 'groups' => [ + GroupEnum::OtherSizes, + GroupEnum::BraSize, + GroupEnum::OverallDimensions, + GroupEnum::LetterClothingSize, + GroupEnum::ClothesAndShoes, ], 'expected' => [ - // 3 + 104 => 'ONE SIZE', + 131 => 'some', + 116 => '80B', + 117 => '75C', 118 => '70B', - 129 => '70C', 119 => '75A', 120 => '75B', - 117 => '75C', - 116 => '80B', - // 5 - 104 => 'ONE SIZE', - 131 => 'some', - // 1 - 105 => 'XXS', - 138 => 'XXS', - 137 => 'XXS/XS', - 122 => 'XXS-XS', + 129 => '70C', + 128 => '40х38х19 см', + 151 => '40х38х19 sm', + 152 => '40х37х19 см', + 153 => '40х37х20 см', + 154 => '40х38х15 см', + 155 => '41х38х15 см', + 156 => '39х38х15 см', 108 => 'XS', - 114 => 'XS/S', 109 => 'S', + 114 => 'XS/S', 127 => 'S/M', + 105 => 'XXS', + 122 => 'XXS-XS', + 137 => 'XXS/XS', + 138 => 'XXS', + 159 => '2xs', 110 => 'M', 115 => 'M/L', + 103 => 'XL', 111 => 'L', 112 => 'L/XL', - 103 => 'XL', 113 => 'XL/2XL', 100 => 'XXL', - // 2 + 158 => '2xl', + 157 => '3xl', 132 => '1', 106 => '2', 150 => '3', @@ -287,14 +230,6 @@ public static function sorterData(): array 143 => '106', 145 => '110-112', 144 => '110-114', - // 4 - 156 => '39х38х15 см', - 152 => '40х37х19 см', - 153 => '40х37х20 см', - 154 => '40х38х15 см', - 151 => '40х38х19 sm', - 128 => '40х38х19 см', - 155 => '41х38х15 см', ], ], ]; diff --git a/tests/Unit/Support/GroupOrder/GetTest.php b/tests/Unit/Support/GroupOrder/GetTest.php new file mode 100644 index 0000000..43dca32 --- /dev/null +++ b/tests/Unit/Support/GroupOrder/GetTest.php @@ -0,0 +1,67 @@ +assertSame($expected, GroupOrder::get($input)); + } + + public static function groupsData(): array + { + return [ + 'empty' => [ + 'expected' => GroupEnum::sorted(), + 'input' => null, + ], + + 'full' => [ + 'expected' => GroupEnum::sorted(), + 'input' => GroupEnum::sorted(), + ], + + 'partial' => [ + 'expected' => [ + GroupEnum::OtherSizes, + GroupEnum::BraSize, + GroupEnum::LetterClothingSize, + GroupEnum::ClothesAndShoes, + GroupEnum::OverallDimensions, + ], + + 'input' => [ + GroupEnum::OtherSizes, + GroupEnum::BraSize, + ], + ], + + 'full random' => [ + 'expected' => [ + GroupEnum::OtherSizes, + GroupEnum::BraSize, + GroupEnum::ClothesAndShoes, + GroupEnum::OverallDimensions, + GroupEnum::LetterClothingSize, + ], + + 'input' => [ + GroupEnum::OtherSizes, + GroupEnum::BraSize, + GroupEnum::ClothesAndShoes, + GroupEnum::OverallDimensions, + GroupEnum::LetterClothingSize, + ], + ], + ]; + } +} diff --git a/tests/Unit/Support/Map/ApplyTest.php b/tests/Unit/Support/Map/ApplyTest.php new file mode 100644 index 0000000..55a93f7 --- /dev/null +++ b/tests/Unit/Support/Map/ApplyTest.php @@ -0,0 +1,124 @@ + 2, + 'foo' => 0, + 'bar' => 1, + ]; + + public function testString(): void + { + $input = collect([ + 'foo', + 'bar', + 'baz', + ]); + + $this->assertSame([ + 2 => 'baz', + 0 => 'foo', + 1 => 'bar', + ], $this->applyMap($input)); + } + + public function testStringable(): void + { + $obj1 = new Stringable('foo'); + $obj2 = new Stringable('bar'); + $obj3 = new Stringable('baz'); + + $input = collect([ + $obj1, + $obj2, + $obj3, + ]); + + $this->assertSame([ + 2 => $obj3, + 0 => $obj1, + 1 => $obj2, + ], $this->applyMap($input)); + } + + public function testNumber(): void + { + $input = collect([ + 123, + 234, + 345, + ]); + + $this->assertSame([ + 2 => 345, + 0 => 123, + 1 => 234, + ], $this->applyMap($input)); + } + + public function testObject(): void + { + $obj1 = (object) ['value' => 'foo']; + $obj2 = (object) ['value' => 'bar']; + $obj3 = (object) ['value' => 'baz']; + + $input = collect([ + $obj1, + $obj2, + $obj3, + ]); + + $this->assertSame([ + 2 => $obj3, + 0 => $obj1, + 1 => $obj2, + ], $this->applyMap($input)); + } + + public function testLaravelModel(): void + { + $obj1 = new LaravelModel(['id' => 1, 'value' => 'foo']); + $obj2 = new LaravelModel(['id' => 2, 'value' => 'bar']); + $obj3 = new LaravelModel(['id' => 3, 'value' => 'baz']); + + $input = collect([ + $obj1, + $obj2, + $obj3, + ]); + + $this->assertSame([ + 2 => $obj3, + 0 => $obj1, + 1 => $obj2, + ], $this->applyMap($input)); + } + + public function testMissingCount(): void + { + $this->expectException(LengthException::class); + $this->expectExceptionMessage('The count of items in the map (2) and collection (3) should be the same.'); + + Map::apply( + collect([1, 2, 3]), + collect([1, 2]) + ); + } + + protected function applyMap(Collection $items): array + { + return Map::apply($items, collect($this->map))->all(); + } +} diff --git a/tests/Unit/Support/Map/MakeTest.php b/tests/Unit/Support/Map/MakeTest.php new file mode 100644 index 0000000..01a3896 --- /dev/null +++ b/tests/Unit/Support/Map/MakeTest.php @@ -0,0 +1,145 @@ + 0, + 'bar' => 1, + 'baz' => 2, + ]; + + public function testString(): void + { + $input = collect([ + 'foo', + 'bar', + 'baz', + ]); + + $this->assertSame([ + $this->prefix('foo', '5::0') => 0, + $this->prefix('bar', '5::1') => 1, + $this->prefix('baz', '5::2') => 2, + ], $this->makeMap($input)); + } + + public function testLongString(): void + { + $input = collect([ + 'foo QWE', + 'bar/RTY', + 'baz-asd', + ]); + + $this->assertSame([ + $this->prefix('foo_qwe', '5::0') => 0, + $this->prefix('bar_rty', '5::1') => 1, + $this->prefix('baz_asd', '5::2') => 2, + ], $this->makeMap($input)); + } + + public function testDuplicates(): void + { + $input = collect([ + 'foo', + 'bar', + 'baz', + 'foo', + ]); + + $this->assertSame([ + $this->prefix('foo', '5::0') => 0, + $this->prefix('bar', '5::1') => 1, + $this->prefix('baz', '5::2') => 2, + $this->prefix('foo', '5::3') => 3, + ], $this->makeMap($input)); + } + + public function testStringable(): void + { + $input = collect([ + new Stringable('foo'), + new Stringable('bar'), + new Stringable('baz'), + ]); + + $this->assertSame([ + $this->prefix('foo', '5::0') => 0, + $this->prefix('bar', '5::1') => 1, + $this->prefix('baz', '5::2') => 2, + ], $this->makeMap($input)); + } + + public function testNumber(): void + { + $input = collect([ + 123, + 234, + 345, + ]); + + $this->assertSame([ + $this->prefix('123', '2::0') => 0, + $this->prefix('234', '2::1') => 1, + $this->prefix('345', '2::2') => 2, + ], $this->makeMap($input)); + } + + public function testObject(): void + { + $column = static fn (object $item) => $item->value; + + $input = collect([ + (object) ['value' => 'foo'], + (object) ['value' => 'bar'], + (object) ['value' => 'baz'], + ]); + + $this->assertSame([ + $this->prefix('foo', '5::0') => 0, + $this->prefix('bar', '5::1') => 1, + $this->prefix('baz', '5::2') => 2, + ], $this->makeMap($input, $column)); + } + + public function testLaravelModel(): void + { + $column = static fn (object $item) => $item->value; + + $input = collect([ + new LaravelModel(['id' => 1, 'value' => 'foo']), + new LaravelModel(['id' => 2, 'value' => 'bar']), + new LaravelModel(['id' => 3, 'value' => 'baz']), + ]); + + $this->assertSame([ + $this->prefix('foo', '5::0') => 0, + $this->prefix('bar', '5::1') => 1, + $this->prefix('baz', '5::2') => 2, + ], $this->makeMap($input, $column)); + } + + protected function makeMap(Collection $items, ?Closure $column = null): array + { + $column ??= static fn (int|string|Stringable $value) => $value; + + return Map::make($items, $column)->all(); + } + + protected function prefix(string $key, int|string $prefix): string + { + return $prefix . Group::Delimiter . $key; + } +} diff --git a/tests/Unit/Support/Resolve/ValueTest.php b/tests/Unit/Support/Resolve/ValueTest.php new file mode 100644 index 0000000..31d95db --- /dev/null +++ b/tests/Unit/Support/Resolve/ValueTest.php @@ -0,0 +1,82 @@ +assertSame($expected, Resolve::value($input, 'value')); + } + + public static function valuesData(): array + { + return [ + 'string' => [ + 'expected' => 'foo', + 'input' => 'foo', + ], + + 'integer' => [ + 'expected' => 123, + 'input' => 123, + ], + + 'float' => [ + 'expected' => 123.45, + 'input' => 123.45, + ], + + 'bool true' => [ + 'expected' => true, + 'input' => true, + ], + + 'bool false' => [ + 'expected' => false, + 'input' => false, + ], + + 'integer enum' => [ + 'expected' => 30, + 'input' => IntegerEnum::Value30, + ], + + 'string enum' => [ + 'expected' => '39х38х15 см', + 'input' => StringEnum::Value39_38_15, + ], + + 'unit enum' => [ + 'expected' => 'Xxxl', + 'input' => UnitEnum::Xxxl, + ], + + 'array' => [ + 'expected' => 'some', + 'input' => ['value' => 'some'], + ], + + 'object' => [ + 'expected' => 'some', + 'input' => (object) ['value' => 'some'], + ], + + 'stringable' => [ + 'expected' => 'some', + 'input' => new Stringable('some'), + ], + ]; + } +} diff --git a/tests/Unit/Support/Validator/EnsureTest.php b/tests/Unit/Support/Validator/EnsureTest.php new file mode 100644 index 0000000..3bd4dc4 --- /dev/null +++ b/tests/Unit/Support/Validator/EnsureTest.php @@ -0,0 +1,37 @@ + 'foo'], + (object) ['value' => 'bar'], + ], stdClass::class); + + $this->assertTrue(true); + } + + public function testWrong(): void + { + $this->expectException(UnexpectedValueException::class); + + $this->expectExceptionMessage( + "Collection should only include [stdClass] items, but 'string' found at position 1." + ); + + Validator::ensure([ + (object) ['value' => 'foo'], + 'bar', + ], stdClass::class); + } +} diff --git a/tests/Unit/Support/Validator/MapCountTest.php b/tests/Unit/Support/Validator/MapCountTest.php new file mode 100644 index 0000000..3f6c6b2 --- /dev/null +++ b/tests/Unit/Support/Validator/MapCountTest.php @@ -0,0 +1,35 @@ +assertTrue(true); + } + + public function testWrong(): void + { + $this->expectException(LengthException::class); + + $this->expectExceptionMessage( + 'The count of items in the map (2) and collection (3) should be the same.' + ); + + Validator::mapCount( + collect([1, 2, 3]), + collect([1, 2]), + ); + } +}