osvaldogdelrio / pdo-database
PHP类,用于处理PDO
Requires
- osvaldogdelrio/factory: dev-main
Requires (Dev)
- phpunit/phpunit: ^9.5
- scrutinizer/ocular: dev-master
README
pdo-database
用PHP编写的用于处理PDO和SQL数据库的库,文档用西班牙语编写。
目前包含基本功能
SELECT JOIN ORDER LIMIT ORDER-LIMIT INSERT UPDATE DELETE TRUNCATE
安装
用PHP 8.0编写
要求:PSR-4:自动加载器,以实现更好的功能而无需重写命名空间
通过composer
composer require osvaldogdelrio/pdo-database
虽然可以使用不使用此方法,但推荐使用Factory以获得更好的使用体验
composer require osvaldogdelrio/factory
不使用Factory使用时,需要大量代码,这在实现模型、控制器和视图的项目中可能会感到繁琐。要传统地建立一个连接,需要做以下操作
$conexion = new ConexionBaseDeDatos( new HostBaseDeDatos('127.0.0.1'), new BaseDeDatos('test'), new UsuarioBaseDeDatos('root'), new ContrasenaBaseDeDatos('') ); $conexion = $conexion->conectar(); /* $conexion Es el objeto PDO con el que interactuará el resto de la librería */
使用Factory和CrearConexionBaseDeDatos类来执行
/* La clase src/pdodatabase/conexion/CrearConexionBaseDeDatos.php contiene los datos de conexión o se pueden implementar con un require_once o pasarlos por medio del array */ $conexion = $factory->crear('CrearConexionBaseDeDatos',[]); $conexion = $conexion->conectar(); /* $conexion Es el objeto PDO con el que interactuará el resto de la librería */
要执行一个“SELECT * FROM id WHERE Idusuario = 1”的查询并显示结果数组,不使用Factory,需要
$conexion = new ConexionBaseDeDatos( new HostBaseDeDatos('127.0.0.1'), new BaseDeDatos('test'), new UsuarioBaseDeDatos('root'), new ContrasenaBaseDeDatos('') ); $conexion = $conexion->conectar(); $consulta = new ConsultaSelectWhere( new EjecutarConsultaConDatos( $conexion ), new SentenciaSelectWhere( new CamposYTabla( new Campos(['*']), new Tabla('id') ), new Como( new Where( new ValidadorDeParametrosWhere( ['idusuario','=',1] ) ) ) ) ); $resultados = new ResultadoEnArrays; $resultados = $resultados->resultado($consulta->obtener()); print_r($resultado); /* array */
库的测试所需的表格在test.sql中可用
- 数据库:test
- 用户:root
- 密码
- 主机:127.0.0.1
使用Factory的代码示例列表
SELECT
SELECT * FROM prueba
$select = $factory->crear('src\factory\Select',[ 'tabla' => 'prueba', 'campos' => ['*'] ]);
SELECT id,uno,dos FROM prueba
$select = $factory->crear('src\factory\Select',[ 'tabla' => 'prueba', 'campos' => ['id','uno','dos'] ]);
WHERE子句中接受的逻辑运算符,AND和OR
在构建查询时,将验证逻辑运算符是否为以下之一
=
>
<
>=
<=
<>
!=
!<
!>
LIKE
IN
WHERE子句构建的验证规则
- 列名只能为字符串
- 必须是有效的逻辑运算符
- 任何值都不能为空
WHERE AND和WHERE OR的验证规则
- 必须是有效的逻辑运算符
- 任何值都不能为空
- 字段只能为字符串
BETWEEN和NOT BETWEEN的验证规则
- 值不能为空
- 值不能相等,例如(WHERE id BETWEEN 1 AND 1)
SELECT * FROM prueba WHERE id = ?
$select = $factory->crear('src\factory\SelectWhere',[ 'tabla' => 'prueba', 'campos' => ['*'], 'where' => ['id','=','1'] ]);
SELECT * FROM prueba WHERE id = ? AND uno != ?
$select = $factory->crear('src\factory\SelectWhereAnd',[ 'tabla' => 'prueba', 'campos' => ['*'], 'where' => ['id','=','1','uno','!=','1'] ]);
SELECT * FROM prueba WHERE id = ? OR uno != ?
$select = $factory->crear('src\factory\SelectWhereOr',[ 'tabla' => 'prueba', 'campos' => ['*'], 'where' => ['id','=','1','uno','!=','1'] ]);
SELECT * FROM prueba WHERE id BETWEEN ? AND ?
$select = $factory->crear('src\factory\SelectWhereBetween',[ 'tabla' => 'prueba', 'campos' => ['*'], 'where' => ['id','1','10'] ]);
SELECT * FROM prueba WHERE id NOT BETWEEN ? AND ?
$select = $factory->crear('src\factory\SelectWhereNotBetween',[ 'tabla' => 'prueba', 'campos' => ['*'], 'where' => ['id','1','10'] ]);
INSERT
INSERT INTO prueba (uno,dos,tres) VALUES (?,?,?)
$select = $factory->crear('src\factory\Insert',[ 'tabla' => 'prueba', 'valores' => ['uno' => 123, 'dos' => 1234, 'tres' => 12345] ]);
UPDATE
UPDATE prueba SET uno = ?,dos = ? WHERE id = ?
$select = $factory->crear('src\factory\Update',[ 'tabla' => 'prueba', 'valores' => ['uno' => 1, 'dos' => 2], 'where' => ['id','=',1] ]);
UPDATE prueba SET uno = ? WHERE id = ? AND id = ?
$select = $factory->crear('src\factory\UpdateWhereAnd',[ 'tabla' => 'prueba', 'valores' => ['uno' => 1], 'where' => ['id','=',1,'id','=',6] ]);
UPDATE prueba SET uno = ? WHERE id = ? OR id = ?
$select = $factory->crear('src\factory\UpdateWhereOr',[ 'tabla' => 'prueba', 'valores' => ['uno' => 1], 'where' => ['id','=',1,'id','=',6] ]);
UPDATE prueba SET uno = ? WHERE BETWEEN id ? AND ?
$select = $factory->crear('src\factory\UpdateWhereBetween',[ 'tabla' => 'prueba', 'valores' => ['uno' => 1], 'where' => ['id',1,5] ]);
UPDATE prueba SET uno = ? WHERE NOT BETWEEN id ? AND ?
$select = $factory->crear('src\factory\UpdateWhereNotBetween',[ 'tabla' => 'prueba', 'valores' => ['uno' => 1], 'where' => ['id',1,5] ]);
DELETE
不允许没有 WHERE, WHERE AND, WHERE OR, WHERE BETWEEN 或 WHERE NOT BETWEEN 的语句,如果要删除一个表中的所有内容,请使用 TRUNCATE
DELETE FROM prueba WHERE id = ?
$select = $factory->crear('src\factory\Delete',[ 'tabla' => 'prueba', 'where' => ['id','=',1] ]);
DELETE FROM prueba WHERE id = ? AND id = ?
$select = $factory->crear('src\factory\DeleteWhereAnd',[ 'tabla' => 'prueba', 'where' => ['id','=',1,'id','=',3] ]);
DELETE FROM prueba WHERE BETWEEN id ? AND ?
$select = $factory->crear('src\factory\DeleteWhereBetween',[ 'tabla' => 'prueba', 'where' => ['id',1,3] ]);
DELETE FROM prueba WHERE NOT BETWEEN id ? AND ?
$select = $factory->crear('src\factory\DeleteWhereNotBetween',[ 'tabla' => 'prueba', 'where' => ['id',1,100] ]);
JOIN
支持 INNER, RIGHT, LEFT, FULL
SELECT prueba.*,prueba2.uno AS columnauno FROM prueba INNER JOIN prueba2 ON prueba.uno = prueba2.uno
$join = $factory->crear('src\factory\Join', [ 'tabla' => 'prueba', //La tabla principal 'campos' => ['*'], // Los campos de la tabla principal 'join' => [ [ 'tipo' => 'inner', //Tipo de Join: inner, left, right, full 'tabla' => 'prueba2', // Tabla que se va a unir 'campos' => ['uno AS columnauno'], //Los campos de la tabla que se va a unir 'key' => ['uno'] // El nombre de columna con el cual se va a enlazar ] ] ])
JOIN 的查询可能非常复杂,这个库可以相对简单地构建大量的 JOIN,其结构简单,JOIN 的基本元素放置在一个数组中,它可以有多个 JOIN 到内部表,例如,在以下 ARRAY 中构建的查询表示
SELECT prueba.*,prueba5.cinco AS columnacinco,prueba4.cuatro AS columnacuatro,prueba3.dos AS columnados,prueba2.uno AS columnauno FROM prueba INNER JOIN prueba2 ON prueba.uno = prueba2.uno INNER JOIN prueba3 ON prueba.dos = prueba3.dos INNER JOIN prueba4 ON prueba3.cuatro = prueba4.cuatro INNER JOIN prueba5 ON prueba4.cinco = prueba5.cinco
最终,没有简单的方法来构建一个像 JOIN 这样长且不引起混淆的语句,但是,能够以几乎无限的方式使用数组来构建它们是非常有用的。
$datos = [ 'tabla' => 'prueba', 'campos' => ['*'], 'join' => [ [ 'tipo' => 'inner', 'tabla' => 'prueba2', 'campos' => ['uno AS columnauno'], 'key' => ['uno'] ], [ 'tipo' => 'inner', 'tabla' => 'prueba3', 'campos' => ['dos AS columnados'], 'key' => ['dos'], 'join' => [ [ 'tipo' => 'inner', 'tabla' => 'prueba4', 'campos' => ['cuatro AS columnacuatro'], 'key' => ['cuatro'], 'join' => [ [ 'tipo' => 'inner', 'tabla' => 'prueba5', 'campos' => ['cinco AS columnacinco'], 'key' => ['cinco'] ] ] ] ] ] ] ]; $select = $factory->crear('src\factory\Join', $datos);
我们可以把这个查询分解成不同的数组
$tabla5 = [ 'tipo' => 'inner', 'tabla' => 'prueba5', 'campos' => ['cinco AS columnacinco'], 'key' => ['cinco'] ]; $tabla4 = [ 'tipo' => 'inner', 'tabla' => 'prueba4', 'campos' => ['cuatro AS columnacuatro'], 'key' => ['cuatro'], 'join' => $tabla5 ]; $tabla3 = [ 'tipo' => 'inner', 'tabla' => 'prueba3', 'campos' => ['dos AS columnados'], 'key' => ['dos'], 'join' => $tabla4 ]; $tabla2 = [ 'tipo' => 'inner', 'tabla' => 'prueba2', 'campos' => ['uno AS columnauno'], 'key' => ['uno'] ]; $datos = [ 'tabla' => 'prueba', 'campos' => ['*'], 'join' => $tabla2, $tabla3 ]; /*El campo que le indica a la clase que debe de crear otro JOIN que tiene como tabla una diferente a la principal es: 'join' => [ // lo que esté dentro de este array se considera un JOIN a la tabla indicada en el mismo array del que sale. [ 'tipo' => '' tipo del join a ejecutar 'tabla'=> '', Nombre d ela tabla 'campos' => [], Valores ilimitados separados por comas (,) o un simple '*' para seleccionar todo 'key' => [] Máximo 2 valores permitidos ], [ Más datos indicando otro JOIN ] ]; Si los nombres de columna en donde se realiza el enlace de las dos tablas no son iguales, el valor a escribir en el array es: ['nombre_de_columna_tabla_padre','nombre_de_columna_tabla_hijo'] Si son iguales en ambas tablas solo un valor: ['nombre de columna'] */
JOIN WHERE
SELECT prueba.*,prueba5.cinco AS columnacinco,prueba4.cuatro AS columnacuatro,prueba3.dos AS columnados,prueba2.uno AS columnauno FROM prueba INNER JOIN prueba2 ON prueba.uno = prueba2.uno INNER JOIN prueba3 ON prueba.dos = prueba3.dos INNER JOIN prueba4 ON prueba3.cuatro = prueba4.cuatro INNER JOIN prueba5 ON prueba4.cinco = prueba5.cinco WHERE prueba.id = ?
$datos = [ 'tabla' => 'prueba', 'campos' => ['*'], 'where' => ['prueba.id','=',1], 'join' => [ [ 'tipo' => 'inner', 'tabla' => 'prueba2', 'campos' => ['uno AS columnauno'], 'key' => ['uno'] ], [ 'tipo' => 'inner', 'tabla' => 'prueba3', 'campos' => ['dos AS columnados'], 'key' => ['dos'], 'join' => [ [ 'tipo' => 'inner', 'tabla' => 'prueba4', 'campos' => ['cuatro AS columnacuatro'], 'key' => ['cuatro'], 'join' => [ [ 'tipo' => 'inner', 'tabla' => 'prueba5', 'campos' => ['cinco AS columnacinco'], 'key' => ['cinco'] ] ] ] ] ] ] ]; $select = $factory->crear('src\factory\JoinWhere', $datos);
同样,我们可以在 JOIN 中添加 WHERE AND, WHERE OR, WHERE BETWEEN 和 WHERE NOT BETWEEN,只需修改数组中的 'where' => 内容即可
// Se agrega al array en el espacio where 6 datos 'nombre_de_columna','operador_lógico','valor','nombre_de_columna','operador_lógico','valor' $select = $factory->crear('src\factory\JoinWhereAnd', $datos); $select = $factory->crear('src\factory\JoinWhereOr', $datos); // Se colocan los 3 datos 'nombre_de_columna','valor1','valor2' $select = $factory->crear('src\factory\JoinWhereBetween', $datos); $select = $factory->crear('src\factory\JoinWhereNotBetween', $datos);
ORDER
要在 SELECT 类型的语句中添加 ORDER BY,只需调用以 OrderBy 结尾的 Factory 类即可
SELECT * FROM prueba ORDER BY id ASC
$datos = [ 'tabla' => 'prueba', 'campos' => ['*'], 'order' => 'id ASC' ]; $select = $factory->crear('src\factory\SelectOrderBy', $datos);
要实现 SELECT WHERE ORDER BY 查询,类是 SelectWhereOrderBy,所有以 OrderBy 结尾的类都将接受数组中的 'order' 参数
LIMIT
要在 SELECT 类型的语句中添加 LIMIT,只需调用以 Limit 结尾的 Factory 类即可
SELECT * FROM prueba LIMIT 1
$datos = [ 'tabla' => 'prueba', 'campos' => ['*'], 'limit' => '1' // Limit tiene que ser numérico pero String ]; $select = $factory->crear('src\factory\SelectLimit', $datos);
要实现 SELECT WHERE LIMIT 查询,类是 SelectWhereLimit,所有以 Limit 结尾的类都将接受数组中的 'limit' 参数
ORDER-LIMIT
可以将 ORDER BY '列名' LIMIT '行数' 的语句添加到任何 Select 或 Join 类中,并包括数组中的两个字段
SELECT * FROM prueba ORDER BY id LIMIT 10
$datos = [ 'tabla' => 'prueba', 'campos' => ['*'], 'order' => 'id', 'limit' => '10' // Limit tiene que ser numérico pero String ]; $select = $factory->crear('src\factory\SelectOrderByLimit', $datos);
TRUNCATE
删除表中的所有数据而不删除表
$truncate = $factory->crear('src\factory\Truncate',['tabla'=>'prueba']);
执行查询
在执行每个 SELECT、UPDATE、INSERT、DELETE、JOIN 或其他查询时
$select->obtener();
SELECT 结果
计算 SELECT 的结果
/* ejecutamos la consulta $consulta = $select->obtener(); */ $resultado = new ContarResultados; $resultado->contar($consulta);
返回对象
$resultadoObj = new ResultadoEnObjetos; $resultadoObj->resultado($consulta);
返回数组
$resultadoArray = new ResultadoEnArrays; $resultadoArray->resultado($consulta);
返回 JSON 格式的字符串
$resultadoJson = new ResultadoEnJson; $resultadoJson->resultado($consulta);
INSERT 结果
ContarResultados 返回由查询执行影响的行数
$insert = $factory->crear('src\factory\Insert',[ 'tabla' => 'prueba', 'valores' => ['uno' => 1234, 'dos' => 1234, 'tres' => 1548] ]); $resultado = new ContarResultados; var_dump($resultado->contar($insert->obtener())); // OUTPUT 1
目录结构
pdo-database
+-- src
| +-- excepciones //Contiene todas las Excepciones a lanzar
| +-- factory //Contiene las clases para generar las peticiones a SQL sin mucho código
| +-- interfaces //Contiene las interfaces de todo el proyecto
| +-- pdodatabase //Contiene todas las clases para construir y hacer las peticiones
| | +-- conexion //Contiene las clases para crear la conexión
| | +-- consultas //Contiene las clases para realizar las consultas
| | +-- select
| +-- sql //Contiene clases para ejecutar varios tipos de consultas SQL
| +-- update
| +-- insert
| +-- delete
| | +-- ejecutar //Contiene las clases para ejecutar las consultas
| | +-- elementos //Contiene las clases para construir las sentencias
| | +-- resultados //Contiene las clases para mostrar resultados
| | +-- sentencias //Contiene las clases para construir las sentencias en texto
| | +-- select
| +-- sql //Contiene clases para construir varias sentencias SQL
| +-- update
| +-- insert
| +-- delete
+-- test //Contiene las pruebas realizadas en PHPUnit
+-- test.sql //Archivo SQL que contiene la creación de las tablas necesarias para las pruebas
使用示例
<?php declare(strict_types=1); require_once __DIR__ . '/vendor/autoload.php'; use src\Factory; use src\pdodatabase\resultados\ContarResultados; use src\pdodatabase\resultados\ResultadoEnObjetos; $factory = new Factory; //Realizando la consulta por medio de Factory // SELECT * FROM prueba $select = $factory->crear('src\factory\Select',[ 'tabla' => 'prueba', 'campos' => ['*'] ]); $select = $select->obtener(); /* Contando los resultados de la consulta */ $numeroDeResultados = new ContarResultados; $numeroDeResultados = $numeroDeResultados->contar($select); /* Resultados con la misma consulta */ $resultadoObj = new ResultadoEnObjetos; $resultadoObj->resultado($select);
PHP Unit
用于执行测试
./vendor/bin/phpunit test
用于执行测试并使用 --testdox 显示文本
./vendor/bin/phpunit test --testdox
库有测试,可以在 pdo-database/test 中找到。它包含连接所需类、执行查询的类、直接执行查询的类、构建 SQL 语句元素的类、减少代码书写的 Factory 类以及以不同格式显示结果的类的单元测试。
- 连接所需类 - ConexionTest.php
- 执行查询的类 - ConsultasTest.php
- 直接执行查询的类 - EjecutarConsultaTest.php
- 构建 SQL 语句元素的类 - ElementosTest.php
- 减少代码书写的 Factory 类 - FactoryTest.php
- 以不同格式显示结果的类 - ResultadosTest.php
理念
主要理念是封装 SQL 语句的值,并停止使用可能产生异常行为的 IF 语句、SETTERS 和 GETTERS。使用面向对象编程,将 SQL 语句视为 SELECT * FROM 这样的对象,该对象在创建后应保持不可变。
为什么有这么多小对象和类?
在我尝试在我的个人项目中使用面向对象编程的过程中,我遇到了一个问题,那就是我不得不一次又一次地修改代码,因为需要在某处移动一行,尤其是当涉及到与数据库交互的类时。通过创建这种结构,我尽可能地按照SOLID原则进行最小化实现,这是一项相当困难的任务,但总是可以不断改进。
例如:如果需要执行一个简单的查询,如SELECT * FROM 'table',为什么要加载处理更复杂查询的组件类?或者在一个类中就能执行select、update、insert、delete、truncate以及create操作?最终,写出只具有一个功能的类并注入到其他类中以构建更复杂的可用性,对于从小型到中型再到大型项目的增长来说,可以节省大量的时间和精力。
组件类大部分都是对环境无关的,也就是说,构建SQL语句的类可以在不与生成连接的类交互的情况下工作等。
使用直接语句的查询
尽管不推荐在不使用bindValue或封装查询以获得安全性(确保我们写的查询就是将要执行的查询,以预防SQL注入)的情况下执行直接查询,但在开发环境中,这样做是有用的,可以这样做:
/* Obtenemos al conexión */ $conexion = $factory->crear('CrearConexionBaseDeDatos',[]); $conexion = $conexion->conectar(); /* Al ser el objeto PDO usamos query y pasamos el parametro */ $query = $conexion->query("SELECT * FROM prueba"); $query->execute();