如何在不更改所有模型命名空间的情况下为执行方法定义复制行为?

How can I define a replicate behavior for the execute method without changing all the models' namespace?

我有一个 class 这样的:

<?php

namespace App\ORM;

use Cake\ORM\Query as ORMQuery;
use Cake\Database\ValueBinder;
use Cake\Datasource\ConnectionManager;

class Query extends ORMQuery
{

    /*Some stuff which has no relevance in this question*/

    protected $mainRepository;

    protected function isReplicate()
    {
        return ($this->mainRepository && $this->mainRepository->behaviors()->has('Replicate') && ($this->getConnection()->configName() !== 'c'));
    }

    public function __construct($connection, $table)
    {
        parent::__construct($connection, $table);
        $this->mainRepository = $table;
    }

    public function execute()
    {
        if ($this->isReplicate()) {
            $connection = $this->getConnection();
            $replica = clone $this;
            $replica->setConnection(ConnectionManager::get('c'));
            $replica->execute();
        }
        $result = parent::execute();
        return $result;
    }
}

这样很好用,就是有一个中央c服务器,还有其他服务器,按区隔开。有一些表要在 c 上刷新,而在地区服务器上 executed 对它们来说是 executed。 Table 个要复制的模型如下所示:

<?php

namespace App\Model\Table;

use App\ORM\Table;

class SomeNameTable extends Table
{
    public function initialize(array $config)
    {
        /*Some initialization that's irrelevant from the problem's perspective*/
    }
}

一切正常,但有一个问题。 use App\ORM\Table; 语句指定应使用我自己的 Table 实现,并且 Table 实现确保一旦 query 被调用,我的 Query class 将被使用,在这个问题的第一个代码块中描述。这是我的 Table class:

<?php

namespace App\ORM;

use Cake\ORM\Table as ORMTable;

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

如前所述,一切都按预期工作,但这种方法实际上意味着我需要为需要此复制行为的所有模型更改基础 class 的命名空间。现在这肯定很容易做到,但将来我担心一些新表将被复制,并且从事这些工作的开发人员将不会阅读文档和最佳实践部分,直接从 Cake 的 class 继承模型姓名。有没有办法告诉 Cake 我希望所有模型都满足一些逻辑验证(例如对于具有行为的模型)将使用覆盖的 execute 方法?一般来说,这类问题的答案是“否”,但我想知道 Cake 是否具有基于某些规则覆盖代码行为的功能。

例如

function isReplicate($model) {
    return ($model->behaviors()->has('Replicate'));
}

上面的函数可以判断新的 execute 是首选还是旧的,Cake one。

不完全是,不,至少据我了解你的问题。

有些事件你可以利用,Model.initialize例如,它可以让你检查初始化模型是否加载了特定的行为,但你不能改变继承的方式query() 方法的行为。

不过您可以将它用于验证目的,这样不遵循您的文档的开发人员在不扩展您的基础时会被打脸 table class,例如检查它是否被扩展并抛出异常,如果不是这种情况,则类似于以下内容:

// in `Application::bootstrap()`

\Cake\Event\EventManager::instance()->on(
    'Model.initialize',
    function (\Cake\Event\EventInterface $event) {
        $table = $event->getSubject();
        assert($table instanceof \Cake\ORM\Table);

        if (
            $table->behaviors()->has('Replicate') &&
            !is_subclass_of($table, \App\Model\Table\AppTable::class)
        ) {
            throw new \LogicException(
                'Tables using the Replicate behavior must extend \App\Model\Table\AppTable'
            );
        }
    }
);

另见