Metadatos de Modelo


Resumen

When using Phalcon\Mvc\Model classes, which correspond to actual tables in the database, Phalcon needs to know essential information regarding those tables, such as fields, data types, primary and foreign keys as well as relationships. The Phalcon\Mvc\Model\MetaData object is offering this functionality, transparently querying the database and generating the necessary data from the database schema. Los datos se pueden almacenar en un almacén de datos (tipo Redis, APCu, etc.) para asegurar que la base de datos no se consulta por el esquema cada vez que se ejecuta una consulta.

NOTE: During deployments to production, please ensure that you always invalidate the metaData cache so that database changes that propagated during your deployment are available in your application. El caché metaData se reconstruirá con todos los cambios necesarios.

<?php

use MyApp\Models\Invoices;
use Phalcon\Mvc\Model\MetaData;

$invoice = new Invoices();

/** @var MetaData\ $metadata */
$metadata = $invoice->getModelsMetaData();

$attributes = $metadata->getAttributes($invoice);
print_r($attributes);

$dataTypes = $metadata->getDataTypes($invoice);
print_r($dataTypes);

El código anterior imprimirá los nombres de los campos y también los campos al vector de tipos de campo. Usamos attributes como un alias de fields.

[
    [0] => inv_id
    [1] => inv_cst_id
    [2] => inv_status_flag
    [3] => inv_title
    [4] => inv_total
    [5] => inv_created_at
    [6] => inv_created_by
    [7] => inv_updated_at
    [8] => inv_updated_by
]

[
    [inv_id]          => 0,
    [inv_cst_id]      => 0,
    [inv_status_flag] => 0,
    [inv_title]       => 2,
    [inv_total]       => 0,
    [inv_created_at]  => 4,
    [inv_created_by]  => 0,
    [inv_updated_at]  => 4,
    [inv_updated_by]  => 0,
]

Constantes

Phalcon\Mvc\Model\MetaData exposes a number of constants that can be used to retrieve attributes from the internal collection.

Nombre Descripción
MODELS_ATTRIBUTES Cada columna en la tabla mapeada
MODELS_AUTOMATIC_DEFAULT_INSERT Campos que se deben ignorar de las sentencias SQL INSERT
MODELS_AUTOMATIC_DEFAULT_UPDATE Campos que se deben ignorar de las sentencias SQL UPDATE
MODELS_COLUMN_MAP Mapa de columna (alias)
MODELS_DATA_TYPES Cada columna y su tipo de datos
MODELS_DATA_TYPES_BIND Como se debe vincular/convertir cada columna
MODELS_DATA_TYPES_NUMERIC Las columnas que tienen tipos de datos numéricos
MODELS_DEFAULT_VALUES Valores por defecto para las columnas
MODELS_EMPTY_STRING_VALUES Columnas que permiten cadenas vacías
MODELS_IDENTITY_COLUMN La columna identidad. false si el modelo no tiene ninguna columna identidad
MODELS_NON_PRIMARY_KEY Cada columna que no sea parte de la clave primaria
MODELS_NOT_NULL Cada columna que no permita valores null
MODELS_PRIMARY_KEY Cada columna que sea parte de la clave primaria
MODELS_REVERSE_COLUMN_MAP Mapa de columna inverso (alias)

Métodos

public function getAttributes(ModelInterface $model): array

Devuelve los nombres de los atributos de la tabla (campos)

print_r(
    $metaData->getAttributes(
        new Invoices()
    )
);
public function getAutomaticCreateAttributes(
    ModelInterface $model
): array

Devuelve los atributos que deben ser ignorados de la generación SQL del INSERT

print_r(
    $metaData->getAutomaticCreateAttributes(
        new Invoices()
    )
);
public function getAutomaticUpdateAttributes(
    ModelInterface $model
): array

Devuelve los atributos que deben ser ignorados de la generación SQL del UPDATE

print_r(
    $metaData->getAutomaticUpdateAttributes(
        new Invoices()
    )
);
public function getBindTypes(ModelInterface $model): array

Devuelve los atributos y sus tipos de datos de enlace

print_r(
    $metaData->getBindTypes(
        new Invoices()
    )
);
public function getColumnMap(ModelInterface $model): array

Devuelve el mapa de columnas si lo hay

print_r(
    $metaData->getColumnMap(
        new Invoices()
    )
);
public function getDefaultValues(ModelInterface $model): array

Devuelve los atributos (que tienen valores por defecto) y sus valores por defecto

 print_r(
     $metaData->getDefaultValues(
         new Invoices()
     )
 );
public function getDataTypes(ModelInterface $model): array

Devuelve los atributos y sus tipos de datos

print_r(
    $metaData->getDataTypes(
        new Invoices()
    )
);
public function getDataTypesNumeric(ModelInterface $model): array

Devuelve los atributos con tipos numéricos

print_r(
    $metaData->getDataTypesNumeric(
        new Invoices()
    )
);
public function getEmptyStringAttributes(
    ModelInterface $model
): array

Devuelve atributos que permiten cadenas vacías

print_r(
    $metaData->getEmptyStringAttributes(
        new Invoices()
    )
);
public function getIdentityField(ModelInterface $model): string

Devuelve el nombre del campo identidad (si hay uno presente)

print_r(
    $metaData->getIdentityField(
        new Invoices()
    )
);
public function getNonPrimaryKeyAttributes(
    ModelInterface $model
): array

Devuelve un vector de campos que no forman parte de la clave primaria

print_r(
    $metaData->getNonPrimaryKeyAttributes(
        new Invoices()
    )
);
public function getNotNullAttributes(ModelInterface $model): array

Devuelve un vector de atributos no nulos

print_r(
    $metaData->getNotNullAttributes(
        new Invoices()
    )
);
public function getPrimaryKeyAttributes(
    ModelInterface $model
): array

Devuelve un vector de campos que forman parte de la clave primaria

print_r(
    $metaData->getPrimaryKeyAttributes(
        new Invoices()
    )
);
public function getReverseColumnMap(
    ModelInterface $model
): array

Devuelve el mapa de columnas inverso si existe

print_r(
    $metaData->getReverseColumnMap(
        new Invoices()
    )
);
public function getStrategy(): StrategyInterface

Devuelve la estrategia para obtener los metadatos

public function hasAttribute(
    ModelInterface $model, 
    string $attribute
): bool

Comprueba si un modelo tiene cierto atributo

print_r(
    $metaData->hasAttribute(
        new Invoices(),
        "inv_title"
    )
);
public function isEmpty(): bool

Comprueba si el contenedor de metadatos interno está vacío

print_r(
    $metaData->isEmpty()
);
public function read(string $key): array | null

Lee los metadatos del adaptador

final public function readColumnMap(
    ModelInterface $model
): array | null

Lee el mapa de columnas ordenado/inverso para cierto modelo

print_r(
    $metaData->readColumnMap(
        new Invoices()
    )
);
final public function readColumnMapIndex(
    ModelInterface $model, 
    int $index
)

Lee información del mapa de columnas para cierto modelo usando la constante MODEL_*

print_r(
    $metaData->readColumnMapIndex(
        new Invoices(),
        MetaData::MODELS_REVERSE_COLUMN_MAP
    )
);
final public function readMetaData(ModelInterface $model): array

Lee los metadatos completos para cierto modelo

print_r(
    $metaData->readMetaData(
        new Invoices()
    )
);
final public function readMetaDataIndex(
    ModelInterface $model, 
    int $index
)

Lee los metadatos para cierto modelo

print_r(
    $metaData->readMetaDataIndex(
        new Invoices(),
        0
    )
);
public function reset(): void

Resetea los metadatos internos para regenerarlos

 $metaData->reset();
public function setAutomaticCreateAttributes(
    ModelInterface $model, 
    array $attributes
): void

Establece los atributos que se deben ignorar en la generación SQL del INSERT

$metaData->setAutomaticCreateAttributes(
    new Invoices(),
    [
        "inv_created_at" => true,
    ]
);
public function setAutomaticUpdateAttributes(
    ModelInterface $model, 
    array $attributes
): void

Establece los atributos que se deben ignorar en la generación SQL del UPDATE

$metaData->setAutomaticUpdateAttributes(
    new Invoices(),
    [
        "inv_updated_at" => true,
    ]
);
public function setEmptyStringAttributes(
    ModelInterface $model, 
    array $attributes
): void

Establece los atributos que permiten valores de cadena vacía

$metaData->setEmptyStringAttributes(
    new Invoices(),
    [
        "inv_title" => true,
    ]
);
public function setStrategy(StrategyInterface $strategy): void

Establece la estrategia de extracción de metadatos

public function write(string $key, array $data): void

Escribe los metadatos al adaptador

final public function writeMetaDataIndex(
    ModelInterface $model, 
    int $index, 
    mixed $data
): void

Escribe metadatos para cierto modelo usando una constante MODEL_*

print_r(
    $metaData->writeColumnMapIndex(
        new Invoices(),
        MetaData::MODELS_REVERSE_COLUMN_MAP,
        [
            "title" => "inv_title",
        ]
    )
);
final protected function initialize(
    ModelInterface $model, 
    mixed $key, 
    mixed $table, 
    mixed $schema
)

Inicializa los metadatos para cierta tabla

Adaptadores

Obtener los metadatos es una operación de base de datos costosa y ciertamente no queremos ejecutarla cada vez que se ejecute una consulta. Sin embargo, podemos usar uno de los muchos adaptadores disponibles para almacenar en caché los metadatos.

NOTE: For local development, the Phalcon\Mvc\Models\MetaData\Memory adapter is recommended so that any changes to the database can be reflected immediately.

Adaptador Descripción
Phalcon\Mvc\Models\MetaData\Apcu This adapter uses the Alternative PHP Cache (APC) to store the table metadata. (producción)
Phalcon\Mvc\Models\MetaData\Libmemcached This adapter uses the Memcached Server to store the table metadata. (producción)
Phalcon\Mvc\Models\MetaData\Memory Este adaptador usa la memoria. Los metadatos se almacenan en caché sólo durante la petición. (desarrollo)
Phalcon\Mvc\Models\MetaData\Redis This adapter uses Redis to store the table metadata. (producción)
Phalcon\Mvc\Models\MetaData\Stream Este adaptador usa ficheros planos para almacenar los metadatos. (no para producción)

APCu

This adapter uses the Alternative PHP Cache (APC) to store the table metadata. La extensión debe estar presente en su sistema para que funcione este caché de metadatos. Si el servidor se reinicia, los datos se perderán. Este adaptador es apropiado para aplicaciones en producción.

The adapter receives a Phalcon\Cache\AdapterFactory class in order to instantiate the relevant cache object. También puede pasar un vector con opciones adicionales para que el caché opere.

El prefijo predeterminado es ph-mm-apcu- y el tiempo de vida es 172.000 (48 horas).

<?php

use Phalcon\Cache\AdapterFactory;
use Phalcon\Di\FactoryDefault;
use Phalcon\Mvc\Model\MetaData\Apcu;
use Phalcon\Storage\SerializerFactory;

$container = new FactoryDefault();
$container->set(
    'modelsMetadata',
    function () {
        $serializerFactory = new SerializerFactory();
        $adapterFactory    = new AdapterFactory($serializerFactory);
        $options = [
            'lifetime' => 86400,
            'prefix'   => 'my-prefix',
        ];

        return new Apcu($adapterFactory, $options);
    }
);

Libmemcached

This adapter uses the Memcached Server to store the table metadata. La extensión debe estar presente en su sistema para que funcione este caché de metadatos. Este adaptador es apropiado para aplicaciones en producción.

The adapter receives a Phalcon\Cache\AdapterFactory class in order to instantiate the relevant cache object. También puede pasar un vector con opciones adicionales para que el caché opere.

El prefijo predeterminado es ph-mm-memc- y el tiempo de vida es 172.000 (48 horas). persistenId está preconfigurado a php-mm-mcid-.

<?php

use Phalcon\Cache\AdapterFactory;
use Phalcon\Di\FactoryDefault;
use Phalcon\Mvc\Model\MetaData\Libmemcached;
use Phalcon\Storage\SerializerFactory;

$container = new FactoryDefault();
$container->set(
    'modelsMetadata',
    function () {
        $serializerFactory = new SerializerFactory();
        $adapterFactory    = new AdapterFactory($serializerFactory);
        $options = [
            'servers' => [
                0 => [
                    'host'   => '127.0.0.1',
                    'port'   => 11211,
                    'weight' => 1
                ],   
            ],
            'lifetime' => 86400,
            'prefix'   => 'my-prefix',
        ];

        return new Libmemcached($adapterFactory, $options);
    }
);

Memory

Este adaptador usa la memoria del servidor para almacenar el caché de metadatos. El cache está disponible sólo durante la petición, y después el caché se pierde. Este caché es más apropiado para desarrollo, ya que se adapta a los cambios frecuentes en la base de datos durante el desarrollo.

<?php

use Phalcon\Di\FactoryDefault;
use Phalcon\Mvc\Model\MetaData\Memory;

$container = new FactoryDefault();
$container->set(
    'modelsMetadata',
    function () {
        return new Memory();
    }
);

Redis

This adapter uses the Redis to store the table metadata. La extensión debe estar presente en su sistema para que funcione este caché de metadatos. Este adaptador es apropiado para aplicaciones en producción.

The adapter receives a Phalcon\Cache\AdapterFactory class in order to instantiate the relevant cache object. También puede pasar un vector con opciones adicionales para que el caché opere.

El prefijo predeterminado es ph-mm-reds- y el tiempo de vida es 172.000 (48 horas).

<?php

use Phalcon\Cache\AdapterFactory;
use Phalcon\Di\FactoryDefault;
use Phalcon\Mvc\Model\MetaData\Redis;
use Phalcon\Storage\SerializerFactory;

$container = new FactoryDefault();
$container->set(
    'modelsMetadata',
    function () {
        $serializerFactory = new SerializerFactory();
        $adapterFactory    = new AdapterFactory($serializerFactory);
        $options = [
            'host'     => '127.0.0.1',
            'port'     => 6379,
            'index'    => 1,
            'lifetime' => 86400,
            'prefix'   => 'my-prefix',
        ];

        return new Redis($adapterFactory, $options);
    }
);

Flujo (Stream)

Este adaptador usa el sistema de ficheros para almacenar los metadatos de la tabla. Este adaptador es apropiado para aplicaciones de producción pero no es recomendable ya que introduce un aumento de E/S.

El adaptador puede aceptar una opción metaDataDir con un directorio donde almacenar los metadatos. El directorio por defecto es el directorio actual.

<?php

use Phalcon\Di\FactoryDefault;
use Phalcon\Mvc\Model\MetaData\Stream;

$container = new FactoryDefault();
$container->set(
    'modelsMetadata',
    function () {
        $options = [
            'metaDataDir' => '/app/storage/cache/metaData',
        ];

        return new Stream($options);
    }
);

Puede usar la opción orm.exception_on_failed_metadata_save en su fichero php.ini para forzar que el componente lance una excepción si hay algún error almacenando los metadatos o si el directorio destino no es escribible.

orm.exception_on_failed_metadata_save = true

Estrategias

La estrategia por defecto para obtener los metadatos del modelo es la introspección de la base de datos. Utilizando esta estrategia, el esquema de información se usa para identificar los campos de una tabla, su clave primaria, campos nulos, tipos de datos, etc.

<?php

use Phalcon\Cache\AdapterFactory;
use Phalcon\Di\FactoryDefault;
use Phalcon\Mvc\Model\MetaData\Apcu;
use Phalcon\Mvc\Model\MetaData\Strategy\Introspection;
use Phalcon\Storage\SerializerFactory;

$container = new FactoryDefault();
$container->set(
    'modelsMetadata',
    function () {
        $serializerFactory = new SerializerFactory();
        $adapterFactory    = new AdapterFactory($serializerFactory);
        $options = [
            'lifetime' => 86400,
            'prefix'   => 'my-prefix',
        ];

        $metadata = new Apcu($adapterFactory, $options);
        $metadata->setStrategy(new Introspection());

        return $metadata;
    }
);

Introspección

Esta estrategia no necesita ninguna personalización y se usa implícitamente por todos los adaptadores de metadatos.

Anotaciones

Esta estrategia hace uso de anotaciones para describir las columnas de un modelo.

<?php

use Phalcon\Mvc\Model;

class Invoices extends Model
{
    /**
     * @Primary
     * @Identity
     * @Column(type='integer', nullable=false)
     */
    public $inv_id;

    /**
     * @Column(type='integer', nullable=false)
     */
    public $inv_cst_id;

    /**
     * @Column(type='string', length=70, nullable=false)
     */
    public $inv_title;

    /**
     * @Column(type='double', nullable=false)
     */
    public $inv_total;
}

Las anotaciones deben colocarse en propiedades que se asignan a columnas en la fuente mapeada. Las propiedades sin la anotación @Column se gestionan como atributos de clase simples.

Se soportan las siguientes anotaciones:

Nombre Descripción
@Primary Marca el campo como parte de la clave primaria de la tabla
@Identity El campo es una columna auto_increment/serial
@Column Esto marca un atributo como una columna mapeada

La anotación @Column soporta los siguientes parámetros:

Nombre Descripción
column Nombre de la columna real
type El tipo de la columna: char, biginteger, blob, boolean, date, datetime, decimal, integer, float, json, longblob, mediumblob, timestamp, tinyblob, text, varchar/string (predeterminado)
length El tamaño de la columna si hay
nullable Establece si la columna acepta valores null o no
skip_on_insert Omite esta columna al insertar
skip_on_update Omite esta columna al actualizar
allow_empty_string La columna permite cadenas vacías
default Valor por defecto

La estrategia de anotaciones se podría configurar de la siguiente manera:

<?php

use Phalcon\Cache\AdapterFactory;
use Phalcon\Di\FactoryDefault;
use Phalcon\Mvc\Model\MetaData\Apcu;
use Phalcon\Mvc\Model\MetaData\Strategy\Annotations;
use Phalcon\Storage\SerializerFactory;

$container = new FactoryDefault();
$container->set(
    'modelsMetadata',
    function () {
        $serializerFactory = new SerializerFactory();
        $adapterFactory    = new AdapterFactory($serializerFactory);
        $options = [
            'lifetime' => 86400,
            'prefix'   => 'my-prefix',
        ];

        $metadata = new Apcu($adapterFactory, $options);
        $metadata->setStrategy(new Annotations());

        return $metadata;
    }
);

Manual

Usando las estrategias de introspección presentadas anteriormente, Phalcon puede obtener los metadatos de cada modelo automáticamente. Sin embargo, tiene la opción de definir los metadatos manualmente. Esta estrategia anula cualquier estrategia que se haya configurado en el gestor de metadatos. Las columnas añadidas, modificadas o eliminadas de la tabla mapeada se deben actualizar manualmente en el modelo para que todo funcione correctamente.

Para configurar los metadatos, usamos el método metaData en un modelo:

<?php

use Phalcon\Mvc\Model;
use Phalcon\Db\Column;
use Phalcon\Mvc\Model\MetaData;

class Invoices extends Model
{
    public function metaData()
    {
        return array(
            MetaData::MODELS_ATTRIBUTES => [
                'inv_id',
                'inv_cst_id',
                'inv_status_flag',
                'inv_title',
                'inv_total',
                'inv_created_at',
                'inv_created_by',
                'inv_updated_at',
                'inv_updated_by',
            ],

            MetaData::MODELS_PRIMARY_KEY => [
                'inv_id',
            ],

            MetaData::MODELS_NON_PRIMARY_KEY => [
                'inv_cst_id',
                'inv_status_flag',
                'inv_title',
                'inv_total',
                'inv_created_at',
                'inv_created_by',
                'inv_updated_at',
                'inv_updated_by',
            ],

            MetaData::MODELS_NOT_NULL => [
                'inv_id',
                'inv_cst_id',
                'inv_status_flag',
                'inv_title',
                'inv_total',
                'inv_created_at',
                'inv_created_by',
                'inv_updated_at',
                'inv_updated_by',

            MetaData::MODELS_DATA_TYPES => [
                'inv_id'          => Column::TYPE_INTEGER,
                'inv_cst_id'      => Column::TYPE_INTEGER,
                'inv_status_flag' => Column::TYPE_INTEGER,
                'inv_title'       => Column::TYPE_VARCHAR,
                'inv_total'       => Column::TYPE_FLOAT,
                'inv_created_at'  => Column::TYPE_DATETIME,
                'inv_created_by'  => Column::TYPE_INTEGER,
                'inv_updated_at'  => Column::TYPE_DATETIME,
                'inv_updated_by'  => Column::TYPE_INTEGER,
            ],

            MetaData::MODELS_DATA_TYPES_NUMERIC => [
                'inv_id'          => true,
                'inv_cst_id'      => true,
                'inv_status_flag' => true,
                'inv_total'       => true,
                'inv_created_by'  => true,
                'inv_updated_by'  => true,
            ],

            MetaData::MODELS_IDENTITY_COLUMN => 'inv_id',

            MetaData::MODELS_DATA_TYPES_BIND => [
                'inv_id'          => Column::BIND_PARAM_INT,
                'inv_cst_id'      => Column::BIND_PARAM_INT,
                'inv_status_flag' => Column::BIND_PARAM_INT,
                'inv_title'       => Column::BIND_PARAM_INT,
                'inv_total'       => Column::BIND_PARAM_DECIMAL,
                'inv_created_at'  => Column::BIND_PARAM_STR,
                'inv_created_by'  => Column::BIND_PARAM_INT,
                'inv_updated_at'  => Column::BIND_PARAM_STR,
                'inv_updated_by'  => Column::BIND_PARAM_INT,
            ],

            MetaData::MODELS_AUTOMATIC_DEFAULT_INSERT => [
                'inv_created_at' => true,
                'inv_created_by' => true,
                'inv_updated_at' => true,
                'inv_updated_by' => true,
            ],

            MetaData::MODELS_AUTOMATIC_DEFAULT_UPDATE => [
                'inv_created_at' => true,
                'inv_created_by' => true,
                'inv_updated_at' => true,
                'inv_updated_by' => true,
            ],

            MetaData::MODELS_DEFAULT_VALUES => [
                'inv_status_flag' => 0,
            ],

            MetaData::MODELS_EMPTY_STRING_VALUES => [
                'inv_created_at' => true,
                'inv_updated_at' => true,
            ],
        );
    }
}

Personalizado

Phalcon offers the Phalcon\Mvc\Model\MetaData\Strategy\StrategyInterface interface, allowing you to create your own Strategy class.

<?php

namespace MyApp\Components\Strategy;

use Phalcon\Mvc\ModelInterface;
use Phalcon\Di\DiInterface;

class MyStrategy StrategyInterface
{
    public function getColumnMaps(
        ModelInterface $model, 
        DiInterface $container
    ): array;

    public function getMetaData(
        ModelInterface $model, 
        DiInterface $container
    ): array;
}