mreschke/dbal

Mreschke 辅助工具

5.3.2 2023-09-16 17:36 UTC

This package is auto-updated.

Last update: 2024-09-16 19:56:28 UTC


README

简介

这是一个基于 Laravel 的 dbal 辅助库和查询构建器,具有简单的基于类的实体集成。

在我在 Laravel 查询构建器或 Eloquent 之前以及我构建了更强大和功能丰富的 https://github.com/mreschke/repository 之前,我构建了这个。

它还有一些优点,例如

  • Mssql 二进制 GUID 转换
  • Mssql int 或 decimal 在 PHP 返回中转换为 int。
  • 在 MySQL PDO 设置 PDO::ATTR_EMULATE_PREPARES => false 和 PDO::ATTR_STRINGIFY_FETCHES => false 将返回 true 整数,但不适用于 MSSQL dblib 连接。
  • 跨/链接服务器支持
  • 简单且清晰的实体类集成(但不是实体映射)

但现在,由于我构建了 mreschke/repository,它主要用于旧版。它主要为我们的小型 SQL farm 构建,但同时也支持 MySQL。

这个 dbal 确实有一个“基本”查询构建器,但也被设计为抛出大量的原始查询...就像你在 SQL IDE 中做的那样。因此,它可以处理大量的完整和部分原始 SQL 查询(其中大多数都没有转义,请注意)。

如果你正在启动一个新项目,只需使用 Laravel 查询构建器或 Eloquent...或者更好的是,https://github.com/mreschke/repository ... 如果你想要具有一致性和实体映射 API 的任何高级功能,请使用 mreschke/repository。有关更多信息,请参阅以下内容基于实体的集成

集合信息

所有多返回,如 ->get()->all()getArray()getAssoc()...都将返回为 Laravel Illuminate\Support\Collection。这意味着您可以在 dbal 返回后执行许多类似的方法...不要将这些与 dbal 方法混淆。

集合使用示例

$customers = $this->db->query("SELECT * FROM customers")->all();
$customer->count(); //collection level count
$customer->first(); //collection level first
etc...

有关更多信息,请参阅 https://laravel.net.cn/api/master/Illuminate/Support/Collection.html

连接和实例化

Mreschke/dbal 是一个 Laravel 库,因此它利用了 Laravel config/database.php 中现有的数据库配置数组。默认情况下,如果您使用 Mreschke\Dbal\Mssql,则它将使用 sqlsrv 连接数组,如果您使用 Mreschke\Dbal\Mysql,则它将使用 mysql 连接数组。

要更改连接,就像 Laravel 查询构建器一样,请使用

$this->db->connection('othercon')->query(...)->get()

Mreschke/dbal 附带了 2 个外观,MysqlMssql。因此,您只需使用

Mssql::query('SELECT * FROM customers')->get()

但是,当然,首选的方法是通过构造函数中 Mreschke\Dbal\Mssql 的依赖注入。

基于实体的集成

您可以使用 mreschke/dbal 进行 即兴查询使用。即兴查询与 Laravel 查询构建器(非 Eloquent)同义。只需在需要时按需使用即可。

但是,当您将 mreschke/dbal 查询构建器与单个类关联时,会出现有趣的集成。

考虑一下您的“实体”类,例如 Sso\User.php。例如,该 User.php 类当然可以有它自己的方法和属性,但它也可以由连接到数据库后端的 mreschke/dbal 支持。您可以通过扩展 Builder 类以添加流畅的实体 API 来将此类与 mreschke/dbal 集成,如下所示

$sso->user->find(45); // provided by dbal
$sso->user->where('email', 'mail@example.com'); // provided by dbal
$sso->user->byServer(1); // custom method in our User.php class, but it uses dbal behind the scenes

因此,将基本的 PHP 类或“实体”对象转换为流畅的模型。就像 Eloquent 做的那样。

注意:我坚信实体应该像API一样被对待,因此应该保持一致性。如果您更改数据库列名,实体的返回值不应改变。在mreschke\dbal中,结果直接与数据库绑定,这对一致性是一个问题。这就是为什么我创建了https://github.com/mreschke/repository

Mreschke/repository是一个比仅仅将数据库与实体类集成更高级的实体系统。它添加了活动记录、列和实体映射抽象以及可替换的后端。如果您需要这种类型的实体集成,强烈建议使用mreschke/repository

对于临时查询,推荐使用Laravel查询构建器(非Eloquent),而对于实体管理和API,推荐使用mreschke/repository

以下是使用mreschke/dbal实现的SSO\User.php实体类示例

<?php

use Mreschke\Dbal\Builder;
use Mreschke\Dbal\Mssql as Dbal;

class User extends Builder
{
    /**
     * Database connection
     * @var Dbal
     */
    protected $db;

    /**
     * Create a new User instance
     * @param Dbal $db
     */
    public function __construct(Dbal $db)
    {
        $this->db = $db;
        $this->db->connection('sso');
        $this->configureBuilder();
    }

    /**
     * Configure the dbal query builder
     * @return void
     */
    public function configureBuilder()
    {
        $this->key = 'user_id';
        $this->select = ['tbl_user.*'];
        $this->from = 'tbl_user';
        $this->where = null;
        $this->groupBy = null;
        $this->having = null;
        $this->orderBy = 'email';
    }

    /**
     * Return the dbal database instance
     * @return DbalInterface
     */
    public function dbInstance() {
        return $this->db;
    }

    /**
     * Get one user by email address
     * Automatically appends @dynatronsoftware.com if no domain specified
     * @param  string $email
     * @return dbal resource
     */
    public function byEmail($email)
    {
        if (!str_contains($email, "@")) $email .= "@dynatronsoftware.com";
        $this->where("email = '$email'");
        $this->orderBy(null);
        return $this->execute();
    }

    /**
     * Get dealer server manager(s)
     * @param  int $dealerID dp dlr id
     * @return dbal resource
     */
    public function serviceManagers($dealerID)
    {
        $this->from('tbl_user
            INNER JOIN tbl_perm_group_link pgl on tbl_user.user_id = pgl.user_id
            INNER JOIN tbl_user_dealer_link udl on tbl_user.user_id = udl.user_id
        ');
        $this->where('tbl_user.disabled', false);
        $this->where('udl.dp_dlr_id', $dealerID);
        $this->where('pgl.group_id', 10); #10 = Service Manager
        $this->distinct();
        return $this->execute();
    }
}

因为这个类扩展了Builder,所以您将获得所有->find()->where()->select()->orderBy...方法,以及添加您自己的、利用构建器的自定义方法的能力,如上面的byEmail()serviceManager()方法。

即兴查询使用

Mreschke/dbal也非常适合临时查询。

<?php

use Mreschke\Dbal\Mssql;

function __construct(Mssql $db)
{
    // Dependency injection.  Facades are also available.
    $this->db = $db;
}

function rawQueries()
{
    // Get all as collection of objects
    $customers = $this->db->query("SELECT * FROM customers")->get(); // or all()

    // Get all as collection of arrays
    $customers = $this->db->query("SELECT * FROM customers")->getArray(); // or getAssoc()

    // Get all as key/value array
    $customers = $this->db->query("SELECT * FROM customers")->pluck('name', 'id');

    // Get first record as object
    $customers = $this->db->query("SELECT * FROM customers")->first();

    // Get first record as array
    $customers = $this->db->query("SELECT * FROM customers")->firstArray(); // or firstAssoc()

    // Get first column from first record (great for scalar queries)
    $customers = $this->db->query("SELECT TOP 1 name FROM customers")->pluck();

    // Get defined column from first record
    $customers = $this->db->query("SELECT TOP 1 * FROM customers")->value('adddress');

    // Count number of results
    // NOTICE: This will actually RUN the full query, so inefficient...
    // A SELECT count(*) is far more efficient.
    // So if you want the results too, get results, then count them yourself
    $count = $this->db->query("SELECT * FROM customers")->count(); // runs full query
    $count = $this->db->query("SELECT count(*) FROM customers")->value(); // db level, very efficient
    $customers = $this->db->query("SELECT * FROM customers")->get();
    count($customers) //or because collection, $customers->count();

    // Count number of columns
    $columnCount = $this->db->query("SELECT * FROM customers")->fieldCount();

    // Escape data for raw input
    $input = $this-db->escape($input);
    $this->db->query("INSERT INTO customers $input");
}

function queryBuilder()
{
    // Query build table does NOT work with databsae or schema names like DB.dbo.my_table
    // Much like the raw ->query() function above, the terminators are ->get,
    // ->all(), ->getAssoc(), ->getArray(), ->first()...
    // Most query builder methods allow RAW entries too, like ->where('raw = this or raw = that')...

    // TABLE and SELECT
        // Build can do basic ->table(), ->select() and ->addSelect().  If you want
        // complex joins, use a full RAW ->query() or add RAW to the ->table() method.
        // By default select is set to *

        // Get all records
        $customers = $this->db->table('customer')->get(); // or all()

        // Get all records, limited columns
        $customers = $this->db->select('id', 'name')->table('customer')->get();

        // Select as RAW, either as one parameter per column, or as one big string
        $customers = $this->db->select('id as UID', 'name as Customer')->table('customer')->get();
        $customers = $this->db->select('id as UID, name as Customer')->table('customer')->get();

        // Complex Raw table and select, but still using build style, not pure ->query()
        $customers = $this->db->select('c.*, r.name as Role')
            ->table('customer c INNER JOIN roles r on c.role_id = r.role_id');
        $customers->addSelect('r.ID');
        $customers->distinct();
        $customers = $customers->get();


    // WHERE, ORDER GROUP, HAVING
        // By default, the ->where() method has 2 params, and the = opreator is assumed
        // But like eloquent, you can override the operator ->where('name', 'like', 'bob')...
        // Chaining ->where() is by default AND...but you can alter to OR...but it won't
        // do compled nested AND/OR combinations...for that I just use RAW queries.

        // Get one with WHERE statement
        $customers = $this->db->table('customer')->where('name', 'Bob')->first();

        // Multiple wheres (AND)
        $customers = $this->db->table('customer')->where('zip', 75067)->where('disabled', false)->first();

        // Multiple wheres (OR)
        $customers = $this->db->table('customer')->where('zip', '=', 75067, 'or')->where('zip', 75068)->get();
        $customers = $this->db->table('customer')->where('zip', '=', 75067)->orWhere('zip', 75068)->get();

        // Mixed in RAW where
        $customers = $this->db->table('customer')->where('(zip = 1 or zip = 2)')->where('disabled', false)->get();

        // Mixed RAW complex
        $customers = $this->db
            ->table('customer')
            ->select('name', 'count(*) as cnt')
            ->where('(x = y AND a = b) OR (c = d)')
            ->groupBy('name')
            ->orderBy('cnt desc')
            ->having('cnt > 1')

}

function procedures()
{
    // No params
    $customers = $this->db->procedure('GetAllCustomers')->get()

    // Params
    $customers = $this->db->procedure('GetCustomersByState', [
        ['name' => 'state', 'value' => 'TX'],
        ['name' => 'zip', 'value' => 75067],
    ]);

    // No return
    $this->db->procedure('DeleteAllCustomers');
}