PHP 8 Enums In Action

Enums In Action

Enumerations, or enums for short, provide a way to define a custom type that is limited to one of a discrete number of possible values.

We’re going to hack and slash our way through learning enums in PHP by example, just like a cyberpunk taking down a rogue AI – except our code will be the weapon and type errors will be the enemies.

Here are a few common use cases for enums in PHP 8+:

  1. Defining a set of status codes: Enums can be used to define a set of status codes for a particular entity or operation. For example, an “Order” class may have an “OrderStatus” enum with constants like “PENDING”, “SHIPPED”, “DELIVERED”, and “CANCELLED”.
  2. Representing a set of options: Enums can be used to define a set of options for a particular feature or setting. For example, an “Email” class may have an “EmailType” enum with constants like “HTML”, “PLAIN_TEXT”, and “MARKDOWN”.
  3. Representing a set of roles or permissions: Enums can be used to define a set of roles or permissions for a user. For example, an “User” class may have an “UserRole” enum with constants like “ADMIN”, “MODERATOR”, “USER”.
  4. Handling error codes: Enums can be used to define a set of error codes for a particular operation. For example, an “API” class may have an “APIError” enum with constants like “INVALID_API_KEY”, “RATE_LIMIT_EXCEEDED”, “INVALID_PARAMETERS”, and “INTERNAL_SERVER_ERROR”.
enum OrderStatus
{
    case PENDING;
    case SHIPPED;
    case DELIVERED;
    case CANCELLED;
}

class Order
{
    private OrderStatus $status;

    public function __construct(OrderStatus $status) {
        $this->status = $status;
    }

    public function setStatus(OrderStatus $status): void {
        $this->status = $status;
    }

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

$order = new Order(OrderStatus::PENDING);
echo $order->getStatus(); // Output: PENDING

$order = new Order(OrderStatus::SHIPPED);
echo $order->getStatus(); // Output: SHIPPED

In our example code above, the OrderStatus is defined as an enum with four constants PENDING, SHIPPED, etc. We create an Order class passing the status as PENDING that will be type checked. Enums are type-safe, so you can be sure that the value of the status property will always be one of the defined constants.

The following is an example of ‘backed enums‘:

enum OrderStatus: string
{
    case PENDING = "pending";
    case SHIPPED = "shipped";
    case DELIVERED = "delivered";
    case CANCELLED = "cancelled";
}

class Order
{
    private OrderStatus $status;

    public function __construct(OrderStatus $status) {
        $this->status = $status;
    }

    public function setStatus(OrderStatus $status): void {
        $this->status = $status;
    }

    public function getStatus(): string {
        return $this->status->value;
    }
}

$order = new Order(OrderStatus::PENDING);
echo $order->getStatus(); // Output: pending

$order->setStatus(OrderStatus::SHIPPED);
echo $order->getStatus(); // Output: shipped

You can use the Backed Enum from() method to grab a corresponding Enum Case. For example:

// The Enum:
enum OrderStatus : string {
    case PENDING = "pending";
    case SHIPPED = "shipped";
    case DELIVERED = "delivered";
    case CANCELLED = "cancelled";
}

// Usage:
class OrderHandler {
    public function handle($request) {
        // get the status value from the request data
        $status = $request["status"];
        // use the from() method to create an enum instance from the status value
        $orderStatus = OrderStatus::from($status);
        // do something with the enum instance
        switch ($orderStatus) {
            case OrderStatus::PENDING:
                // handle pending order
                echo "Handling pending order\n";
                break;
            case OrderStatus::SHIPPED:
                // handle shipped order
                echo "Handling shipped order\n";
                break;
            case OrderStatus::DELIVERED:
                // handle delivered order
                echo "Handling delivered order\n";
                break;
            case OrderStatus::CANCELLED:
                // handle cancelled order
                echo "Handling cancelled order\n";
                break;
        }
    }
}

// Example usage:
$handler = new OrderHandler();
$request1 = ["status" => "pending"];
$request2 = ["status" => "shipped"];
$request3 = ["status" => "delivered"];
$request4 = ["status" => "cancelled"];
$handler->handle($request1); // Output: Handling pending order
$handler->handle($request2); // Output: Handling shipped order
$handler->handle($request3); // Output: Handling delivered order
$handler->handle($request4); // Output: Handling cancelled order

Enums & Laravel

Let’s say we’re working with a Laravel eloquent model to store and retrieve these values from the database. Now with our backed enum, all we need to do is add it to our $casts array:

app/Models/Order.php

protected $casts = [
   'order_status' => OrderStatus::class,
];

We can also use enums for validation:

// pull in the enum validation rule:
use Illuminate\Validation\Rules\Enum;

// and your enum class:
use App\Enums\OrderStatus;

// validate your enum like so:
$request->validate([
    'order_status' => ['required', new Enum(OrderStatus::class)],
]);

Enum Methods

You may define helpful methods inside your enums. These methods can be used to perform specific operations on the enum values or to obtain information about the values. Here are some ideas on how to use methods in your enums:

  • Associate a custom behavior with each value. For example, if you have an enum representing different colors, you could define a method for each color that returns the corresponding HTML color code.
  • Ensure that all instances of the enum behave consistently. For example, if you have an enum representing different units of measurement, you could define a method for each unit that converts a value from that unit to another unit.
  • Provide basic utility such as returning an array of the backed enum values.

If your methods happen to be used in more than one enum, consider creating a trait that can easily be included. For example:

// The trait:

namespace App\Traits;

trait EnumValues
{
    // Return array of just the values.
    public static function values(): array
    {
        return array_column(self::cases(), 'value');
    }

    // Return array with enum value for key and enum name for value.
    // Useful for select menus.
    public static function keyedValues(): array
    {
        return array_combine(
            array_column(self::cases(), 'value'),
            array_column(self::cases(), 'name')
        );
    }

}

// The enum:

namespace App\Enums;

use App\Traits\EnumValues;

enum FileType: string
{
    // Including the trait in your enum:    
    use EnumValues;

    case Image = 'image';
    case PDF = 'pdf';
    case Text = 'text';
    case Audio = 'audio';

}

// Usage:

$values = FileType::values();

What About AI Assistants?

As we’ve just explored the delightful world of order statuses, you might be wondering, “Can Enums be used in other fascinating and whimsical ways?” Fear not, dear reader, for we shall now embark on a thrilling journey from the bustling realm of e-commerce to the serene gardens of the future!

Picture this: a world where AI-driven gardening assistants dutifully tend to the needs of our precious plants. As we trade in our carts and checkout lanes for watering cans and pruning shears, Enums will prove themselves as versatile and steadfast companions. But wait, there’s more! Our tale of garden adventures wouldn’t be complete without the powerful Monolog library to handle our logging needs with grace and precision.

So, buckle up and put on your gardening gloves as we dive into the verdant landscape of Enums, Monolog, and AI-powered gardening. Our trusty AI assistants will expertly navigate the tasks of watering, pruning, fertilizing, and monitoring plant health, while Monolog ensures we never miss a beat in our logs. Prepare to be amazed as Enums, AI, and Monolog join forces to transform your garden into a futuristic oasis!

<?php

require 'vendor/autoload.php';

use Psr\Log\LoggerInterface;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Psr\Log\LogLevel;

// Enum class for garden tasks
enum GardenTask: string
{
    case WATER_PLANTS = 'water_plants';
    case PRUNE_PLANTS = 'prune_plants';
    case FERTILIZE_PLANTS = 'fertilize_plants';
    case MONITOR_PLANT_HEALTH = 'monitor_plant_health';
}

// AI-driven gardening assistant class
class AIPlantGardenAssistant
{
    private LoggerInterface $logger;

    // Constructor that accepts a logger implementing LoggerInterface
    public function __construct(LoggerInterface $logger)
    {
        $this->logger = $logger;
    }

    // Main method to assist with the requested garden task
    public function assist(array $request): void
    {
        if (!isset($request['task'])) {
            $this->logger->error('Invalid request: Missing garden task');
            return;
        }

        $task = $request['task'];

        try {
            $gardenTask = GardenTask::from($task);
        } catch (ValueError $e) {
            $this->logger->error("Invalid garden task: {$task}");
            return;
        }

        // Handle the garden task based on the enum value
        switch ($gardenTask) {
            case GardenTask::WATER_PLANTS:
                $this->assistWithWateringPlants();
                break;
            case GardenTask::PRUNE_PLANTS:
                $this->assistWithPruningPlants();
                break;
            case GardenTask::FERTILIZE_PLANTS:
                $this->assistWithFertilizingPlants();
                break;
            case GardenTask::MONITOR_PLANT_HEALTH:
                $this->assistWithMonitoringPlantHealth();
                break;
        }
    }

    // Helper methods for each garden task
    private function assistWithWateringPlants(): void
    {
        $this->logger->info('AI assisting with watering plants in the garden');
    }

    private function assistWithPruningPlants(): void
    {
        $this->logger->info('AI assisting with pruning plants in the garden');
    }

    private function assistWithFertilizingPlants(): void
    {
        $this->logger->info('AI assisting with fertilizing plants in the garden');
    }

    private function assistWithMonitoringPlantHealth(): void
    {
        $this->logger->info('AI assisting with monitoring plant health in the garden');
    }
}

// Example usage:
echo '<pre>';
$logger = new Logger('AIPlantGardenAssistant');
$logger->pushHandler(new StreamHandler('php://output', LogLevel::INFO));

// Instantiate the AI gardening assistant
$assistant = new AIPlantGardenAssistant($logger);

// Test the assistant with different garden tasks
$request1 = ['task' => 'water_plants'];
$request2 = ['task' => 'prune_plants'];
$request3 = ['task' => 'fertilize_plants'];
$request4 = ['task' => 'monitor_plant_health'];
$assistant->assist($request1); // Output: AI assisting with watering plants in the garden
$assistant->assist($request2); // Output: AI assisting with pruning plants in the garden
$assistant->assist($request3); // Output: AI assisting with fertilizing plants in the garden
$assistant->assist($request4); // Output: AI assisting with monitoring plant health in the garden

In conclusion, enums in PHP 8 are a powerful tool for creating expressive and maintainable code. The examples shown here are just a small taste of what you can do with enums, and there are other useful cases waiting to be discovered. Whether you’re a seasoned PHP developer or new to the language, learning how to use enums can take your code to the next level. So go forth and start slicing and dicing with enums!

Related Posts
Leave a Reply

Your email address will not be published.Required fields are marked *