PHP - 如何使用每个元素上定义的getter来排序对象数组?

时间:2017-10-28 19:03:34

标签: php arrays sorting php-7 usort

我们如何通过指定的getter的返回值来命令对象数组?

例如,我们有一个对象数组。每个对象都有一个'getCreatedAt'函数,它返回一个DateTime对象。我们需要命令该数组按日期获取最后一个元素。

1 个答案:

答案 0 :(得分:1)

解决方案达成了内部对象管理的php特性。现在我们可以通过在我们的实体中简单地实现这个特征来比较它们之间的对象数组。这是代码。

trait ObjectManagement 
{
    private function orderObjects(array $objects, string $getter, string $ordering): array
    {
        usort(
            $objects,
            [new ObjectAscDescOrderingByGetter($getter, $ordering), 'order']
        );

        return $objects;
    }

}


class ObjectAscDescOrderingByGetter
{

    private $getter;
    private $ordering;

    public function __construct(string $getter, string $ordering)
    {
        if (!in_array($ordering, ['ASC', 'DESC'])) {
            throw new RuntimeException('Ordering type must be ASC or DESC.');
        }

        $this->getter = $getter;
        $this->ordering = $ordering;
    }

    public function order($objectA, $objectB)
    {
        $getter = $this->getter;

        $comparisonValueA = $objectA->$getter();
        $comparisonValueB = $objectB->$getter();

        if ($comparisonValueA === $comparisonValueB) {
            return 0;
        }

        if ($this->ordering == 'DESC') {
            return ($comparisonValueA > $comparisonValueB) ? -1 : 1;
        } else {
            return ($comparisonValueA > $comparisonValueB) ? 1 : -1;
        }
    }
}

我们知道在某些情况下特征不是最好的方法,但在这种情况下我们发现它非常有用,因为这个排序函数是可以在其他类中静态实现的代码。现在这是我们在实体中的用例:

class Car {

  private $inputDate;

  public function getInputDate() : DateTime 
  {
     return $this->inputDate;
  }
}

class CarPark {
   use ObjectManagement;
   private $cars;

   public function getLastCar() : Car {
     $result = $this->orderObjects($this->cars, 'getInputDate', 'DESC');
     return current($result);
   }
}

我们也离开了测试......

class ObjectManagementTestCase extends TestCase
{

    use ObjectManagement;

    /**
     * @dataProvider getOrderingCases
     * @param array $datesToOrder
     * @param array $datesExpectedOrder
     * @param string $ordering
     */
    public function testOrdering(array $datesToOrder, array $datesExpectedOrder, string $ordering)
    {
        $objectsToOrder = [];
        foreach ($datesToOrder as $dateToOrder) {
            $objectsToOrder[] = $this->getOrderableMock($dateToOrder);
        }
        $objectsExpectedOrder = [];
        foreach ($datesExpectedOrder as $expectedDateOrder) {
            $objectsExpectedOrder[] = $this->getOrderableMock($expectedDateOrder);
        }

        $result = $this->orderObjects($objectsToOrder, 'getCreatedAt', $ordering);
        $this->assertEquals($objectsExpectedOrder, $result);
    }

    public function getOrderingCases()
    {
        return [
            'Must order this in descendant manner.' => [
                ['2016-01-01', '2017-01-01', '2017-01-03', '2017-01-05'],
                ['2017-01-05', '2017-01-03', '2017-01-01', '2016-01-01'],
                'DESC'
            ],
            'Must order this in ascendant manner.' => [
                ['2016-01-01', '2017-01-03', '2017-01-01', '2017-01-05'],
                ['2016-01-01', '2017-01-01', '2017-01-03', '2017-01-05'],
                'ASC'
            ],
        ];
    }

    private function getOrderableMock(string $date)
    {
        return new OrderableTestObject($date);
    }

    public function testThatInputArrayIsNotModified()
    {
        $objects = [
            $this->getOrderableMock('2016-01-01'),
            $this->getOrderableMock('2017-01-03'),
        ];

        $this->orderObjects($objects, 'getCreatedAt', 'DESC');

        $this->assertEquals(
            [
                $this->getOrderableMock('2016-01-01'),
                $this->getOrderableMock('2017-01-03'),
            ],
            $objects
        );

    }

    public function testExceptionOnNotRecognizedOrder()
    {
        $this->expectException(RuntimeException::class);
        $this->expectExceptionMessage('Ordering type must be ASC or DESC.');
        $this->orderObjects([], 'createdAt', '');
    }
}


class OrderableTestObject
{
    private $createdAt;

    public function __construct(string $date)
    {
        $this->createdAt = new DateTime($date);
    }

    public function getCreatedAt(): DateTime
    {
        return $this->createdAt;
    }
}