User Tools

Site Tools


nvg-cockpit:dxo-base-model

This is an old revision of the document!


_DXOBaseModel (PHP)

Generic Base Model providing shared ORM-like functionality.

  • Author: dynaxio AG
  • Created: 2025-11-06
  • Updated: 2025-11-11
  • Version: 1.4.1
  • License: See LICENSE.md in the project root.

Provides dynamic getters/setters, automatic persistence, per-class caching, consistency-edges registration, and policy-aware deletion via _destroyMySelf(). Edges and policies may use \Lib\GlobalOptions::* identifiers for table names (resolved to physical tables at runtime).


Quick facts


Example: minimal child model

namespace Lib\Model;
 
use Lib\_DXOBaseModel;
 
class Partner extends _DXOBaseModel
{
    protected ?string $table = null;
 
    // Outbound relations from Partner rows
    protected array $edgesMap = [
        'owner_user_id' => [
            'relation'   => 'partner:owner',
            'dst_table'  => 'GlobalOptions::CPTDB_TBL_USERS',
        ],
        'account_key' => [
            'relation'   => 'partner:account',
            'dst_table'  => 'GlobalOptions::CPTDB_TBL_ACCOUNTS',
            'resolve_by' => 'uniqueid', // external key → accounts.id
        ],
    ];
 
    public function __construct(int $id = 0, array $initial = [])
    {
        $this->table = \Lib\GlobalOptions::CPTDB_TBL_PARTNERS();
        parent::__construct($id, $initial);
    }
}

Properties

Property Type Purpose
protected \PDO $pdo PDO Shared DB handle.
protected ?Utils $utils Utils\null Logger & helpers.
public int $id int Primary key.
public array $record array Full row (assoc).
protected ?string $table string\null Physical table name (child sets).
protected ?array $allowedFields string[]\null Optional whitelist for setters.
protected array $readOnlyFields string[] Defaults to [id].
protected array $validators array<string,callable> fn($val,$field,$this):bool.
protected array $edgesMap array Outbound edges (field → relation config).
protected const RECORD_CACHE_TTL int Cache TTL (default 10s).
protected static array $recordCache array In-memory cache by class/id.
protected ?string $tableSpec string\null Optional canonical source spec (e.g. GlobalOptions::XYZ).

Construction lifecycle

public function __construct(int $id = 0, array $initial = [])

* If $id > 0: loads the record. * If $id == 0: creates an empty row (insertEmptyRow()), then loads. * Populates $this→record and $this→id.

insertEmptyRow() uses an empty insert:

INSERT INTO `<table>` () VALUES ()

On success, the new id is stored and the record is reloaded.


Magic accessors

Use dynamic getters/setters: getXxx() / setXxx($val). Xxx is converted to snake_case.

$partner = new Partner(123);
$name    = $partner->getName();      // reads $record['name']
$ok      = $partner->setName('ACME'); // validates, saves, re-registers edges

Guards: * $allowedFields (whitelist) and $readOnlyFields (defaults to id) are enforced. * Per-field validators can be added via $validators['field'] = fn($val,$field,$self) ⇒ bool.


Persistence

Save

protected function _save(): false|int

* If the row exists → UPDATE changed fields. * If not → INSERT (or empty insert when $record is empty). * On success:

  • invalidates the cache,
  • registers outbound edges,
  • returns id.

Load (with cache)

protected function _load(): false|array

* Per-class, per-id cache with TTL (RECORD_CACHE_TTL). * Falls back to DB if expired/missing.


Table name resolution

protected function fetchTableName(): string|false
protected function resolveTableName(string $maybe): string

* fetchTableName(): returns $this→table, logs error if missing. * resolveTableName(): accepts GlobalOptions::X or \Lib\GlobalOptions::X and calls the static method to resolve to a raw table name; otherwise returns input.


Consistency edges

Outbound edges are derived from $edgesMap and the current $record.

edgesFromSelf()

public function edgesFromSelf(): array

Returns an array of edges:

[
  'relation'   => string,
  'dst_table'  => string,     // canonical: 'GlobalOptions::XYZ' or raw table
  'dst_id'     => string,     // left as provided (string or numeric)
  'resolve_by' => string|null,// e.g. 'uniqueid'
  'meta'       => array,
]

Notes: * dst_id is cast to string to allow unique external keys. * If resolve_by is present, the manager may resolve the key → numeric id before storing the edge.

registerConsistencyEdges()

protected function registerConsistencyEdges(): void

* Skips if \Lib\Model\_ConsistencyManager is not available. * Determines $srcSpec ($this→tableSpec if set, else physical table). * Removes all existing outbound edges for (src_table, src_id). * Recreates edges via ensureLink() and forwards resolve_by when configured.


Policy-aware deletion

public function _destroyMySelf(): bool

* Validates id and table. * If \Lib\Model\_ConsistencyManager exists, delegates:

<code php>
$mgr->deleteNode(['table' => $this->table, 'id' => $this->id]);
</code>

* The manager reads ConsistencyPolicy::map() and applies RESTRICT / NULLIFY / CASCADE / DETACH. * Fallback: hard delete (not recommended).

Always delete via _destroyMySelf().


Utilities

public function getId(): int
public function invalidateRecordCache(): void
protected function toSnakeCase(string $name): string

Logging & error handling

* No exceptions are thrown to the caller in normal flow; failures are logged via Utils→log(level) with levels like debug, warning, error. * Methods return false on error (or 0 for IDs), enabling simple guard patterns.


Worked snippets

Create, set, save

$p = new Partner(0, ['name' => 'Init']);
$p->setName('ACME SA');        // UPDATE (after empty insert), edges re-registered
$id = $p->getId();

Policy-aware delete

$p = new Partner(123);
if (!$p->_destroyMySelf()) {
    // Deletion was restricted by policy (see logs)
}

Custom validator

$this->validators['email'] = function($val) {
    return filter_var($val, FILTER_VALIDATE_EMAIL) !== false;
};

Restrict writable fields

protected ?array $allowedFields = ['name','email','owner_user_id'];
protected array  $readOnlyFields = ['id','created_at'];

Interop with Consistency Manager

* edgesFromSelf() emits canonical dst_table specs (GlobalOptions::XYZ or raw). * registerConsistencyEdges() calls _ConsistencyManager::ensureLink():

  • Resolves GlobalOptionsraw tables before insert.
  • Optionally resolves destination by resolve_by (e.g. uniqueid) to numeric id.
  • Stores edges with src_id (int) and dst_id (string or resolved numeric string).

See the Consistency Manager page for schema, policies, and the reindex CLI.


Changelog

  • 1.4.1 (2025-11-11): Restore insertEmptyRow() and fix method name.
  • 1.4.0 (2025-11-11): Add GlobalOptions table-name resolver for edges/policy.
  • 1.3.0 (2025-11-11): Single-method delete approach (always call _destroyMySelf()).
  • 1.2.0 (2025-11-11): Edges + policy-aware _destroyMySelf().
  • 1.1.0 (2025-11-06): Convert to non-exception flow using Utils→log().
  • 1.0.0 (2025-11-06): Created.
nvg-cockpit/dxo-base-model.1762880361.txt.gz · Last modified: by 127.0.0.1