Cara untuk mengesahkan medan dan syarat semasa melakukan penggantian
P粉448346289
P粉448346289 2024-01-10 18:06:26
0
1
422

Kami mempunyai pelayan pusat dan beberapa pelayan serantau seperti d1, d2, d3, d4, d5.

Ada beberapa jadual untuk disalin. Untuk kesederhanaan, mari kita anggap bahawa kita mempunyai jadual yang dipanggil tblFoo yang wujud pada d1, d2, d3, d4, d5 dan c dan mempunyai struktur yang sama pada semua pelayan ini. Peraturannya mudah sahaja:

  • Jika rekod wujud pada pelayan d, ia juga wujud pada pelayan c dengan nilai yang betul-betul sama untuk setiap medan
  • Jika rekod wujud pada pelayan d (cth. pada d1), ia tidak akan wujud pada mana-mana pelayan d lain (d2, d3, d4 atau d5)

Matlamatnya adalah untuk memastikan jika perubahan (masukkan, kemas kini, padam) dibuat kepada tblFoo pada pelayan d, ia harus segera ditukar pada pelayan c juga. Ini berfungsi dengan baik untuk operasi sisipan (kerana id, iaitu pkFooID, mempunyai atribut auto_increment mengikut definisi). Ini juga berfungsi untuk mengemas kini dan memadam operasi, tetapi kami mempunyai beberapa kebimbangan dengan operasi ini. Berikut ialah kod (versi ringkas):

namespace App\ORM;

use Cake\ORM\Query as ORMQuery;
// 其他一些use语句

class Query extends ORMQuery
{
    // 大量的代码...

    /**
     * 重写同名方法以处理与c的同步
     */
    public function execute()
    {
        // 一些表需要复制。如果是这样的表,则我们需要执行一些额外的步骤。否则,我们只需调用父方法
        if (($this->_repository->getIgnoreType() || (!in_array($this->type(), ['select']))) && $this->isReplicate() && ($this->getConnection()->configName() !== 'c')) {
            // 获取表
            $table = $this->_repository->getTable();
            // 复制查询
            $replica = clone $this;
            // 将复制品的连接设置为c,因为我们需要在中央应用地区的更改
            $replica->setParentConnectionType('d')->setConnection(ConnectionManager::get('c'));
            $replica->setIgnoreType($this->_repository->getIgnoreType());
            // 首先执行复制品,因为我们需要引用c的ID而不是反过来
            $replica->execute();
            // 如果是插入操作,我们还需要处理ID
            if (!empty($this->clause('insert'))) {
                // 加载主键的名称,以便稍后使用它来查找最大值
                $primaryKey = $this->_repository->getPrimaryKey();
                // 获取最高的ID值,这将始终是一个正数,因为我们已经在复制品上执行了查询
                $firstID = $replica->getConnection()
                                   ->execute("SELECT LAST_INSERT_ID() AS {$primaryKey}")
                                   ->fetchAll('assoc')[0][$primaryKey];

                // 获取列
                $columns = $this->clause('values')->getColumns();
                // 为了添加主键
                $columns[] = $primaryKey;
                // 然后用这个调整后的数组覆盖插入子句
                $this->insert($columns);
                // 获取值
                $values = $this->clause('values')->getValues();
                // 以及它们的数量
                $count = count($values);
                // 可能已经将多个行插入到复制品中作为此查询的一部分,我们需要复制所有它们的ID,而不是假设有一个单独的插入记录
                for ($index = 0; $index < $count; $index++) {
                    // 将适当的ID值添加到所有要插入的记录中
                    $values[$index][$primaryKey] = $firstID + $index;
                }
                // 用这个调整后的数组覆盖值子句,其中包含PK值
                $this->clause('values')->values($values);
            }
        }
        if ($this->isQueryDelete) {
            $this->setIgnoreType(false);
        }
        // 无论如何都执行查询,无论它是复制表还是非复制表
        // 如果是复制表,则我们已经在if块中对查询进行了调整
        return parent::execute();
    }

}

Masalah yang perlu dibimbangkan ialah : Jika kami melaksanakan kemas kini atau memadam kenyataan pada d1, yang syaratnya boleh dipenuhi pada pelayan serantau lain (d2, d3, d4, d5), maka kemas kini dan padam kenyataan dilaksanakan dengan betul pada d1 , tetapi Sebaik sahaja pernyataan yang sama dilaksanakan pada d1, kami mungkin mengemas kini/memadam rekod secara tidak sengaja di kawasan lain daripada pelayan c.

Untuk menyelesaikan masalah ini, penyelesaian yang disyorkan adalah untuk mengesahkan pernyataan dan membuang pengecualian jika salah satu daripada syarat berikut tidak dipenuhi:

  • Syaratnya ialah = atau IN
  • Medan ialah [pk|fk]*ID, iaitu kunci asing atau kunci primer

Jadual tanpa gelagat replikasi akan dilaksanakan seperti biasa Sekatan di atas hanya digunakan pada jadual dengan gelagat replikasi, seperti tblFoo dalam contoh kami.

Soalan

Bagaimanakah saya boleh mengesahkan kemas kini/memadam pertanyaan dalam laksanakan penulisan semula saya supaya hanya kunci utama atau asing boleh dicari dan hanya operator = atau IN boleh digunakan?

P粉448346289
P粉448346289

membalas semua(1)
P粉333186285

Ini adalah cara saya menyelesaikan masalah saya.

Model dengan kelakuan penyalinan melakukan pengesahan berikut:

<?php

namespace App\ORM;

use Cake\ORM\Table as ORMTable;

class Table extends ORMTable
{
    protected static $replicateTables = [
        'inteacherkeyjoin',
    ];

    public function isValidReplicateCondition(array $conditions)
    {
        return count(array_filter($conditions, function ($v, $k) {
            return (bool) preg_match('/^[\s]*[pf]k(' . implode('|', self::$replicateTables) . ')id[\s]*((in|=).*)?$/i', strtolower(($k === intval($k)) ? $v : $k));
        }, ARRAY_FILTER_USE_BOTH)) > 0;
    }

    public function validateUpdateDeleteCondition($action, $conditions)
    {
        if ($this->behaviors()->has('Replicate')) {
            if (!is_array($conditions)) {
                throw new \Exception("在调用{$action}时,需要传递一个数组");
            } elseif (!$this->isValidReplicateCondition($conditions)) {
                throw new \Exception("传递给{$action}操作的条件不安全,您需要指定主键或带有=或IN运算符的外键");
            }
        }
    }

    public function query()
    {
        return new Query($this->getConnection(), $this);
    }
}

Untuk Query类,我们有一个isReplicate方法,触发我们需要的验证,where kaedah telah ditindih untuk memastikan syarat disahkan dengan betul:

/**
     * 当且仅当:
     * - _repository已正确初始化
     * - _repository具有复制行为
     * - 当前连接不是c
     */
    protected function isReplicate()
    {
        if (($this->type() !== 'select') && ($this->getConnection()->configName() === 'c') && ($this->getParentConnectionType() !== 'd')) {
            throw new \Exception('副本表必须始终从区域连接更改');
        }
        if (in_array($this->type(), ['update', 'delete'])) {
            $this->_repository->validateUpdateDeleteCondition($this->type(), $this->conditions);
        }

        return ($this->_repository && $this->_repository->behaviors()->has('Replicate'));
    }

    public function where($conditions = null, $types = [], $overwrite = false)
    {
        $preparedConditions = is_array($conditions) ? $conditions : [$conditions];
        $this->conditions = array_merge($this->conditions, $preparedConditions);

        return parent::where($conditions, $types, $overwrite);
    }
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan