Skip to the content.

Create a custom Comparator

The heart of the intl-sort library is the Comparator, which handles the comparision of values and influences the sorting of elements. A configured and ready to use PHP Intl Collator is injected into the Comparator where you can decide which part of values should be compared. If the default Comparator or one of the other provided comparators and factories don’t fit to your needs, you can create your own one.

Info
intl-sort can sort objects with CallableAccess in callables. This example is for showing how to create a custom Comparator

Let’s use code examples and explain how you can implement a Comparator that e.g. can sort an array of objects of the same type. We assume your objects are instances of a class named Product:

namespace Your\ProjectNamespace;

class Product
{
    private string $name;

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

Create your Comparator

Your code is providing an array of Product-instances, and you want to be able to order them with the sort builder of intl-sort by their names. While the default Comparator can handle string and integer, you will need one that can handle Product-objects and for that you have to implement the Budgegeria\IntlSort\Comparator\Comparable interface. It can look like this in an example class called ProductNameComparator:

namespace Your\ProjectNamespace;

use Budgegeria\IntlSort\Collator\Collator;
use Budgegeria\IntlSort\Comparator\Comparable;

class ProductNameComparator implements Comparable
{
    /**
     * @var Collator
     */
    private Collator $collator;

    public function __construct(Collator $collator)
    {
        $this->collator = $collator;
    }

    /**
     * {@inheritDoc}
     */
    public function compare(mixed $product, mixed $comparativeProduct): int
    {
        // Use Product::getName() for comparision.
        return $this->collator->compare($product->getName(), $comparativeProduct->getName());
    }
}

Allow your ProductNameComparator the injection of the Budgegeria\IntlSort\Collator\Collator in the constructor, which is the instance that contains the configuration of the sorter builder.

The comparator has to return -1 if the compared value of the first object is less than the second one, 1 if it’s greater and 0 if both are equal. If you directly use the given Budgegeria\IntlSort\Collator\Collator instance, its Collator::compare() method will handle this for you.

Don’t forget to handle errors in your class. You have to use Budgegeria\IntlSort\Exception\IntlSortException in case you want to throw an exception.

This is a small example and the complexity of comparision is up to you and your domain logic.

Create a Comparator factory

The Budgegeria\IntlSort\Builder is always creating a new Comparable instance when Builder::getComparator() is called or inside Builder::getSorter(). The same goes for your ProductNameComparator. To be able to do that, you also need to create a factory which is responsible for the creation of your custom comparator. It can look like this:

namespace Your\ProjectNamespace;

use Budgegeria\IntlSort\Collator\Collator;
use Budgegeria\IntlSort\Comparator\Comparable;
use Budgegeria\IntlSort\ComparatorFactory\Factory;
use Your\ProjectNamespace\ProductNameComparator;

class ProductNameComparatorFactory implements Factory
{
    public function create(Collator $collator): Comparable
    {
        return new ProductNameComparator($collator);
    }
}

A factory has also the advantage to inject dependencies into a constructor in case your comparator needs more than just a Collator.

Get your comparator into the sorter builder

The constructor of Budgegeria\IntlSort\Builder has an optional second argument where you can inject your factory instance:

use Budgegeria\IntlSort\Builder;
use Your\ProjectNamespace\ProductNameComparatorFactory;
use Your\ProjectNamespace\Product;

$sortBuilder = new Builder('de_DE', new ProductNameComparatorFactory());
$sorter = $sortBuilder->getSorter();

$products = [
    new Product('shoes'),
    new Product('scarf'),
    new Product('socks'),
];

$sortedProducts = $sorter->sort($products);

When $sortBuilder->getSorter() is invoked, your factory will create and return an instance of ProductNameComparator for the sorter that sorts an array of Product-instances by the product name.

Now you can order your products with all the different possible configurations allowed by intl-sort to the different needs of each supported country/region throughout your project code. You can do the same with any other type of value as long as it contains unicode comparable values.

Home