my6uot9/yii2-dynamic-ar

扩展 Yii ActiveRecord 以支持 Maria 动态列

安装: 3

依赖: 0

建议者: 0

安全: 0

星星: 0

观察者: 1

分支: 15

类型:yii2-extension

0.4.3 2021-02-24 12:38 UTC

README

这是 Tom Worster 版本的分支:Tom Worster's version.

yii2-dynamic-ar 扩展为 Yii 2 框架Active Record ORM 添加了类似 NoSQL 的文档。

Maria 动态列和 PostgreSQL jsonb

Maria 10.0+ 和 jsonb 列类型 以及 函数PostgreSQL 9.4+ 中提供了,实际上每个 SQL 表的每一行都附加了一个 NoSQL 文档。这是一个强大的特性,允许你做一些在关系型数据库中难以完成的事情。可能会让你转向 Couch 或 Mongo,或者在你的模式中实施像 EAV 的犯罪行为,现在突然变得容易,当

  • 记录可以有任意数量的属性时,
  • 属性名可以即时生成,
  • 动态属性名不会出现在模式中,
  • 动态属性可以像关联数组一样结构化。

Dynamic AR 现在适用于 Maria,并将在未来适用于 PostgreSQL。

示例

一个在线购物网站有一个存储每个产品信息的表。

CREATE TABLE product (
    id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
    sku VARCHAR(32),
    upc VARCHAR(32),
    title VARCHAR(255),
    price DECIMAL(9, 2),
    stock INT(11),
    details LONGBLOB NOT NULL
);

在这个(简化的)示例中,details 将持有 Maria 的 动态列 blob,并在模型类中通过 dynamicColumn() 方法声明。Dynamic AR 类声明中的其他一切都是熟悉的 AR 内容。

class Product extends \my6uot9\dynamicAr\DynamicActiveRecord
{
    public static function tableName()
    {
        return 'product';
    }

    public static function dynamicColumn() : string
    {
        return 'details';
    }
}

现在我们可以用 Product 做所有正常的 AR 事情,但除此之外,我们还可以读取、写入和更新模式中未提到的属性。

$product = new Product([
    'sku' => 5463,
    'upc' => '234569',
    'price' => 4.99,
    'title' => 'Clue-by-four',
    'description' => 'Used for larting lusers or constructing things',
    'dimensions' => [
        'unit' => 'inch',
        'width' => 4,
        'height' => 2,
        'length' => 20,
    ],
    'material' => 'wood',
]);
$product->save();

details 表列视为存储序列化关联数组。但与在文本字段中保存 JSON 文档不同,你可以在代码的任何地方使用动态属性,包括查询中,就像使用模式属性一样。区别在于

  • 嵌套属性使用点表示法,例如 dimensions.length
  • 在模型实例上直接获取和设置嵌套属性使用 getAttribute()setAttribute() 方法,因为 PHP 不允许在标识符中使用点表示法。
  • 当动态属性出现在查询中时,用感叹号括号包裹它,例如 (! dimensions.length !)。(属性名和其感叹号括号之间的空格是可选的,所以 (!material!) 也可以。)

例如

$model = new Product([
    'title' => 'Car',
    'specs.fuel.tank.capacity' => 50,
    'specs.fuel.tank.capacity.unit' => 'liter',
]);
$model->setAttribute('specs.wheels.count', 4);
$model = Product::find()->where(['(!dimensions.length!)' => 10]);
$section = Product::find()
    ->select('CONCAT((! dimensions.width !), " x ", (! dimensions.height !))')
    ->where(['id' => 11])
    ->one();

点表示法可以在 Yii 接受属性名字符串的任何地方工作,例如

class Product extends \my6uot9\dynamicAr\DynamicActiveRecord
{
    public function rules()
    {
        return [['dimensions.length', 'double', 'min' => 0.0]];
    }

    public function search($params)
    {
        $dataProvider = new ActiveDataProvider([
            'sort' => [
                'attributes' => [
                    'dimensions.length' => [
                        'asc' => ['(! dimensions.length !)' => SORT_DESC],
                        'desc' => ['(! dimensions.length !)' => SORT_ASC],
                    ],
                ],
            ],
            // ...
        ]);
    }
    
    public static function dynamicColumn() : string
    {
        return 'details';
    }
}

设计原则

DynamicActiveRecord 为读取和写入 AR 模型属性可以做的三件事情增加了第四件

  1. $model->foo 如果存在,将访问实例变量 $foo,
  2. 否则,如果模型的表中有列 "foo",则访问列属性 foo,
  3. 否则,如果模型的类有魔法方法 getFoo() / setFoo(),则访问虚拟属性 foo,
  4. 否则 $model->foo 访问名为 "foo" 的动态属性。

因此,任何不指向正常三种AR模型属性(实例变量、列属性、虚拟属性)的属性名,一旦使用它,就自动成为动态属性。无法声明动态属性,只能通过写入它来定义。

读取不存在的属性将返回 null。

PHP null、SQL NULL 和 Maria

Maria 不会对设置为 SQL NULL 的动态列进行编码

SELECT COLUMN_CREATE('a', 1, 'b', null) = COLUMN_CREATE('a', 1);
>> 1

因此,如果一个表记录当前有一个动态列 'b',而 Maria 执行一个将其设置为 NULL 的更新,那么 Maria 将从记录中删除 'b'。 (如果 NULL 有传统数据库中 '数据值不存在' 的含义,这是有意义的。)所以 DynamicActiveRecord 在从数据库读取回后无法区分 NULL 值和不存在于动态列。

为了保持一致,DynamicActiveRecord 总是在读取未设置的动态属性时返回 null,与抛出异常的 ActiveRecord 相比。但这也符合设计原则(如上所述)。

进一步阅读

类参考

更多文档

有用的链接

在 gh-pages 分支中重新生成文档

vendor/bin/apidoc api . . --template="spinitron\dynamicAr\doc\template\ApiRenderer"

问题、评论、问题

使用 问题跟踪器

版权(c)2015 Spinitron LLC
版权 2021 My6UoT9