stave.dev

Entity Framework

An EAV data layer for Zero. Two tables hold every type you'll ever define; each type gets a pivoted SQL view you can query like a normal table.

Quick Start

Write a class that extends \Zero\Entity\Entity or call GenericEntity::define(). The first new MyType([...]) call inserts the type, adds a MySQL partition, writes the schema, and saves the record — in one pass.

What is the Entity Framework?

The Entity Framework is a small EAV (Entity-Attribute-Value) data layer that collapses schema, records, and class definition into one mechanism. You don't write migrations. You don't add columns. You declare a class, hand it a data array, and the storage layer takes care of itself.

The whole thing rests on two MySQL tables and a convention: each type gets its own partition in the entity table plus an auto-generated pivot view ({name}_view) that gives SELECTs the normal row-per-record shape.

How It Works

new User([...])
Lookup type
Define if missing
REPLACE INTO entity
Rebuild view

The first call with a given class name defines the schema forever: the keys of the constructor array become the column names, in order. Later calls with extra keys auto-append attributes to the schema and rebuild the pivot view. That "first call defines the schema" behavior is the single most important thing to internalize.

Core Topics

Minimal Example

A hand-coded entity class is just a marker — the binding is its lowercased short name:

<?php
namespace Zero\Entity;

class Note extends Entity {
    public $uniq = ['title'];
}

Use it:

require_once ZERO_ROOT . '/entity/Entity.php';   // also auto-inits $pdo
require_once ZERO_ROOT . '/entity/Note.php';

// First call defines the schema: title, body, created_at
$n = new \Zero\Entity\Note([
    'title'      => 'First Note',
    'body'       => 'Hello, world.',
    'created_at' => date('Y-m-d H:i:s'),
]);

// Later calls reuse the schema
$another = new \Zero\Entity\Note([
    'title'      => 'Second Note',
    'body'       => 'More text.',
    'created_at' => date('Y-m-d H:i:s'),
]);

That's all. The first call creates the note entity type, partitions the entity table, seeds the schema rows, and creates a note_view. Every subsequent call just writes data.

The First Call Is the Migration

If the first call's array has the wrong keys or wrong order, you have just defined the schema with the wrong keys, in the wrong order. Rename columns in SQL — or DELETE FROM entity WHERE type = ? AND id = 0 and start over.

When to Reach for It

Use it for small-to-medium, schema-fluid records where you don't want to write a migration every time a field is added — logging (Event), auth (User), admin-defined types, one-off test fixtures.

Skip it when you need typed columns, complex joins, transactions across many rows, fields longer than 255 characters, or high write throughput. Values are stored as VARCHAR(255) strings with zero type coercion, and every write is a REPLACE INTO. entity/ThingiverseQueue.php sits alongside the framework as a sibling class that chose a dedicated table instead — follow that precedent when the EAV model is a poor fit.