Fuentes de datos


1Presentación

Temma ofrece un mecanismo para gestionar el acceso a las fuentes de datos, facilitando la conexión a ellas y su uso de manera unificada, permitiendo a la vez recurrir a las capacidades específicas de cada fuente.

El funcionamiento unificado significa que la mayoría de las fuentes de datos se pueden usar de la misma manera, para escribir y leer pares clave/valor. Las fuentes de datos usadas de esta manera se pueden intercambiar (por ejemplo, un acceso Memcache reemplazado por MySQL o Redis; o una cola de mensajes Beanstalkd reemplazada de forma transparente por AWS SQS).


2Configuración

Para abrir una conexión a una fuente de datos, hay que configurarla en el archivo etc/temma.php (ver la documentación de configuración).

<?php

return [
    // configuración de la aplicación
    'application' => [
        'dataSources' => [
            'db'      => 'mysql://user:passwd@localhost/my_base',
            'ndb'     => 'redis://localhost',
            'cache'   => 'memcache://localhost',
            'file'    => 'file:///var/data/temma',
            's3'      => 's3://ACCESS_KEY:SECRET_KEY@REGION/my_bucket',
            'sqs'     => 'sqs://ACCESS_KEY:SECRET_KEY@my_queue',
            'mq'      => 'beanstalk://localhost/my_queue',
            'sms'     => 'smsmode://API_KEY',
            'slack'   => 'slack://hooks.slack.com/services/TXX/BYY/ZZ',
            'discord' => 'discord://discord.com/api/webhooks/ABC/XYZ',
            'gchat'   => 'googlechat://chat.googleapis.com/v1/spaces/XXXXXXX/messages?key=YYYYYYY&token=ZZZZZZZ',
            'tg'      => 'telegram://API_TOKEN',
            'push'    => 'pushover://APP_TOKEN',
            'mail'    => 'smtp+tls://user:pass@smtp.example.com:587',
            'myDS1'   => '[MyDataSource]mydsn://SOME_PARAM',
            'myDS2'   => '[\Namespace\OtherDataSource]otherdsn://OTHER_PARAM',
        ]
    ]
];
  • Línea 7: Conexión a MySQL.
  • Línea 8: Conexión a Redis.
  • Línea 9: Conexión a Memcache.
  • Línea 10: Acceso a archivos locales.
  • Línea 11: Conexión a Amazon S3.
  • Línea 12: Conexión a Amazon SQS.
  • Línea 13: Conexión a Beanstalkd.
  • Línea 14: Conexión a smsmode.
  • Línea 15: Conexión a Slack.
  • Línea 16: Conexión a Discord.
  • Línea 17: Conexión a Google Chat.
  • Línea 18: Conexión a Telegram.
  • Línea 19: Conexión a Pushover.
  • Línea 20: Conexión a un relay SMTP.
  • Línea 21: Usa el objeto MyDataSource como fuente de datos.
  • Línea 22: Usa el objeto \Namespace\OtherDataSource como fuente de datos.

Consulta la documentación de cada fuente de datos para saber más sobre su configuración.

En los controladores, las fuentes de datos son directamente accesibles usando los nombres definidos en la configuración: $this->db, $this->cache, $this->s3, etc.

Los objetos que acceden al componente de inyección de dependencias deben pasar por el registro dataSources, que se puede usar con escritura orientada a objetos o como array:

$db = $this->_loader->dataSources->db;
$db = $this->_loader->dataSources['db'];


3Acceso unificado

Las fuentes de datos ofrecen una serie de métodos comunes para acceder a pares clave/valor. Algunos métodos no siempre están disponibles (por ejemplo, Memcache, Amazon SQS o Beanstalkd no permiten buscar entre los datos almacenados), pero los principios son en gran medida los mismos.

Hay tres conjuntos de métodos:

  1. Métodos generales (averiguar si un dato existe, eliminar datos, etc.).
  2. Métodos para leer y escribir datos complejos. Los datos se serializan (generalmente en formato JSON, a menos que la fuente de datos disponga de su propio mecanismo de serialización).
  3. Métodos para leer y escribir datos en bruto. Estos métodos manipulan cadenas que se almacenan directamente.

Por simplicidad, se puede acceder a los datos complejos usando la sintaxis de array asociativo.


4Acceso de tipo tabla

4.1Existencia de datos

Usa la función isset().

if (isset($this->source['key1']))
    TµLog::l("El dato 'key1' existe.");

4.2Lectura de datos

El dato se deserializa.

$data = $this->source['key1'];

4.3Escritura de datos

El dato se serializa.

$this->source['key1'] = $data;

4.4Eliminación de datos

Usa la función unset().

unset($this->source['key1']);

4.5Obtener el número de datos almacenados

Usa la función count().

// número total de elementos
$cnt = count($this->source);

// número de elementos que coinciden con un patrón
$cnt = $this->source->count('user:');

5Métodos generales

5.1isSet()

isSet(string $key) : bool

Devuelve true si la clave $key hace referencia a un dato que existe.

Ejemplo:

// Comprobar si la clave "key1" existe
if (!$this->db->isSet('key1'))
    TµLog::l("Dato desconocido.");

// escritura alternativa
if (!isset($this->db['key1']))
    TµLog::l("Dato desconocido.");

5.2remove()

remove(string $key) : void

Elimina el dato referenciado por la clave $key.

Ejemplo:

// elimina la clave "key1"
$this->cache->remove('key1');

5.3mRemove()

mRemove(array $keys) : void

Elimina todos los datos cuyas claves figuran en el array $keys.

Ejemplo:

// elimina las claves "key1" y "key2"
$this->cache->mRemove(['key1', 'key2']);

5.4clear()

clear(string $pattern) : void

Elimina todos los datos cuya clave corresponde al parámetro proporcionado. Según el funcionamiento de la fuente de datos, el parámetro puede ser un prefijo o una máscara de expresión regular.

Ejemplo:

// elimina todos los archivos del directorio "images"
$this->files->clear('images/*');

5.5flush()

flush() : void

Elimina todos los datos almacenados en la fuente de datos.

Ejemplo:

// elimina todos los archivos almacenados en un bucket de Amazon S3
$this->s3->flush();

6Gestión de datos complejos serializados

search(string $pattern, bool $getValues=false, null|bool|string|array $sort=null, int $offset=0, int $limit=0) : array

Devuelve la lista de claves correspondientes al primer parámetro. Según el funcionamiento de la fuente de datos, el parámetro puede ser un prefijo o una máscara de expresión regular.

Si el segundo parámetro se pone a true, la lista devuelta contiene los valores de datos (deserializados) asociados a cada clave.

El tercer parámetro $sort ordena los resultados: null para el orden natural (por defecto), true para el orden inverso, false para un orden aleatorio, o un nombre de campo (prefijado con - para orden descendente; también se acepta una lista de campos). El ordenamiento por nombre de campo solo lo soportan las fuentes de datos que lo gestionan.

Los parámetros $offset y $limit permiten paginar los resultados. $limit puesto a 0 significa "sin límite".

Ejemplos:

// recupera la lista de nombres de archivo del directorio "users"
$users = $this->files->search('users/*');
foreach ($users as $login)
    TµLog::l("Usuario: $login");

// recupera la lista de archivos del directorio "users",
// con su contenido deserializado
$users = $this->files->search('users/*', true);
foreach ($users as $path => $user) {
    $login = mb_substr($path, mb_strlen('users/'));
    TµLog::l("Usuario  : $login");
    TµLog::l("Edad     : " . $user['age']);
    TµLog::l("Dirección: " . $user['address']);
}

// recupera los primeros 10 resultados
$users = $this->files->search('users/*', false, null, 0, 10);

// recupera los siguientes 10 resultados, con sus valores
$users = $this->files->search('users/*', true, null, 10, 10);

6.2get()

get(string $key, mixed $defaultOrCallback=null, mixed $options=null) : mixed

Devuelve el dato (deserializado) cuya clave se proporciona como primer parámetro.

Si el dato no existe en la fuente de datos, se usa el segundo parámetro:

  • si contiene un dato escalar, este se usa como valor por defecto y lo devuelve el método;
  • si contiene una función, esta se ejecuta. Su valor de retorno se añade a la fuente de datos, asociado a la clave proporcionada, y lo devuelve el método. En este caso, el tercer parámetro puede recibir opciones que se usan para almacenar el dato.

Ejemplos:

// recupera un dato en caché
$colors = $this->cache->get('app:colors');

// escritura alternativa
$color = $this->cache['app:colors'];

// idem, pero si el dato no está en caché, se devuelve un valor por defecto
$colors = $this->cache->get('app:colors', ['blue', 'red', 'green']);
// $colors = ['blue', 'red', 'green'];

// Recupera un dato en caché. Si el dato no está en caché, se llama a una función
// para obtener el dato de la base de datos (mediante un DAO). El dato
// se guarda en caché y se devuelve. Estará disponible para accesos posteriores
// a la caché.
$user = $this->cache->get($userId, function() use ($userId) {
    return $this->_dao->get($userId);
});

6.3mGet()

mGet(array $keys) : array

Recibe una lista de claves como parámetro, y devuelve un array asociativo con las claves y su contenido (deserializado).

Ejemplo:

// recupera los datos (deserializados) de las claves "key1" y "key2"
$data = $this->db->mGet(['key1', 'key2']);
foreach ($data as $key => $datum) {
    TµLog::l("Nombre: '$key' - tamaño: '{$datum['size']}'.");
}

6.4set()

set(string $key, mixed $value=null, mixed $options=null) : mixed

Escribe un dato (serializado) en la fuente de datos.

  • 1er parámetro: Clave del dato que se creará o actualizará.
  • 2o parámetro: Contenido del dato (escalar o complejo).
  • 3er parámetro: Opción usada para el registro, según el tipo de fuente de datos.

Ejemplo:

// añade un dato a la caché
$this->cache->set('app:color', 'blue');

// escritura alternativa
$this->cache['app:color'] = 'blue';

// crea un archivo de texto en Amazon S3 con derechos de lectura pública
$this->s3->set('text/introduction.txt', $text, [
    'public'   => true,
    'mimetype' => 'text/plain',
]);

6.5mSet()

mSet(array $data, mixed $options=null) : int

Escribe varios datos (serializados).

  • 1er parámetro: Array asociativo cuyas claves son los identificadores de los datos que se escribirán, y cuyos valores asociados son los contenidos de los datos que se escribirán.
  • 2o parámetro: Opción usada para todos los registros, según el tipo de fuente de datos.

Ejemplo:

// escribe varias claves (serializadas) en una base de datos Redis
$this->ndb->mSet([
    'key1' => 'value 1',
    'key2' => ['value 2.1', 'value 2.2', 'value 2.3'],
    'key3' => [
        'key3.1' => 'value 3.1',
        'key3.2' => 'value 3.2',
    ],
]);

7Gestión de datos en bruto

7.1find()

find(string $pattern, bool $getValues=false, null|bool|string|array $sort=null, int $offset=0, int $limit=0) : array

Devuelve la lista de claves correspondientes al primer parámetro. Según el funcionamiento de la fuente de datos, el parámetro puede ser un prefijo o una máscara de expresión regular.

Si el segundo parámetro se pone a true, la lista devuelta contiene los valores de datos (en bruto) asociados a cada clave.

Si el segundo parámetro se pone a false, este método es idéntico al método search().

El tercer parámetro $sort ordena los resultados: null para el orden natural (por defecto), true para el orden inverso, false para un orden aleatorio, o un nombre de campo (prefijado con - para orden descendente; también se acepta una lista de campos). El ordenamiento por nombre de campo solo lo soportan las fuentes de datos que lo gestionan.

Los parámetros $offset y $limit permiten paginar los resultados. $limit puesto a 0 significa "sin límite".

Ejemplos:

// recupera la lista de nombres de archivo del directorio "images"
$keys = $this->files->find('images/*');
foreach ($keys as $key)
    TµLog::l("Archivo: '$key'.");

// recupera la lista de archivos del directorio "images",
// con su contenido en bruto
$keys = $this->files->find('images/*', true);
foreach ($keys as $key => $value) {
    $size = mb_strlen($value, 'ascii');
    TµLog::l("Archivo: '$key' - tamaño: '$size'.");
}

// recupera los primeros 20 resultados
$keys = $this->files->find('images/*', false, null, 0, 20);

// recupera los siguientes 20 resultados, con sus valores
$keys = $this->files->find('images/*', true, null, 20, 20);

7.2read()

read(string $key, mixed $defaultOrCallback=null, mixed $options=null) : mixed

Devuelve el dato cuya clave se proporciona como primer parámetro.

Si el dato no existe en la fuente de datos, se usa el segundo parámetro:

  • si contiene un dato escalar, este se usa como valor por defecto y lo devuelve el método;
  • si contiene una función, esta se ejecuta. Su valor de retorno se añade a la fuente de datos, asociado a la clave proporcionada, y lo devuelve el método. En este caso, el tercer parámetro puede recibir opciones que se usan para almacenar el dato.

Ejemplo:

// Recuperación de un archivo CSS en caché. Si el archivo no está en caché,
// se lee y se añade a la caché con un tiempo de vida de 10 minutos.
$user = $this->cache->read('style.css', function() {
    $css = file_get_contents('/path/to/style.css');
    return ($css);
}, 600);

7.3mRead()

mRead(array $keys) : array

Recibe una lista de claves como parámetro, y devuelve un array asociativo con las claves y su contenido.

Ejemplo:

// lee varios datos en bruto de Redis
$data = $this->ndb->mRead(['key1', 'key2', 'key3']);

7.4copyFrom()

copyFrom(string $key, string $localPath, mixed $defaultOrCallback=null, mixed $options=null) : bool

Recupera un dato y lo guarda en un archivo local.

Si el dato no existe, el tercer y el cuarto parámetros se usan para devolver un valor por defecto o para generar el dato (ver el método read).

Ejemplo:

// recupera un archivo de Amazon S3 y lo guarda localmente
$this->s3->copyFrom('images/user1.jpg', '/var/data/img/login.jpg');

7.5mCopyFrom()

mCopyFrom(array $keys) : int

Recibe como parámetro un array asociativo cuyas claves son los identificadores de los datos que se recuperarán, y cuyos valores asociados son las rutas de los archivos en los que se escribirán los datos.

Ejemplo:

// recupera varios archivos de Amazon S3 y los guarda localmente
$this->s3->mCopyFrom([
    'images/user1.jpg' => '/var/data/img/login1.jpg',
    'images/user2.jpg' => '/var/data/img/login2.jpg',
]);

7.6write()

write(string $key, string $value, mixed $options=null) : mixed

Escribe un dato en la fuente de datos.

  • 1er parámetro: Clave del dato que se creará o actualizará.
  • 2o parámetro: Contenido textual (o binario) del dato.
  • 3er parámetro: Posible opción usada para el registro, según el tipo de fuente de datos.

Ejemplos:

// escribe un dato en un servidor Redis
$this->ndb->write('linux:year', '1991');

// escribe un dato en Redis, con un tiempo de vida de 10 minutos
$this->ndb->write('linux:year', '1991', 600);

7.7mWrite()

mWrite(array $data, mixed $options=null) : int

Escribe varios datos.

  • 1er parámetro: Array asociativo cuyas claves son los identificadores de los datos que se escribirán, y cuyos valores asociados son los contenidos de los datos que se escribirán.
  • 2o parámetro: Opción usada para todos los registros, según el tipo de fuente de datos.

Ejemplo:

// escribe varios datos en un servidor Redis
$this->ndb->mWrite([
    'qnx'      => '1984',
    'nextstep' => '1988',
    'solaris'  => '1990',
    'linux'    => '1991',
]);

7.8copyTo()

copyTo(string $key, string $localPath, mixed $options=null) : mixed

Escribe un dato en la fuente de datos, copiando el contenido de un archivo local.

  • 1er parámetro: Clave del dato que se creará o actualizará.
  • 2o parámetro: Ruta del archivo de origen.
  • 3er parámetro: Posible opción usada para el registro, según el tipo de fuente de datos.

Ejemplo:

// copia un archivo local a Amazon S3
$this->s3->copyTo('css/style.css', '/var/data/css/style.css');

7.9mCopyTo()

mCopyTo(array $data, mixed $options=null) : int

Escribe varios conjuntos de datos, copiando el contenido de varios archivos locales.

  • 1er parámetro: Array asociativo cuyas claves son los identificadores de los datos que se escribirán, y cuyos valores asociados son las rutas de los archivos de origen.
  • 2o parámetro: Opción usada para todos los registros, según el tipo de fuente de datos.

Ejemplo:

// copia varios archivos locales a Amazon S3
$this->s3->mCopyTo([
    'css/style.css'    => '/var/data/css/style.css',
    'js/app.js'        => '/var/data/js/app.js',
    'images/login.jpg' => '/var/data/img/login.jpg',
]);

8Gestión de la conexión

Normalmente no necesitas gestionar las conexiones. En el primer acceso (lectura o escritura), el objeto se conecta a la fuente de datos. Cuando se destruye el objeto, la desconexión es automática.

Sin embargo, hay casos raros en los que quizá quieras abrir o cerrar la conexión explícitamente. Los métodos connect(), reconnect() y disconnect() están ahí para eso.

No todas las fuentes de datos gestionan conexiones y desconexiones. Por ejemplo, la fuente File no realiza ninguna conexión (accede a los archivos deseados al leer o escribir). Los métodos connect/disconnect se pueden llamar igualmente, pero no hacen nada.


8.1connect()

connect() : void

Se conecta a la fuente de datos.

Si la conexión ya se ha establecido, no ocurre nada.


8.2reconnect()

reconnect() : void

Se reconecta a la fuente de datos.

Si la conexión no estaba abierta, se abrirá. Si ya había una conexión abierta, se cierra primero antes de volver a abrirse.


8.3disconnect()

disconnect() : void

Cierra la conexión a la fuente de datos.