stratadox/sorting

v0.3.1 2019-11-27 19:40 UTC

This package is auto-updated.

Last update: 2024-09-28 06:32:57 UTC


README

Build Status Coverage Status Scrutinizer Code Quality

排序包包含用于对表格或对象集合等数据结构进行排序的工具。

排序指令是对象,因此可以作为参数轻松传递。可以通过链式调用多个指令以按多个字段进行排序。

安装

使用composer安装

composer require stratadox/sorting

基本用法

二维数组

当对一个关联数组集合进行排序时,使用

<?php
use Stratadox\Sorting\ArraySorter;
use Stratadox\Sorting\Sort;

$table = [
    ['name' => 'Foo', 'rating' => 3],
    ['name' => 'Bar', 'rating' => 1],
    ['name' => 'Baz', 'rating' => 2],
];
$sorter = new ArraySorter();

$table = $sorter->sort($table, Sort::descendingBy('rating'));

assert($table === [
    ['name' => 'Foo', 'rating' => 3],
    ['name' => 'Baz', 'rating' => 2],
    ['name' => 'Bar', 'rating' => 1],
]);

对象

如果集合由对象组成,排序定义保持不变。在这种情况下,使用ObjectSorter而不是ArraySorter来获取排序需求。

<?php
use Stratadox\Sorting\ObjectSorter;
use Stratadox\Sorting\Sort;

class SomeObject
{
    private $name;
    private $rating;

    public function __construct(string $name, int $rating)
    {
        $this->name = $name;
        $this->rating = $rating;
    }

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

    public function rating(): int
    {
        return $this->rating;
    }
}

$objects = [
    new SomeObject('Foo', 3),
    new SomeObject('Bar', 1),
    new SomeObject('Baz', 2),
];
$sorter = new ObjectSorter();

$objects = $sorter->sort($objects, Sort::ascendingBy('rating'));

assert($objects == [
    new SomeObject('Bar', 1),
    new SomeObject('Baz', 2),
    new SomeObject('Foo', 3),
]);

方法映射

当方法名称与字段名称不匹配,或需要使用自定义方法来确定排序权重时,可以给ObjectSorter提供一个映射。

<?php
use Stratadox\Sorting\ObjectSorter;
use Stratadox\Sorting\Sort;

class SomeObject
{
    private $name;
    private $rating;

    public function __construct(string $name, int $rating)
    {
        $this->name = $name;
        $this->rating = $rating;
    }

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

    public function getTheRating(): int
    {
        return $this->rating;
    }
}

$objects = [
    new SomeObject('Foo', 3),
    new SomeObject('Bar', 1),
    new SomeObject('Baz', 2),
];
$sorter = new ObjectSorter([
    'name' => 'getName',
    'rating' => 'getTheRating',
]);

$objects = $sorter->sort($objects, Sort::ascendingBy('rating'));

assert($objects == [
    new SomeObject('Bar', 1),
    new SomeObject('Baz', 2),
    new SomeObject('Foo', 3),
]);

嵌套数组

如果要排序的结构由嵌套数组列表组成,可以使用NestedArraySorter。

<?php
use Stratadox\Sorting\NestedArraySorter;
use Stratadox\Sorting\Sort;

$unsorted = [
    ['result' => ['index' => 3, 'label' => 'bar']],
    ['result' => ['index' => 2, 'label' => 'qux']],
    ['result' => ['index' => 1, 'label' => 'baz']],
    ['result' => ['index' => 2, 'label' => 'foo']],
];
$sorter = new NestedArraySorter();
$result = $sorter->sort($unsorted, Sort::ascendingBy('result.index'));

assert($result === [
   ['result' => ['index' => 1, 'label' => 'baz']],
   ['result' => ['index' => 2, 'label' => 'qux']],
   ['result' => ['index' => 2, 'label' => 'foo']],
   ['result' => ['index' => 3, 'label' => 'bar']],
]);

按多个字段排序

<?php
use Stratadox\Sorting\ArraySorter;
use Stratadox\Sorting\Sort;

$table = [
    ['name' => 'Bar', 'rating' => 1],
    ['name' => 'Foo', 'rating' => 3],
    ['name' => 'Baz', 'rating' => 1],
];
$sorter = new ArraySorter();
$table = $sorter->sort($table, 
    Sort::descendingBy('rating', Sort::descendingBy('name'))
);

assert($table == [
    ['name' => 'Foo', 'rating' => 3],
    ['name' => 'Baz', 'rating' => 1],
    ['name' => 'Bar', 'rating' => 1],
]);

或者

<?php
use Stratadox\Sorting\ArraySorter;
use Stratadox\Sorting\Sort;


$table = [
    ['name' => 'Bar', 'rating' => 1],
    ['name' => 'Foo', 'rating' => 3],
    ['name' => 'Baz', 'rating' => 1],
];
$sorter = new ArraySorter();
$table = $sorter->sort($table, 
    Sort::descendingBy('rating')->andThenDescendingBy('name')
);

assert($table == [
    ['name' => 'Foo', 'rating' => 3],
    ['name' => 'Baz', 'rating' => 1],
    ['name' => 'Bar', 'rating' => 1],
]);

或者

<?php
use Stratadox\Sorting\ArraySorter;
use Stratadox\Sorting\Sorted;


$table = [
    ['name' => 'Bar', 'rating' => 1],
    ['name' => 'Foo', 'rating' => 3],
    ['name' => 'Baz', 'rating' => 1],
];
$sorter = new ArraySorter();
$table = $sorter->sort($table, Sorted::by([
    'rating' => false,
    'name' => false,
]));

assert($table == [
    ['name' => 'Foo', 'rating' => 3],
    ['name' => 'Baz', 'rating' => 1],
    ['name' => 'Bar', 'rating' => 1],
]);

结构

排序包包含两个核心概念

  • 排序定义,用于声明排序需求,
  • 排序器,根据给定定义对元素进行排序。

保持这种分离是为了将排序意图与被排序的数据结构解耦。

例如,排序定义 Sort::ascendingBy('name') 可以应用于关联数组集合或对象集合 - 请求排序的客户端不需要知道要排序的元素内部结构。

在设计允许排序的接口时,一个方法可能接受一个排序定义,例如 public function getSorted(Sorting $definition): array。此接口的实现者可以使用他们认为合适的任何排序器,只要他们产生预期的结果。

某些实现甚至可能不使用任何排序器,而是直接使用排序定义以不同的方式生成排序后的结果列表。此类用法的示例可能是一个具有内存和SQL实现的存储库。

<?php
use Stratadox\Sorting\Contracts\Sorting;
use Stratadox\Sorting\ObjectSorter;
use Stratadox\Sorting\OrderByParser;

class SomeEntity
{
    // ...
}

interface SomeRepository
{
    /** @return SomeEntity[] */
    public function all(Sorting $sorting): array;
}

class SomeInMemoryRepository implements SomeRepository
{
    private $sorter;
    private $entities;

    public function __construct(SomeEntity ...$entities)
    {
        $this->sorter = new ObjectSorter();
        $this->entities = $entities;
    }

    public function all(Sorting $sorting): array
    {
        return $this->sorter->sort($this->entities, $sorting);
    }
}

class SomeDatabaseRepository implements SomeRepository
{
    private $connection;
    private $orderByParser;
    private $deserializer;

    public function __construct(PDO $connection, DeserializesObjects $deserializer)
    {
        $this->connection = $connection;
        $this->orderByParser = OrderByParser::allowing('foo', 'bar');
        $this->deserializer = $deserializer;
    }

    public function all(Sorting $sorting): array
    {
        $query = $this->connection->query(
            'SELECT * FROM some_table ' . $this->orderByParser->parse($sorting)
        );
        $entities = [];
        foreach ($query->fetchAll(PDO::FETCH_ASSOC) as $row) {
            $entities[] = $this->deserializer->from($row);
        }
        return $entities;
    }
}