Laravel专供:实现Schemaless

赞助

如果你觉得我写得还行,欢迎付费支持。

之所以要实现 Schemaless,主要是因为在线 DDL 有很多痛点,关于这一点,我在以前已经写过文章,没看过的不妨看看「史上最LOW的在线DDL解决方案」,不过那篇文章主要以介绍为主,并没有涉及具体的实现,所以我写了一个 Laravel 的例子。

首先创建测试用的 users 表,并且添加虚拟字段 name、address、level:

mysql> CREATE TABLE users (
           id INT UNSIGNED NOT NULL AUTO_INCREMENT,
           created_at timestamp null,
           updated_at timestamp null,
           data JSON NOT NULL,
           PRIMARY KEY(id)
       );

mysql> ALTER TABLE users add name VARCHAR(100) AS
       (JSON_UNQUOTE(JSON_EXTRACT(data, '$.name'))) AFTER id;

mysql> ALTER TABLE users add address VARCHAR(100) AS
       (JSON_UNQUOTE(JSON_EXTRACT(data, '$.address'))) AFTER name;

mysql> ALTER TABLE users add level INT UNSIGNED AS
       (JSON_EXTRACT(data, '$.level')) AFTER name;

然后是核心代码 Schemaless.php,以 trait 的方式实现:

<?php

namespace App;

trait Schemaless
{
    public function getDirty()
    {
        $dirty = collect(parent::getDirty());

        $keys = $dirty->keys()->map(function($key) {
            if (in_array($key, $this->virtual)) {
                $key = $this->getDataColumn() . '->' . $key;
            }

            return $key;
        });

        return $keys->combine($dirty)->all();
    }

    public function save(array $options = [])
    {
        if (!$this->exists) {
            $attributes = collect($this->getAttributes());

            $virtual = $attributes->only($this->virtual);

            $attributes = $attributes->diffKeys($virtual)->merge([
                $this->getDataColumn() => json_encode($virtual->all()),
            ]);

            $this->setRawAttributes($attributes->all());
        }

        return parent::save($options);
    }

    public function getDataColumn()
    {
        static $column;

        if ($column === null) {
            $column = defined('static::DATA') ? static::DATA : 'data';
        }

        return $column;
    }
}

接着是 Model 实现 User.php,里面激活了 schemaless,并设置了虚拟字段:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    use Schemaless;

    protected $virtual = ['name', 'address', 'level'];

    protected $hidden = ['data'];
}

最后是 Controller 实现 UsersController.php,里面演示了如何创建和修改:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\User;

class UsersController extends Controller
{
    public function __construct(User $user)
    {
        $this->user = $user;
    }

    public function store()
    {
        $user = $this->user;

        $user->name = '老王';
        $user->address = '东北';
        $user->level = 1;
        $user->save();
    }

    public function update()
    {
        $user = $this->user->find(1);

        $user->address = '北京';
        $user->save();
    }
}

从演示中可见,用法上并没有对使用者造成困扰,没有任何变化,完全透明!

发表评论

电子邮件地址不会被公开。 必填项已用*标注