piecesphp / database
Clases que interactuan con bases de datos.
Requires
- php: >=8.5 <8.6
- ext-ctype: *
- ext-date: *
- ext-fileinfo: *
- ext-json: *
- ext-mbstring: *
- ext-mysqli: *
- ext-pcre: *
- ext-pdo: *
- ext-pdo_mysql: *
- ext-pdo_sqlite: *
- ext-sqlite3: *
- ext-zip: *
Requires (Dev)
- nikic/php-parser: ^5.5
- phpstan/phpstan: ^2.1
- phpstan/phpstan-deprecation-rules: ^2.0
- phpunit/phpunit: ^10.5
- rector/rector: ^2.3
Suggests
None
Provides
None
Conflicts
None
Replaces
None
- dev-master
- v5.3.0
- v5.2.0
- v5.1.1
- v5.1.0
- v5.0.0
- v4.1.0
- v4.0.0
- v3.9.0
- v3.8.1
- v3.8.0
- v3.7.0
- v3.6.0
- v3.5.0
- v3.4.0
- v3.3.0
- v3.2.1
- v3.2.0
- v3.1.0
- v3.0.4
- v3.0.3
- v3.0.2
- v3.0.1
- v3.0.0
- v2.6.3
- v2.6.2
- v2.6.1
- v2.6.0
- v2.5.10
- v2.5.9
- v2.5.8
- v2.5.7
- v2.5.6
- v2.5.5
- v2.5.4
- v2.5.3
- v2.5.2
- v2.5.1
- v2.5
- v2.4
- v2.3
- v2.2
- v2.1
- v2.0.2
- v2.0.1
- v2.0.0
- v2.0.0-beta
- v1.9.5
- v1.9.4
- v1.9.1
- v1.9.0.4
- v1.9.0.3
- v1.9.0.2
- v1.9.0.1
- v1.9
- 1.8.1
- v1.8
- v1.7
- v1.6.3
- v1.6.2
- v1.6.1
- v1.6.0.1
- v1.6.0
- v1.5.4
- v1.5.3
- v1.5.2
- v1.5.1
- v1.5
- v1.4.43
- v1.4.42
- v1.4.41
- v1.4.4
- v1.4.3
- v1.4.2
- v1.4.1
- v1.4
- v1.1
- v1.0.1
- v1
- dev-dev
- dev-branch-v2.0.1
This package is auto-updated.
Last update: 2026-10-08 22:25:42 UTC
README
Acceso a datos para PHP sobre PDO: la conexión, un constructor de consultas encadenable, mappers de entidad
declarativos y la generación del CREATE TABLE a partir de ellos.
Es la capa de datos del framework PiecesPHP, pero no depende de él.
Requisitos
- PHP
>=8.5 <8.6. - Extensiones:
pdoconpdo_mysql(opdo_sqlite),mbstring,json,ctype,pcre,date,fileinfo,mysqli,sqlite3,zip. - Sin dependencias de Composer en ejecución.
Instalación
composer require piecesphp/database
Qué trae
Todo bajo PiecesPHP\Core\Database:
| Clase | Qué es |
|---|---|
Database | La conexión. Extiende \PDO, siempre en modo excepción |
ORM\ActiveRecord | El constructor de consultas: select, insert, update, delete, where, join, having, groupBy, orderBy |
ActiveRecordModel | ActiveRecord configurado con un array de opciones y un almacén de conexiones compartido |
EntityMapper | Una fila como objeto: declara $table y $fields, y carga, guarda y actualiza |
ORM\Statements\WhereSegment, Critery\WhereItem, WhereItemGroup | Criterios con marcadores, incluidos grupos (a OR b) AND c |
ORM\Statements\HavingSegment, Critery\HavingItem, HavingItemGroup | Lo mismo para HAVING |
SchemeCreator | Genera (y opcionalmente ejecuta) el SQL de la tabla de un EntityMapper |
Exceptions\DatabaseClassesExceptions | La excepción de la biblioteca |
⚠ Seguridad: la forma de texto va tal cual al SQL
where('texto'),having('texto'),join($tabla, 'texto'),groupBy()yorderBy()insertan el texto literalmente. Nunca metas ahí un valor que venga del usuario.- Un valor del usuario va en la forma de array (
where(['columna' => $valor])) o en unWhereSegment: viaja por marcador. - Desde la 5.0.0 el texto se guarda y se lee tal cual, sin
addslashes/stripslashes. Si vienes de la 4.x, lee el CHANGELOG. getCompiledSQL(true)es para registrar y depurar, nunca para ejecutar. Para envolver una consulta en otra —un recuento—, usagetPreparedSQL(), que da el texto con sus marcadores, y ejecútalo con los valores enlazados (getReplaceWhereAndHavingValues()en unSELECT). Ejemplo en el CHANGELOG, versión 5.3.0.
Uso
Conectar
use PiecesPHP\Core\Database\Database;
$db = Database::instanceMySQL('mi_base', 'usuario', 'clave', 'localhost');
// o SQLite en memoria:
$db = Database::instance();
Consultar
use PiecesPHP\Core\Database\ORM\ActiveRecord;
$posts = new ActiveRecord($db, 'posts');
$posts->select(['id', 'title'])
->where([
'status' => 1, // status = :marcador
'createdAt' => ['>=' => '2026-01-01'], // createdAt >= :marcador
])
->orderBy('createdAt DESC');
if ($posts->execute(true, 1, 20)) { // asociativo, página 1, 20 por página
$filas = $posts->result(); // array|null
}
where()recibe un solo argumento.where('status', 1)no filtra por 1: el segundo argumento se ignora.execute()devuelvebool, no el resultado: se pide después conresult().execute()limpia la consulta al terminar.row()da la primera fila;get($valor, 'columna')busca por una columna.
Insertar, actualizar, borrar
$posts->insert(['title' => 'Hola'])->execute();
$id = $posts->lastInsertId();
$posts->update(['title' => 'Otro'])->where(['id' => $id])->execute();
$posts->delete(['id' => $id])->execute();
update() sin where() falla, salvo que se pida explícitamente con update($datos, true).
Un mapper
use PiecesPHP\Core\Database\EntityMapper;
class PostMapper extends EntityMapper
{
const TABLE = 'posts';
protected $table = self::TABLE;
protected $fields = [
'id' => ['type' => 'int', 'primary_key' => true],
'title' => ['type' => 'varchar', 'length' => 255],
'author' => [
'type' => 'int',
'reference_table' => 'users',
'reference_field' => 'id',
'human_readable_reference_field' => 'name',
],
];
}
PostMapper::setOptions(['driver' => 'mysql', 'database' => 'mi_base', 'user' => 'usuario', 'password' => 'clave']);
$post = new PostMapper(1); // carga la fila con id 1
$post->title = 'Editado';
$post->update();
$nuevo = new PostMapper();
$nuevo->title = 'Nuevo';
$nuevo->author = 1; // obligatorio: el campo no declara 'null' => true
$nuevo->save();
Tras save(), $nuevo->id ya tiene el id que generó la base (desde la 5.2.0), y un segundo save() del mismo objeto
actualiza esa fila. Se asigna solo a claves int o bigint que venían vacías. En SQLite, declara la clave como
INTEGER PRIMARY KEY.
Un campo es obligatorio salvo que declare 'null' => true: save() lanza DatabaseClassesExceptions si falta.
Opciones de un campo (con su valor por defecto): type (text), length, null, empty, default, raw,
primary_key, auto_increment, reference_table, reference_field, reference_primary_key,
human_readable_reference_field, has_many, mapper, representation_on_select_statement y meta.
Generar la tabla
use PiecesPHP\Core\Database\SchemeCreator;
$sql = (new SchemeCreator(new PostMapper()))->getSQL(); // solo el SQL de una tabla
// Varias tablas: createScript() y dropScript() reciben SchemeCreator, no mappers.
$creadores = [new SchemeCreator(new PostMapper())];
$sqlDeVarios = SchemeCreator::createScript($creadores); // padres antes que hijas
$bajada = SchemeCreator::dropScript($creadores); // el inverso
Si les pasas mappers en vez de
SchemeCreator, lanzanInvalidArgumentException(desde la 5.1.0).
Serializar un mapper
Un mapper se puede serializar (serialize(), caché, sesión, cola) sin llevar las credenciales de la base: desde la
5.1.0 no se incluyen. Al deserializarlo toma la conexión de EntityMapper::setOptions(). Si usaba una conexión propia,
vuelve a dársela después de unserialize().
Demos y pruebas
demos/: ejemplos por componente (active-record/,entity-mapper/,orm/,scheme-creator/), con las tablas de apoyo endemos/db/. Usan MySQL.unit-tests/: suites propias (no PHPUnit). Usan MySQL si conectan y, si no, SQLite en memoria.
composer install
php examples/readme.php # ejecuta los ejemplos de este README; sale con 1 si alguno no da lo que dice
bin/cli unit-tests --test="all"
Cambios
Ver CHANGELOG.md. La 5.0.0 rompe compatibilidad con los datos guardados por la 4.x.
Licencia
MIT. Ver LICENSE.