osvaldogdelrio/pdo-database

PHP类,用于处理PDO

v1.0 2021-09-12 21:21 UTC

This package is auto-updated.

Last update: 2024-09-13 04:39:23 UTC


README

Scrutinizer Code Quality Build Status Code Intelligence Status Code Coverage Travis Build Status

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();