Skip to content

Recipes ​

Runnable, self-contained scripts for common problems. Every file lives in examples/ in the repository and can be run directly with php examples/<file>.php once dependencies are installed (composer install).

Looking for the business context behind these snippets — the risk, the legacy code, why it matters? See Case Studies for end-to-end walkthroughs: transactional email, SAP integration, and contract validation.

Checkout flow with coupons and payment authorization ​

Option for an optional coupon code, Result chaining a discount step into a payment authorization step.

examples/option-result.php

Customer registration with a full DTO ​

A complete DTO example: schema with trimmed(), regex(), bounded int, an optional field via Schema::option(), and structured error reporting through ValidationErrorBag.

examples/schema-dto.php

Repository lookup with Option ​

A repository returns Option instead of null; a service layer converts absence into a typed Result error only where it actually needs one, using okOr() and andThen().

php
final class CustomerRepository
{
    public function findById(int $id): Option
    {
        // ...
        return Option::none();
    }
}

$service = static function (CustomerRepository $repo, int $id): Result {
    return $repo->findById($id)
        ->okOr('customer_not_found')
        ->andThen(fn (array $customer): Result =>
            $customer['active'] ? Result::ok($customer['name']) : Result::err('customer_inactive')
        );
};

examples/recipe-repository-lookup.php

Wrapping a legacy, exception-throwing call ​

At a boundary with a third-party SDK or legacy code that only communicates failure via exceptions, wrap the call once instead of letting exceptions leak into Result-typed code:

php
function tryCatch(callable $fn): Result
{
    try {
        return Result::ok($fn());
    } catch (\Throwable $e) {
        return Result::err($e);
    }
}

examples/recipe-safe-external-call.php

Batch import with per-row error reporting ​

Validate an entire batch (e.g. a CSV import) in one pass with Schema::arrayOf() — errors report the row index in the path ($[1].email). For partial imports, validate row by row instead and keep both an imported and a rejected bucket.

examples/recipe-batch-import.php

More async recipes ​

For more before/after patterns (validating filters, standardizing JSON error responses, converting legacy null/false returns), see the Practical Recipes guide in the repository.

Using an AI assistant with Maybe ​

If you're using an AI coding assistant (Claude, Copilot, Cursor, etc.) to write code against this library, point it at llms.txt — a condensed, exact API reference meant for LLM consumption, so it doesn't have to guess method names or signatures.