initphp/database

InitPHP 数据库(QueryBuilder、DBAL 和 ORM)库

3.0 2023-12-09 20:45 UTC

README

使用或不用抽象管理数据库。这个库基于PHP PDO插件构建,主要用于构建和执行SQL查询。

Latest Stable Version Total Downloads Latest Unstable Version License PHP Version Require

要求

  • PHP 8.0 及以上。
  • PHP PDO 扩展。

支持的数据库

这个库应该在几乎使用基本SQL语法的任何数据库中都能正常工作。PDO支持的数据库和合适的驱动程序可以在https://php.ac.cn/manual/en/pdo.drivers.php找到。

安装

composer require initphp/database

用法

QueryBuilder & DBAL 和 CRUD

require_once "vendor/autoload.php";
use \InitPHP\Database\DB;

// Connection
DB::createImmutable([
    'dsn'       => 'mysql:host=localhost;port=3306;dbname=test;charset=utf8mb4',
    'username'  => 'root',
    'password'  => '',
    'charset'   => 'utf8mb4',
    'collation' => 'utf8mb4_general_ci',
]);

创建

use \InitPHP\Database\DB;
$data = [
    'title'     => 'Post Title',
    'content'   => 'Post Content',
];

$isInsert = DB::create('post', $data);

/**
* This executes the following query.
* 
* INSERT INTO post 
* (title, content) 
* VALUES 
* ("Post Title", "Post Content");
*/
if($isInsert){
    // Success
}
创建批量
use \InitPHP\Database\DB;

$data = [
    [
        'title'     => 'Post Title 1',
        'content'   => 'Post Content 1',
        'author'    => 5
    ],
    [
        'title'     => 'Post Title 2',
        'content'   => 'Post Content 2'
    ],
];

$isInsert = DB::createBatch('post', $data);

/**
* This executes the following query.
* 
* INSERT INTO post 
* (title, content, author) 
* VALUES 
* ("Post Title 1", "Post Content 1", 5),
* ("Post Title 2", "Post Content 2", NULL);
*/

if($isInsert){
    // Success
}

读取

use \InitPHP\Database\DB;


/**
* This executes the following query.
* 
* SELECT user.name AS author_name, post.id, post.title 
* FROM post, user 
* WHERE user.id = post.author AND post.status = 1
* ORDER BY post ASC, post.created_at DESC
* LIMIT 20, 10
*/

/** @var \InitORM\DBAL\DataMapper\Interfaces\DataMapperInterface $res */
$res = DB::select('user.name as author_name', 'post.id', 'post.title')
    ->from('post')
    ->selfJoin('user', 'user.id=post.author')
    ->where('post.status', 1)
    ->orderBy('post.id', 'ASC')
    ->orderBy('post.created_at', 'DESC')
    ->offset(20)->limit(10)
    ->read();
    
if($res->numRows() > 0){
    $results = $res->asAssoc()
                    ->rows();
    foreach ($results as $row) {
        echo $row['title'] . ' by ' . $row['author_name'] . '<br />';
    }
}

更新

use \InitPHP\Database\DB;
$data = [
    'title'     => 'New Title',
    'content'   => 'New Content',
];

$isUpdate = DB::where('id', 13)
                ->update('post', $data);
    
/**
* This executes the following query.
* 
* UPDATE post 
* SET title = "New Title", content = "New Content"
* WHERE id = 13
*/
if ($isUpdate) {
    // Success
}
更新批量
use \InitPHP\Database\DB;
$data = [
    [
        'id'        => 5,
        'title'     => 'New Title #5',
        'content'   => 'New Content #5',
    ],
    [
        'id'        => 10,
        'title'     => 'New Title #10',
    ]
];

$isUpdate = DB::where('status', '!=', 0)
                ->updateBatch('id', 'post', $data);
    
/**
* This executes the following query.
* 
* UPDATE post SET 
* 	title = CASE 
* 		WHEN id = 5 THEN 'New Title #5' 
* 		WHEN id = 10 THEN 'New Title #10' 
* 		ELSE title END, 
* 	content = CASE 
* 		WHEN id = 5 THEN 'New Content #5'
* 		ELSE content END 
* WHERE status != 0 AND id IN (5, 10)
*/
if ($isUpdate) {
    // Success
}

删除

use \InitPHP\Database\DB;

$isDelete = DB::where('id', 13)
                ->delete('post');
    
/**
* This executes the following query.
* 
* DELETE FROM post WHERE id = 13
*/
if ($isUpdate) {
    // Success
}

原始

use \InitPHP\Database\DB;

/** @var \InitORM\DBAL\DataMapper\Interfaces\DataMapperInterface $res */
$res = DB::query("SELECT id FROM post WHERE user_id = :id", [
    ':id'   => 5
]);

原始构建器

use \InitPHP\Database\DB;

/** @var \InitORM\DBAL\DataMapper\Interfaces\DataMapperInterface $res */
$res = DB::select(DB::raw("CONCAT(name, ' ', surname) AS fullname"))
        ->where(DB::raw("title = '' AND (status = 1 OR status = 0)"))
        ->limit(5)
        ->read('users');
        
/**
 * SELECT CONCAT(name, ' ', surname) AS fullname 
 * FROM users 
 * WHERE title = '' AND (status = 1 OR status = 0)
 * LIMIT 5
 */
$results = $res->asAssoc()
                ->rows();
foreach ($results as $row) {
    echo $row['fullname'];
}

使用不同的连接

这个库的开发思路是您将使用单个数据库和连接,但我知道在某些项目中您需要处理多个连接和数据库。

如果您想使用非全局连接,请使用 connect() 方法。

use \InitPHP\Database\DB;

DB::connect([
    'dsn'       => 'mysql:host=localhost;port=3306;dbname=test;charset=utf8mb4',
    'username'  => 'root',
    'password'  => '',
    'charset'   => 'utf8mb4',
    'collation' => 'utf8mb4_general_ci',
]);

模型和实体

模型和实体是数据库抽象中常用的两个概念。以下是最粗略的解释;

  • 模型:每个模型是一个代表数据库表中一个表的类。
  • 实体:实体是一个代表数据单行的类。

最基础的模型类示例可能如下所示。

namespace App\Model;

class Posts extends \InitPHP\Database\Model
{

    /**
    * If your model will use a connection other than your global connection, provide connection information.
    * @var array|null <p>Default : NULL</p> 
    */
    protected array $credentials = [
        'dsn'               => '',
        'username'          => 'root',
        'password'          => '',
        'charset'           => 'utf8mb4',
        'collation'         => 'utf8mb4_unicode_ci',
    ];

    /**
     * If not specified, \InitPHP\Database\Entity::class is used by default.
     * 
     * @var string<\InitPHP\Database\Entity>
     */
    protected $entity = \App\Entities\PostEntity::class;

    /**
     * If not specified, the name of your model class is used.
     * 
     * @var string
     */
    protected string $schema = 'posts';

    /**
     * The name of the PRIMARY KEY column. If not, define it as NULL.
     * 
     * @var string
     */
    protected string $schemaId = 'id';

    /**
     * Specify FALSE if you want the data to be permanently deleted.
     * 
     * @var bool
     */
    protected bool $useSoftDeletes = true;

    /**
     * Column name to hold the creation time of the data.
     * 
     * @var string|null
     */
    protected ?string $createdField = 'created_at';

    /**
     * The column name to hold the last time the data was updated.
     * 
     * @var string|null
     */
    protected ?string $updatedField = 'updated_at';

    /**
     * Column name to keep deletion time if $useSoftDeletes is active.
     * 
     * @var string|null
     */
    protected ?string $deletedField = 'deleted_at';

    protected bool $readable = true;

    protected bool $writable = true;

    protected bool $deletable = true;

    protected bool $updatable = true;
    
}

最基础的实体类示例可能如下所示。

namespace App\Entities;

class PostEntity extends \InitPHP\Database\Entity 
{
    /**
     * An example of a getter method for the "post_title" column.
     * 
     * Usage : 
     * echo $entity->post_title;
     */
    public function getPostTitleAttribute($title)
    {
        return strtoupper($title);
    }
    
    /**
     * An example of a setter method for the "post_title" column.
     * 
     * Usage : 
     * $entity->post_title = 'New Post Title';
     */
    public function setPostTitleAttribute($title)
    {
        $this->post_title = strtolower($title);
    }
    
}

开发工具

以下列举了一些您可以在开发和开发后使用的开发者工具。

日志记录器

use \InitPHP\Database\DB;

DB::createImmutable([
    'dsn'       => 'mysql:host=localhost;dbname=test;port=3306;charset=utf8mb4;',
    'username'  => 'root',
    'password'  => '',
    
    'log'       => __DIR__ '/logs/db.log', // string, callable or object
]);

如果您将文件路径定义为字符串,则尝试使用 file_put_contents() 写入。

注意:您可以在文件名中定义如 {year}{month}{day} 等变量。

  • 您还可以使用 critical 方法定义一个对象。数据库库将日志消息作为参数传递给此方法。或者将其定义为可调用的数组,以使用对象的任何方法。
use \InitPHP\Database\DB;

class Logger {
    
    public function critical(string $msg)
    {
        $path = __DIR__ . '/log.log';
        file_put_contents($path, $msg, FILE_APPEND);
    }

}

$logger = new Logger();

DB::createImmutable([
    'dsn'       => 'mysql:host=localhost;dbname=test;port=3306;charset=utf8mb4;',
    'username'  => 'root',
    'password'  => '',
    
    'log'       => $logger, // or [$logger, 'critical']
]);
  • 同样,您也可以将其定义为可调用的方法。
use \InitPHP\Database\DB;

DB::createImmutable([
    'dsn'       => 'mysql:host=localhost;dbname=test;port=3306;charset=utf8mb4;',
    'username'  => 'root',
    'password'  => '',
    
    'log'       => function (string $msg) {
        $path = __DIR__ . '/log.log';
        file_put_contents($path, $msg, FILE_APPEND);
    },
]);

调试模式

调试模式用于在错误消息中包含执行的SQL语句。 它只应在开发环境中激活

use \InitPHP\Database\DB;

DB::createImmutable([
    'dsn'       => 'mysql:host=localhost;dbname=test;port=3306;charset=utf8mb4;',
    'username'  => 'root',
    'password'  => '',
    
    'debug'     => true, // boolean
]);

分析模式

分析模式是v3及以上版本中可用的一项开发者工具。它是一种功能,允许您查看执行的查询及其执行时间。

use InitPHP\Database\DB;

DB::enableQueryLog();

DB::table('users')->where('name', 'John')->read();

var_dump(DB::getQueryLogs());

/**
 * The output of the above example looks like this;
 * [
 *      [
 *          'query' => 'SELECT * FROM users WHERE name = :name',
 *          'time'  => '0.00064',
 *          'args'  => [
 *              ':name'     => 'John',
 *          ]
 *      ]
 * ]
 * 
 */

待办事项

  • 将准备更详细的文档。

获取帮助

如果您有任何问题、疑虑、错误报告等,请在该存储库的问题跟踪器中提交问题。

参与项目

本项目所有贡献将根据MIT许可证发布。通过提交拉取请求或提交错误、问题或功能请求,您同意遵守此版权放弃声明。

帮助项目的主要方式有两种

  • 使用问题跟踪器,
  • 更改代码库。

使用问题跟踪器

使用问题跟踪器提出功能请求、报告错误和提问。这也是与项目开发人员以及其他对此解决方案感兴趣的人建立联系的好方法。

使用问题跟踪器找到贡献的方法。找到错误或功能,在问题中说明您将承担这项工作,然后遵循以下更改代码库的指南。

更改代码库

一般来说,您应该fork这个仓库,在自己的fork中进行修改,然后提交pull request。所有新的代码都应该有相关的单元测试,以验证实现的功能和缺陷的存在或不存在。此外,代码应遵循项目规定的任何风格和架构指南。如果没有这样的指南,应模仿现有代码库中的风格和模式。

致谢

许可协议

版权所有 © 2022 MIT许可协议