tuki/soda

A lightweight library for property validation and object-to-array/JSON conversion using Symfony Validator and traits.

Maintainers

Package info

gitlab.com/Grupotuki/soda

Issues

pkg:composer/tuki/soda

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

v1.0.0 2026-04-01 00:20 UTC

This package is auto-updated.

Last update: 2026-04-01 00:30:38 UTC


README

PHP Pipeline Status Downloads Latest Version

Structured Object Data Arrayable

Soda is a lightweight, developer-friendly library designed to transform Plain Old PHP Objects (POPOs) into intelligent data structures. It provides seamless hydration from arrays/JSON, data extraction, and optional property validation using Symfony Constraints—all through a simple, Trait-based API.

Why Soda?

The name represents the four core architectural pillars of the library:

  • Structured: Ensures your data objects follow a predictable and reliable schema.
  • Object: Embraces pure PHP objects without requiring heavy inheritance or boilerplate.
  • Data: Focused entirely on data integrity and safe information transfer (DTOs).
  • Arrayable: Native, bidirectional conversion between raw arrays and structured objects.

Core Philosophy

Soda leverages Symfony Validator and PHP Reflection under the hood. It is designed to be opt-in: you decide which capabilities to grant your objects by using specific Traits and Contracts.

⚠️ IMPORTANT

For optimal performance and security:

  • Use protected properties for your data. The Validates trait uses __set to trigger validation automatically when a protected property is modified from outside (via magic setter).
  • Public properties are only validated during factory creation (fromArray, fromJson). If you modify a public property directly after the object is created, no automatic validation will occur.
  • Avoid overriding __set unless you manually call $this->validate() within it, as it may break the automatic validation mechanism.

How to install

composer require tuki/soda

Quick Start

Basic Usage (Validation + Conversion)

<?php

use Tuki\Soda\Traits\Validates;
use Tuki\Soda\Traits\AsArray;
use Tuki\Soda\Contracts\Validatable;
use Symfony\Component\Validator\Constraints as Assert;

class User implements Validatable
{
    use Validates, AsArray;

    #[Assert\NotBlank]
    #[Assert\Email]
    protected string $email;

    #[Assert\Length(min: 8)]
    protected string $password;
    
    // Public properties are only validated on creation
    #[Assert\Range(min: 18)]
    public int $age;
}

// Creation via Factory (validates immediately)
$user = User::fromArray([
    'email' => 'user@example.com',
    'password' => 'secret123',
    'age' => 25
]);

// Updating a protected property (triggers automatic validation via __set)
$user->email = 'new-email@example.com'; 

// Converting back to array
$data = $user->toArray();

JSON Support

<?php

use Tuki\Soda\Traits\AsJson;
use Tuki\Soda\Contracts\Validatable;
use Symfony\Component\Validator\Constraints as Assert;

class Product implements Validatable
{
    use AsJson, Validates;

    #[Assert\NotBlank]
    protected string $sku;
}

$product = Product::fromJson('{"sku": "ABC-123"}');
echo $product->toJson();

Standalone Trait Usage

You can use AsArray or AsJson without the Validates trait if you only need the conversion features without validation.

<?php

use Tuki\Soda\Traits\AsArray;

class SimpleUser
{
    use AsArray;

    public string $name;
    public string $role;
}

// Works even without the Validates trait or Validatable contract
$user = SimpleUser::fromArray(['name' => 'Alice', 'role' => 'admin']);
$data = $user->toArray();

The Importance of the Validatable Contract

Automatic validation during factory creation (fromArray, fromJson) only occurs if your class implements the Validatable contract. If you use the traits without the contract, you must call validate() manually.

<?php

use Tuki\Soda\Traits\Validates;
use Tuki\Soda\Traits\AsArray;
use Symfony\Component\Validator\Constraints as Assert;

// Missing "implements Validatable"
class IncompleteUser
{
    use AsArray, Validates;

    #[Assert\Email]
    public string $email;
}

// No validation occurs here even if the email is invalid!
$user = IncompleteUser::fromArray(['email' => 'not-an-email']);

// You must validate manually if the contract is missing
// $user->validate(); 

Manual Validation & Error Handling

You can also trigger validation manually and handle errors using the ObjectValidationException.

<?php

use Tuki\Soda\Traits\Validates;
use Tuki\Soda\Exceptions\ObjectValidationException;
use Symfony\Component\Validator\Constraints as Assert;

class Contact
{
    use Validates;

    #[Assert\NotBlank]
    public string $name;

    #[Assert\Email]
    public string $email;
}

$contact = new Contact();
$contact->name = 'John';
$contact->email = 'invalid-email';

try {
    $contact->validate();
} catch (ObjectValidationException $e) {
    // Get all error messages grouped by property
    $errors = $e->errorsBag();
    
    /*
    [
        "email" => ["This value is not a valid email address."]
    ]
    */
}

Extended Constraints (Soda Power-Ups)

Soda extends Symfony's validation power with a set of custom constraints designed for common real-world scenarios, semantic clarity, and regional compliance. You can find them under the Tuki\Soda\Validator\Constraints namespace.

Semantic & Convenience Helpers

These are "shortcuts" for common Symfony configurations to keep your DTOs clean.

ConstraintDescriptionEquivalent to...
#[SodaAssert\Required]Field must not be empty or null.#[Assert\NotBlank]
#[SodaAssert\Prohibits]Field must be empty or null.#[Assert\Blank]
#[SodaAssert\MinAge(x)]Validates that a birth date corresponds to at least x years old.#[Assert\LessThanOrEqual("-x years")]
#[SodaAssert\MaxAge(x)]Validates that a person is not older than x years.#[Assert\GreaterThanOrEqual("-x years")]
#[SodaAssert\Slug]Validates a URL-friendly string (lowercase, numbers, hyphens).#[Assert\Regex("/^[a-z0-9]+(?:-[a-z0-9]+)*$/")]
#[SodaAssert\Phone]Basic international phone format validation.#[Assert\Regex("/^\+?[0-9\s\-]{7,15}$/")]

Conditional Constraints

Powerful logic to make properties mandatory or prohibited based on other values.

ConstraintDescription
#[SodaAssert\RequiredIf("expression")]Required only if the condition is true.
#[SodaAssert\RequiredWith("field")]Required if the other field is present.
#[SodaAssert\RequiredWithout("field")]Required if the other field is missing.
#[SodaAssert\ProhibitedIf("expression")]Must be empty if the condition is true.
#[SodaAssert\ProhibitedWith("field")]Must be empty if the other field is present.
#[SodaAssert\ProhibitedWithout("field")]Must be empty if the other field is missing.

Regional Compliance

Native support for identification numbers that Symfony doesn't include by default.

ConstraintDescription
#[SodaAssert\IsNif]Validates Spanish NIF (National ID).
#[SodaAssert\IsNie]Validates Spanish NIE (Foreigner ID).
#[SodaAssert\IsCif]Validates Spanish Business Tax ID (CIF) and Honduras RTN.

Example: Advanced Validation

use Tuki\Soda\Validator\Constraints as SodaAssert;

class CheckoutRequest implements Validatable
{
    use Validates, AsArray;

    #[SodaAssert\Required]
    public string $customerName;

    #[SodaAssert\IsCif]
    #[SodaAssert\RequiredIf("this.isCompany == true")]
    protected ?string $taxId;

    public bool $isCompany = false;

    #[SodaAssert\MinAge(18)]
    protected string $birthDate;

    #[SodaAssert\RequiredWith("creditCardNumber")]
    protected ?string $cvv;
}

License

Soda is open-source software licensed under the MIT License. Maintained by the Tuki Foundation.