I have a class that allows filtering based on an option, I need a way to accept options and also reject invalid options.
In the past, I would reach for a switch statement, or more recently a match statement. But an enum is better suited for this task.
public function filter($key, $value): static
{
$validOptions = [
'ids' => 'ids',
'includeArchived' => 'includeArchived',
'order' => 'order',
'page' => 'page',
'searchTerm' => 'searchTerm',
'summaryOnly' => 'summaryOnly',
'where' => 'where',
];
if (! in_array($key, $validOptions)) {
throw new InvalidArgumentException("Filter option '$key' is not valid.");
}
$this->queryString[$key] = $value;
return $this;
}
public function filter($key, $value): static
{
switch ($key) {
case 'ids':
case 'includeArchived':
case 'order':
case 'page':
case 'searchTerm':
case 'summaryOnly':
case 'where':
$this->queryString[$key] = $value;
break;
default:
throw new InvalidArgumentException("Filter option '$key' is not valid.");
}
$this->queryString[$key] = $value;
return $this;
}
public function filter($key, $value): static
{
$this->queryString[$key] = match ($key) {
'ids', 'includeArchived', 'order', 'page', 'searchTerm', 'summaryOnly', 'where' => $this->queryString[$key] = $value,
default => throw new InvalidArgumentException("Filter option '$key' is not valid."),
};
return $this;
}
All the logic is stored in an enum class making the filter method smaller:
public function filter($key, $value): static
{
if (! ContactFilterOptions::isValid($key)) {
throw new InvalidArgumentException("Filter option '$key' is not valid.");
}
$this->queryString[$key] = $value;
return $this;
}
The advantage to this approach the enum is reusable, allowing to easily test the behaviour of both the enum and filter method.
The enum class looks like this:
enum ContactFilterOptions: string {
case IDS = 'ids';
case INCLUDEARCHIVED = 'includeArchived';
case ORDER = 'order';
case PAGE = 'page';
case SEARCHTERM = 'searchTerm';
case SUMMARYONLY = 'summaryOnly';
case WHERE = 'where';
public static function isValid(string $value): bool
{
$validValues = array_map(fn($case) => $case->value, self::cases());
return in_array($value, $validValues);
}
}
The isValid method is an easy way to determine if the string is in the lost of accepted values.
Adding tests to confirm for valid and invalid matches:
test('a valid option returns true', function() {
assertTrue(ContactFilterOptions::isValid('ids'));
assertTrue(ContactFilterOptions::isValid('includeArchived'));
assertTrue(ContactFilterOptions::isValid('order'));
assertTrue(ContactFilterOptions::isValid('page'));
assertTrue(ContactFilterOptions::isValid('searchTerm'));
assertTrue(ContactFilterOptions::isValid('summaryOnly'));
assertTrue(ContactFilterOptions::isValid('where'));
});
test('an invalid option returns false', function() {
assertFalse(ContactFilterOptions::isValid('bogus'));
});
Testing the filter method throws an exception:
test('invalid filter option throws exception', function(){
(new Contacts())->filter('bogus', 1);
})->throws(InvalidArgumentException::class, "Filter option 'bogus' is not valid.");
With this approach, your methods become smaller and easier to test.
Subscribe to my newsletter for the latest updates on my books and digital products.
Find posts, tutorials, and resources quickly.
Subscribe to my newsletter for the latest updates on my books and digital products.