phrity/comparison
Lightweight PHP interfaces and a helper trait for object comparisons. Implement Equalable/Comparable (or just compare() via ComparisonTrait) to get equals/greater/less helpers, plus a Comparator utility for sorting and filtering. Compatible with PHP 7.1+.
composer require phrity/comparison
Comparable or Equalable in your model/DTO:
use Phrity\Comparison\{Comparable, ComparisonTrait};
class Product implements Comparable
{
use ComparisonTrait;
public function compare($compare_with): int
{
return $this->price <=> $compare_with->price;
}
}
Comparator for sorting/filtering:
$products = [new Product(100), new Product(50)];
$comparator = new \Phrity\Comparison\Comparator($products);
$sorted = $comparator->sort(); // Ascending
$filtered = $comparator->greaterThan($products[0]); // Products > $50
Sort a Laravel Collection of Eloquent models:
use Phrity\Comparison\{Comparable, ComparisonTrait};
class User extends Model implements Comparable
{
use ComparisonTrait;
public function compare($compare_with): int
{
return $this->created_at <=> $compare_with->created_at;
}
}
// Usage:
$users = User::all();
$comparator = new \Phrity\Comparison\Comparator($users->toArray());
$sortedUsers = $comparator->sort(); // Sorted by creation date
ComparisonTrait to auto-implement Comparable methods with a single compare() method.class Order implements Comparable
{
use ComparisonTrait;
public function compare($compare_with): int
{
return $this->total <=> $compare_with->total;
}
}
Comparable/Equalable manually for fine-grained control.class Category implements Comparable
{
public function equals($compare_with): bool
{
return $this->id === $compare_with->id;
}
public function compare($compare_with): int
{
return strcmp($this->name, $compare_with->name);
}
// Implement remaining Comparable methods...
}
$comparator = new \Phrity\Comparison\Comparator($models);
$ascending = $comparator->sort(); // Ascending
$descending = $comparator->rsort(); // Descending
$expensive = $comparator->greaterThan($thresholdModel);
$recent = $comparator->lessThanOrEqual($cutoffModel);
$cheapest = $comparator->min();
$mostRecent = $comparator->max();
Illuminate\Support\Collection to use Comparator:
use Phrity\Comparison\Comparator;
class ComparableCollection extends \Illuminate\Support\Collection
{
public function sortByComparator(): self
{
return $this->sort(fn ($a, $b) => $a->compare($b));
}
}
$collection = new ComparableCollection($models);
$sorted = $collection->sortByComparator();
$comparator = new Comparator();
$sortedByPrice = $comparator->sort($products, fn ($a, $b) => $a->price <=> $b->price);
$sortedByName = $comparator->sort($products, fn ($a, $b) => strcmp($a->name, $b->name));
Comparable to models for database-agnostic sorting:
class Product extends Model implements Comparable
{
use ComparisonTrait;
public function compare($compare_with): int
{
return $this->price <=> $compare_with->price;
}
}
$products = Product::all();
$comparator = new Comparator($products);
$sorted = $comparator->sort();
Comparator-like filtering:
use Illuminate\Database\Eloquent\Builder;
Builder::macro('whereGreaterThan', function ($field, $condition) {
return $this->where($field, '>', $condition->$field);
});
$expensive = Product::whereGreaterThan('price', $thresholdProduct);
equals()/greaterThan():
public function rules()
{
return [
'price' => [
'numeric',
function ($attribute, $value, $fail) {
$existing = Product::where('price', '>=', $value)->first();
if ($existing && $existing->greaterThan($this->product)) {
$fail('Price must be unique.');
}
},
],
];
}
Comparator logic in a service:
class ProductService
{
public function getSortedProducts(array $products, string $sortBy)
{
$comparator = new Comparator($products);
return match ($sortBy) {
'price' => $comparator->sort(),
'name' => $comparator->sort(fn ($a, $b) => strcmp($a->name, $b->name)),
default => $products,
};
}
}
compare() may receive non-comparable objects (e.g., null, primitives).IncomparableException explicitly:
public function compare($compare_with): int
{
if (!$compare_with instanceof self) {
throw new IncomparableException('Can only compare with ' . self::class);
}
return $this->price <=> $compare_with->price;
}
Comparator may fail if input contains null values.null values before passing to Comparator:
$validItems = array_filter($items, fn ($item) => $item !== null);
$comparator = new Comparator($validItems);
Comparator->sort() uses PHP’s usort, which is O(n log n). For >10,000 items, consider:
orderBy() in Eloquent.Cursor or Collection::chunk().ComparisonTrait may conflict with other traits (e.g., Arrayable).insteadof:
trait ComparisonTrait
{
insteadof Arrayable {
// Custom array conversion logic
}
}
Comparator stores input arrays internally. Modifying the stored array affects all operations.$comparator = new Comparator(array_map(fn ($item) => clone $item, $items));
compare() logic with edge cases:
$a = new Product(100);
$b = new Product(100);
$c = new Product(50);
assert($a->equals($b)); // true
assert($a->greaterThan($c)); // true
assert($a->compare($b) === 0); // 0 (equal)
assert($a->compare($c) === 1); // 1 (greater)
IncomparableExceptionComparator usage in a try-catch:
try {
$result = $comparator->greaterThan($invalidItem);
} catch (
How can I help you explore Laravel packages today?