Search by

italix / contracts

andrea-sivieri

Shared contracts (interfaces) for Italix libraries - enables seamless integration between italix/orm, italix/forms, and other compatible libraries

Package info

github.com/italix-net/contracts

pkg:composer/italix/contracts

Statistics

Installs: 23

Dependents: 11

Suggesters: 0

Stars: 0

Open Issues: 0

2.0.1 2026-08-29 14:59 UTC

This package is not auto-updated.

Last update: 2026-08-30 12:51:23 UTC


README

PHP Version License

Shared interfaces and foundational classes for Italix libraries, enabling seamless integration between italix/orm, italix/forms, and other compatible libraries.

Installation

composer require italix/contracts

Purpose

This package provides the common contracts and building blocks that allow different Italix libraries to work together without tight coupling:

  • italix/orm implements these contracts on its Table and Column classes
  • italix/forms consumes any object implementing these contracts
  • Third-party libraries can implement these contracts for compatibility

Contents

Interfaces

Interface Description
TableMeta Describes a table/entity with its columns
ColumnMeta Describes a column/field with its type and constraints
RelationMeta Describes a foreign key relationship
RelationalColumnMeta A column that may have FK relations (extends ColumnMeta)
PolymorphicColumnMeta A polymorphic FK column (extends ColumnMeta)
DelegatedTableMeta A table using delegated types pattern (extends TableMeta)

Classes

Class Description
DataContainer Lightweight array-backed data container with array access, iteration, and JSON support

DataContainer

A general-purpose data container that wraps an associative array and provides both array syntax ($c['key']) and a clean method-based API.

Implements ArrayAccess, Countable, IteratorAggregate, and JsonSerializable.

Quick Start

use Italix\Contracts\DataContainer;

// Create with initial data
$user = new DataContainer(['name' => 'Alice', 'age' => 30]);

// Array syntax works naturally
$user['email'] = 'alice@example.com';
echo $user['name'];      // 'Alice'
isset($user['email']);    // true
unset($user['age']);

// Method syntax with fluent chaining
$user->set('city', 'Rome')
     ->set('role', 'admin')
     ->merge(['active' => true, 'score' => 100]);

Getting Values

$user = new DataContainer(['name' => 'Alice', 'role' => 'admin']);

// get() with default value — never throws on missing keys
$user->get('name');              // 'Alice'
$user->get('missing');           // null
$user->get('missing', 'N/A');   // 'N/A'

// Check existence
$user->has('name');       // true  — key exists and is not null
$user->has('missing');    // false
$user->has_key('name');   // true  — key exists (even if value is null)

Extracting Data

$user = new DataContainer([
    'id' => 1,
    'name' => 'Alice',
    'email' => 'alice@example.com',
    'password' => 'hashed...',
]);

// Get everything as a plain array
$user->to_array();
// ['id' => 1, 'name' => 'Alice', 'email' => 'alice@example.com', 'password' => 'hashed...']

// Pick specific keys
$user->only(['name', 'email']);
// ['name' => 'Alice', 'email' => 'alice@example.com']

// Exclude sensitive keys
$user->except(['password']);
// ['id' => 1, 'name' => 'Alice', 'email' => 'alice@example.com']

// Inspect keys and values
$user->keys();    // ['id', 'name', 'email', 'password']
$user->values();  // [1, 'Alice', 'alice@example.com', 'hashed...']

Modifying Data

$config = new DataContainer(['debug' => false]);

// set() returns $this for chaining
$config->set('debug', true)
       ->set('log_level', 'info');

// merge() overwrites existing keys
$config->merge(['debug' => false, 'cache' => true]);
// Result: ['debug' => false, 'log_level' => 'info', 'cache' => true]

// Remove a key
$config->remove('cache');

// Clear everything
$config->clear();
$config->is_empty(); // true

Iteration, Counting, and JSON

$data = new DataContainer(['a' => 1, 'b' => 2, 'c' => 3]);

// count()
count($data);  // 3

// foreach
foreach ($data as $key => $value) {
    echo "$key: $value\n";
}

// json_encode()
echo json_encode($data);  // {"a":1,"b":2,"c":3}

// Check state
$data->is_empty();  // false
$data->count();     // 3

Extending DataContainer

DataContainer is designed to be extended. The $data property is protected, so subclasses can access it directly:

use Italix\Contracts\DataContainer;

class Config extends DataContainer
{
    /**
     * Get a nested value using dot notation.
     */
    public function dot_get(string $path, $default = null)
    {
        $keys = explode('.', $path);
        $value = $this->data;

        foreach ($keys as $key) {
            if (!is_array($value) || !array_key_exists($key, $value)) {
                return $default;
            }
            $value = $value[$key];
        }

        return $value;
    }
}

$config = new Config([
    'database' => ['host' => 'localhost', 'port' => 3306],
]);

$config->dot_get('database.host');  // 'localhost'
$config->dot_get('database.name', 'myapp');  // 'myapp' (default)
use Italix\Contracts\DataContainer;

class FormData extends DataContainer
{
    /**
     * Sanitize all string values.
     */
    public function sanitize(): self
    {
        foreach ($this->data as $key => $value) {
            if (is_string($value)) {
                $this->data[$key] = trim(strip_tags($value));
            }
        }
        return $this;
    }
}

$input = new FormData($_POST);
$input->sanitize();
$clean_name = $input->get('name');

snake_case Convention

All Italix public methods use snake_case. The methods required by PHP's built-in interfaces (offsetGet, jsonSerialize, getIterator) are implemented as required, with snake_case aliases provided:

PHP interface method snake_case alias
jsonSerialize() json_serialize()
getIterator() get_iterator()

The ArrayAccess methods (offsetGet, offsetSet, offsetExists, offsetUnset) don't need aliases because they are called implicitly via array syntax ($c['key']), not directly. Use get(), set(), has(), and remove() instead.

Complete API Reference

Method Returns Description
__construct(array $data = []) Create with optional initial data
get($key, $default = null) mixed Get value by key, with default
set($key, $value) self Set a value (chainable)
has($key) bool Key exists and is not null
has_key($key) bool Key exists, even if null
remove($key) self Remove a key (chainable)
merge(array $data) self Merge in new data (chainable)
to_array() array Get all data as plain array
only(array $keys) array Get subset of keys
except(array $keys) array Get all data except specified keys
keys() array Get all keys
values() array Get all values
is_empty() bool Check if container has no data
clear() self Remove all data (chainable)
count() int Number of entries (also works with count())
json_serialize() array Get data for JSON encoding
get_iterator() Traversable Get iterator for foreach

Interfaces

Implementing TableMeta

use Italix\Contracts\TableMeta;
use Italix\Contracts\ColumnMeta;

class UsersTable implements TableMeta
{
    private array $columns;

    public function describe_columns(): iterable
    {
        return $this->columns;
    }

    public function describe_column(string $name): ?ColumnMeta
    {
        return $this->columns[$name] ?? null;
    }
}

Implementing ColumnMeta

use Italix\Contracts\ColumnMeta;

class VarcharColumn implements ColumnMeta
{
    public function get_name(): string { return $this->name; }
    public function get_type(): string { return 'VARCHAR'; }
    public function is_nullable(): bool { return $this->nullable; }
    public function is_primary_key(): bool { return false; }
    public function get_length(): ?int { return $this->length; }
    public function get_default() { return $this->default; }
    public function has_default(): bool { return $this->hasDefault; }
}

Using with italix/forms

use Italix\Forms\FormMeta;

// Any TableMeta implementation works
$form = new FormMeta($usersTable);

// DelegatedTableMeta enables delegation
$form = new FormMeta($thingsTable);
$form->delegate('Book');  // Merges Thing + Book columns

Hierarchy

Interfaces:

    TableMeta
        └── DelegatedTableMeta

    ColumnMeta
        ├── RelationalColumnMeta
        └── PolymorphicColumnMeta

    RelationMeta (standalone)

Classes:

    DataContainer (ArrayAccess, Countable, IteratorAggregate, JsonSerializable)

Requirements

  • PHP 7.4 or higher

License

Apache-2.0