CakePHP3:修改所有读取的实体

CakePHP3:修改所有读取的实体,php,cakephp,cakephp-3.0,Php,Cakephp,Cakephp 3.0,我很难找出解决问题的最佳方法。我的文件模型中有两个字段,例如文件路径和文件名。每当读取文件名字段时(无论是通过get(),find(),无论是JSON还是常规视图调用),我都希望对其进行修改并返回两个字段的一个合并($file\u name=$file\u path./'.$file\u name) 我想在一个地方这样做,使代码干燥。我认为实现这一点的一种方法是文档中指定的实体的访问器方法,但访问器仅在通过对象表示法访问实体时工作,并且它们不使用find()) 有没有办法在一个地方(最好是模型)

我很难找出解决问题的最佳方法。我的
文件
模型中有两个字段,例如
文件路径
文件名
。每当读取
文件名
字段时(无论是通过
get()
find()
,无论是JSON还是常规视图调用),我都希望对其进行修改并返回两个字段的一个合并(
$file\u name=$file\u path./'.$file\u name

我想在一个地方这样做,使代码干燥。我认为实现这一点的一种方法是文档中指定的实体的访问器方法,但访问器仅在通过对象表示法访问实体时工作,并且它们不使用
find()

有没有办法在一个地方(最好是模型)透明地执行此操作?我正在研究模型事件,但无法确定哪一个事件在我的情况下有用(看起来我需要类似于afterRead的东西…)

这是我尝试过的,但它不适用于查找者:

namespace App\Model\Entity;
use Cake\ORM\Entity;

class File extends Entity
{
    protected function _getFileName($f)
    {
        return $this->file_path.''.$f;
    }
}

您已经向实体添加了一个名为
\u getFileName($f)
的方法,它将改变您的数据。它不会做你认为它会做的事

从CakePHP访问器和变异器手册:

保存实体时将使用访问器,因此在定义格式化数据的方法时要小心,因为格式化数据将被持久化

因此,当您保存实体时,
文件名
字段将被保存为
$this->file\u path.'.$f
并且每次保存时都会发生

对于大多数CakePHP开发人员来说,这是一个非常令人困惑的问题。我会给你一个关于这些方法的快速解释

函数_getXXXX()
返回要保存在数据库中的值。当您返回不同的值时。这意味着存储在实体对象中的值不是您希望存储在数据库中的值。这会欺骗人们,因为他们会在视图中看到更新的值。所以他们认为它是用来格式化的

函数_setXXXX()
返回要分配给实体对象的值。执行此操作时,
$entity->title='FooBar'实体将调用
\u setTitle(“FooBar”)
并使用返回值作为分配给实体的
标题
值。同样,这将保存在数据库中

你看。这两种方法都会更改数据库中存储的内容

使用函数 我所做的就是让它保持简单

public function getFullFileName() {
     return $this->file_path.' '.$this->file_name;
}
在您的视图中,只需调用函数
echo$entity->getFullFileName()

支持序列化 如果需要用于序列化的自定义属性(即JSON)。上述函数方法不起作用。您需要使用该方法并使用不同的
字段名。这里的关键是虚拟属性不会与任何表列冲突

class Document extends Entity {
     protected $_virtual = ['full_path'];

     public function _getFullPath() {
           return $this->file_path.' '.$this->file_name;
     }
 }

因此,在上面的示例中,我们使用了一个访问器函数。我们还将这个新字段名添加到
$\u virtual
中,以便CakePHP知道如何将其包含在JSON输出中。由于表架构中没有名为
full\u path
的列,因此不会将其持久化到数据库。

实体没有
find()
方法。您是指查询中的
find()
方法吗?是的,我正在控制器中执行find(),希望find()返回修改过的实体。我可以在抓取结果集后在控制器中进行此修改,但我正在考虑一种全局机制,允许我在结果集“移交”给控制器之前修改结果集。另一个例子:假设在“people”表中有“name”字段。每次抓取此字段时(无论在哪个控制器中),您都希望在其值中添加“Mr.”前缀。我相信我在这里找到了解决方案:它涉及使用beforeFind()事件和$query->formatResults以及$results闭包。我会做测试,稍后公布答案,除非其他人想这么做。。。有趣的是,您可以使用beforeFind事件在查找之后修改数据:)谢谢@cgTag,我非常感谢您的详细回答!但是:如果我使用函数方法,我将不得不修改很多视图,正如您所说的,这个解决方案不会将其用于JSON数据。最接近的可能是使用虚拟属性,但根据这本书(以及我的测试):“请记住,虚拟属性不能用于查找”,因此这与访问器相同,我回到了原来的位置。访问器或虚拟属性都不能用于查找。我不知道你用find是什么意思。您的意思是在SQL查询中使用别名字段吗?如果您在控制器中定义虚拟属性“foo”并执行
$this->Model->find()
,虚拟属性将不会显示在查找器返回的结果集中。它是虚拟的,因此不支持出现在结果中。这是一个伪属性。虚拟属性不会出现在finder返回的结果中,但只要您使用水合作用(因此获取实体而不是数组),它就能够在您引用它时动态地重新计算它。换句话说,
foo
将不在
$entity
的属性列表中,但是
$entity->foo
将为您提供预期的结果。