iqomp/model

通用数据库模型,用于标准化使用

2.2.0 2021-11-22 06:28 UTC

This package is auto-updated.

Last update: 2024-09-22 12:58:11 UTC


README

另一种以风格管理数据库的方法。此模块的主要目的是创建一种与数据库交互的方式,而无需处理连接和管理本身。这是与数据库通信的另一种方式,不同于hyperf使用的方式。

安装

composer require iqomp/model

发布配置

php bin/hyperf.php vendor:publish iqomp/model

配置

配置保存于 config/autoload/model.php,其中包含用于某些模型的模型连接类型。数据库连接本身返回到hyperf风格。

<?php

return [
    'drivers' => [
        'pdo' => 'PDO\\Driver\\Class',
        // ...
    ],
    'models' => [
        'Model\\Class\\Name' => [
            'read' => 'slave',
            'write' => 'master'
        ],
        'Model\\Wildcard\\*' => [
            'read' => 'slave',
            'write' => 'master'
        ]
    ],
    'chains' => [
        'Model\\Class\\Name' => [
            '/field/' => [
                'model' => 'Model\\Other\\Class',
                'self' => 'id',
                'children' => 'wallet_id',
                'type' => 'left'
            ]
        ]
    ]
];

驱动程序

所有已知驱动程序。大多数情况下,此属性由模块注册。

模型

配置以决定为每个模型使用哪个数据库连接。如果模型在此处未注册,则如果存在,将使用 default 连接,否则抛出 ConnectionNotFoundException 异常。模型名称接受通配符( * ),可以匹配模型名称的任何部分。每个模型都应该有 readwrite 属性,用于定义读和写连接。

模型结构

每个模型类应扩展自 Iqomp\Model\Model 并至少有一个名为 table 的公共静态属性。以下是模型的大致样子

<?php

namespace Company\Project;

class Product extends \Iqomp\Model\Model
{
    public static $table = 'product';

    public static $chains = [
        '/field/' => [
            'model' => 'Model\\Other\\Class',
            'self' => 'id',
            'children' => 'wallet_id',
            'type' => 'left'
        ]
    ];
}

驱动程序

驱动程序是管理数据库连接和与数据库通信的一个。每个驱动程序应将自己注册为模型驱动程序,内容配置为文件 config/autoload/model.php,如下所示

<?php

return [
    'drivers' => [
        'name' => 'Class'
    ]
];

驱动程序类应实现接口 Iqomp\Model\DriverInterface。以下是驱动程序应实现的方法列表

/**
 * Construct new model object
 * @param array DB connection options
 *  @param string $model Model name
 *  @param string $table Table name
 *  @param array $chains?
 *  @param array $q_field?
 *  @param array $connections
 *    @param array $read List of connection for read
 *    @param array $write List of connection for write
 */
__construct(array $options);

/**
 * Count average value of field
 * @param string $field the field sum total to average
 * @param array $where Where condition
 * @return float Average value of the column
 */
avg(string $field, array $where = []): float;

/**
 * Count total rows in table
 * @param array $where Where condition
 * @return int Total row
 */
count(array $where = []): int;

/**
 * Insert single data to database
 * @param array $row Array column-value pair of data to insert
 * @param bool $ignore Ignore error data already there
 * @return int Last inserted id on success, null otherwise
 */
create(array $row, bool $ignore = false): ?int;

/**
 * Insert many data at once
 * @param array $rows List of array list data to insert
 * @param bool $ignore Ignore exists data if possible
 * @return boolean true on success false otherwise.
 */
createMany(array $rows, bool $ignore = false): bool;

/**
 * Decrease multiple columns with condition
 * @param array $fields List of field-value pair of column to decrease by value
 * @param $where Where condition
 */
dec(array $fields, array $where = []): bool;

/**
 * Escape string to use in raw query
 * @param string $str String to escape
 * @return escaped string
 */
escape(string $str): string;

/**
 * Get single row from table
 * @param array $where Where condition
 * @param array $order Array list of field-direction pair of sort
 * @return object if exists or null
 */
getOne(array $where = [], array $order = ['id' => false]): ?object;

/**
 * Get multiple rows from database
 * @param array $where Where condition
 * @param int $rpp Result per page, default 0 which is all.
 * @param int $page Page number, default 1.
 * @param array $order Array list of field-direction pair of sort
 * @return array list of object or empty array
 */
get(array $where = [], int $rpp = 0, int $page = 1, array $order = ['id' => false]): array;

/**
 * Get connection object
 * @param string $target Connection type target
 * @return resource connection
 */
getConnection(string $target = 'read');

/**
 * Get connection name in config that the model use for $target connection
 * @param string $target Connection type target
 * @return string connection config name
 */
getConnectionName(string $target = 'read'): ?string;

/**
 * Get current connection database name
 * @param string $target Connection type target
 * @return string database name
 */
getDBName(string $target = 'read'): ?string;

/**
 * Get the driver name used for this model
 * @return string driver name
 */
getDriver(): ?string;

/**
 * Get the model name of current model
 * @return string model name
 */
getModel(): string;

/**
 * Get the tabel name that this model handle
 * @return string
 */
getTable(): string;

/**
 * Increase multiple columns with condition
 * @param array $fields List of field-value pair of column to increase by value
 * @param $where Where condition
 */
inc(array $fields, array $where = []): bool;

/**
 * Return last error accured
 * @return string error message or null
 */
lastError(): ?string;

/**
 * Return last id inserted to database
 * @return int last inserted id, or null otherwise
 */
lastId(): ?int;

/**
 * Return the most last executed query
 * @return string if exists, null otherwise
 */
lastQuery(): ?string;

/**
 * Get the maximum value of field from table
 * @param string $field The field to process
 * @param array $where Where condition
 * @return int The max value of field.
 */
max(string $field, array $where = []): int;

/**
 * Get the minimum value of field from table
 * @param string $field THe field to process
 * @param array $where Where condition
 * @return int The smallest value of field.
 */
min(string $field, array $where = []): int;

/**
 * Remove row from table
 * @param array $where Where condition
 * @return boolean true on success, false otherwise.
 */
remove(array $where = []): bool;

/**
 * Update table
 * @param array $fields List of field-value pair of data to update
 * @param array $where Where condition.
 * @return boolean true on success false otherwise.
 */
set(array $fields, array $where = []): bool;

/**
 * Sum table single field.
 * @param string $field The field to sum
 * @param array $where Where conditon.
 * @return int total sum of the field value.
 */
sum(string $field, array $where = []): int;

/**
 * Truncate the table
 * @param string $target Connection target
 */
truncate(string $target = 'write'): bool;

所有上述方法都是 public 方法。

用法

创建模型后,现在很容易从应用程序中使用它

<?php

use Company\Project\Model\Product;

$id = Product::create($array);
$product = Product::getOne(['id'=>$id]);

排序

方法 getgetOne 具有可选参数 $order,可用于对结果进行排序。值是一个字段-顺序对的数组,其中 field 是表的列名,而 order 是布尔值 false 为降序,true 为升序。

Where 条件

某些方法接受参数 $where,用于过滤操作执行。本部分解释了where的用法

标准

创建where条件的最简单方法是如下所示

$where = [
    'id'    => 1,
    'name'  => 'User'
];
// `id` = 1 AND `name` = 'User'

IN 查询

将多个筛选值组合到数组中,以便在 in 操作符中使用

$where = [
    'id' => [1,2,3],
    'status' => 1
];
// `id` IN (1,2,3) AND `status` = 1

运算符

要使用除 = 之外的运算符进行比较,请使用以下样式

$where = [
    'id' => ['__op', '!=', 12],
    'status' => 1
];
// `id` != 12 AND `status` = 1

$where = [
    'status' => ['__op', '>', 0]
];
// `status` > 0

$where = [
    'meta' => ['__op', '!=', NULL]
];
// `meta` IS NOT NULL

$where = [
    'status' => ['__op', 'NOT IN', [1,2]]
];
// `status` NOT IN (1,2)

迄今为止已知的运算符有 ><<=>=!=NOT IN

BETWEEN

要使用between运算符,请按以下方式使用它

$where = [
    'status' => ['__between', 1, 5]
];
// `status` BETWEEN 1 AND 5

LIKE

使用数组前缀 __like 可以使用 LIKE 操作符

$where = [
    'title' => ['__like', 'name']
];
// `title` LIKE '%name%'

$where = [
    'title' => ['__like', 'name', 'left'],
    // 'title' => ['__like', 'name', 'both']
    // 'title' => ['__like', 'name', 'right']
    // 'title' => ['__like', 'name', 'none']
];
// `title` LIKE '%name'

$where = [
    'title' => ['__like', 'name', null, 'NOT']
];
// `title` NOT LIKE 'name'

$where = [
    'title' => ['__like', ['na1', 'na2', 'na3']]
];
// `title` LIKE '%na1%' OR `title` LIKE '%na2%' OR `title` LIKE '%na3%'

AND

默认情况下,每个where数组通过 AND 操作符组合。如果您的where条件不那样规范,您可以使用 $and 数组键将每个子数组组合在一起,与 AND 结合

$where = [
    '$and' => [
        [
            'created' => ['__op', '!=', NULL]
        ],
        [
            'created' => ['__op', '>', '2010-02-01']
        ]
    ]
];
// ( ( `created` IS NOT NULL ) AND ( `created` > '2010-02-01' ) )

OR

就像 $and 一样,您也可以使用 OR 使用 $or 数组键将每个条件组合在一起

$where = [
    '$or' => [
        [
            'status' => 1,
            'user' => 2
        ],
        [
            'status' => 2
        ]
    ]
];
// ( ( `status` = 1 AND `user` = 2 ) OR ( `status` = 2 ) )

转义

如果您需要在where条件下转义列名,请在列名前添加前缀 ?

$where = [
    '?`user`' => 1
];
// `user` = 1

$where = [
    '?JSON_EXTRACT(`price`, \'$.anually\')' => ['__op', '!=', '']
];
// JSON_EXTRACT(`price`, '$.anually') != ''

验证器

如果您的应用程序使用 iqomp/validator 进行对象验证,则此模块添加了可用于验证与数据模型相关对象的验证器。

唯一

确保数据尚未存在于数据库中

    // ...
    'rules' => [
        'unique' => [
            'model' => 'Company\\Project\\Model\\Product',
            'field' => 'slug',
            'where' => [ /* ... */ ] // additional where condition
        ]
    ]
    // ...

如果需要测试多个列,将field属性的值设置为数组

    // ...
    'rules' => [
        'unique' => [
            'model' => 'Company\\Project\\Model\\Product',
            'field' => [
                // same name of table column and object property
                'slug',

                // object property => table column
                'wallet' => 'wallet_id'

            ],
            'where' => [ /* ... */ ] // additional where condition
        ]
    ]
    // ...

存在

确保数据存在于数据库中

    // ...
    'rules' => [
        'exists' => [
            'model' => 'Company\\Project\\Model\\Product',
            'field' => 'slug',
            'where' => [ /* ... */ ] // additional where condition
        ]
    ]
    // ...

存在列表

确保对象属性(数组)的所有值都已在数据库中存在

    // ...
    'rules' => [
        'exists-list' => [
            'model' => 'Company\\Project\\Model\\Product',
            'field' => 'slug',
            'where' => [ /* ... */ ] // additional where condition
        ]
    ]
    // ...

格式化器

如果您在对象格式化器中使用iqomp/formatter,此模块添加了一些格式类型。

默认情况下,不会从数据库中获取数据,而是将所有具有以下格式类型的属性值使用null{"id": value}作为对象值。

如果您需要使用表中的数据填充属性,请在调用格式化器时添加额外的选项

$result = Formatter::format('fmt-name', $object, ['user'=>true]);

通过调用上述函数,options.user = true告诉处理程序从数据库中获取数据。

如果对象user有您想从数据库中获取的子对象,请使用数组键布尔对作为选项user的值

$result = Formatter::format('fmt-name', $object, ['user' => ['profile'=>true]]);

上述操作将从数据库中获取当前对象的user属性,然后从user属性的数据库中获取profile属性。

如果您需要在获取数据时添加额外的where条件,您也可以在选项中添加

$result = Formatter::format('fmt-name', $object, [
    'user' => [
        '_where' => [
            'status' => 1
        ]
    ]
]);

上述操作将带有附加where条件status = 1的数据从数据库中获取。

格式类型

多对象

将用某些字符串分隔的对象属性或使用json_decode分解成从数据库获取的对象

    // ...
    'publishers' => [
        'type'      => 'multiple-object',
        'separator' => ',', // 'json'

        'model' => [
            'name' => 'Company\\Project\\Model\\User',
            'field' => 'id'
        ],

        // optional see below
        'field' => [
            'name' => '/field/',
            'type' => '/type/'
        ],

        // optional, see below
        'fields' => [
            ['name' => '/field1/', 'type' => '/type/'],
            ['name' => '/field2/', 'type' => '/type/']
        ],

        // optional, see below
        'format' => '/object-format/'
    ]
    // ...

链式

从通过另一个表连接的另一个表中获取对象。

    // ...
    'tags' => [
        'type' => 'chain',
        'chain' => [
            'model' => [
                'name'  => 'Company\\Project\\Model\\PostTagChain',
                'field' => 'post'
            ],
            'identity' => 'post_tag'
        ],
        'model' => [
            'name'  => 'Company\\Project\\Model\\PostTag',
            'field' => 'id'
        ],

        // optional see below
        'field' => [
            'name' => '/field/',
            'type' => '/type/'
        ],

        // optional, see below
        'fields' => [
            ['name' => '/field1/', 'type' => '/type/'],
            ['name' => '/field2/', 'type' => '/type/']
        ],

        // optional, see below
        'format' => '/object-format/'
    ]
    // ...

对象

将对象属性的值转换为从表中获取的对象数据

    // ...
    'user' => [
        'type' => 'object',
        'model' => [
            'name' => 'Company\\Project\\Model\\User',
            'field' => 'id'
        ],

        // optional see below
        'field' => [
            'name' => '/field/',
            'type' => '/type/'
        ],

        // optional, see below
        'fields' => [
            ['name' => '/field1/', 'type' => '/type/'],
            ['name' => '/field2/', 'type' => '/type/']
        ],

        // optional, see below
        'format' => '/object-format/'
    ]
    // ...

对象切换

根据其他对象属性的值切换模型使用

    // ...
    'meta' => [
        'type' => 'object-switch',
        'field' => 'type',
        'cases' => [
            1 => [
                'model' => [
                    'name' => 'Company\\Project\\Model\\User',
                    'field' => 'id'
                ],

                // optional see below
                'field' => [
                    'name' => '/field/',
                    'type' => '/type/'
                ],

                // optional, see below
                'fields' => [
                    ['name' => '/field1/', 'type' => '/type/'],
                    ['name' => '/field2/', 'type' => '/type/']
                ],

                // optional, see below
                'format' => '/object-format/'
            ],
            2 => [
                // ...
            ]
        ]
    ]
    // ...

部分

使用关系id与当前对象id相同的其他表中的对象。此操作将添加新的对象属性

    // ...
    'content' => [
        'type' => 'partial',
        'model' => [
            'name' => 'Company\\Project\\Model\\User',
            'field' => 'id'
        ],

        // optional see below
        'field' => [
            'name' => '/field/',
            'type' => '/type/'
        ],

        // optional, see below
        'fields' => [
            ['name' => '/field1/', 'type' => '/type/'],
            ['name' => '/field2/', 'type' => '/type/']
        ],

        // optional, see below
        'format' => '/object-format/'
    ]
    // ...

附加属性

每个格式都接受名为fieldfieldsformat的附加配置。在单个属性配置中只能有一个。

field

从数据库获取数据后,只取一个列(即field->name)的结果,并将其值用作当前对象属性。如果存在属性type,将该格式类型应用于取值。

fields

类似于field,接受此方法从数据库结果中获取多个数据,并使用所有提到的属性作为当前对象属性的新值。如果存在属性type,将该格式类型应用于取值。

format

使用此格式名称格式化从数据库获取的数据,并使用格式化的数据作为当前对象属性。