在 FatFree 映射器对象上使用计算属性(不从数据库中检索)

Using computed properties on FatFree mapper object (not retrieved from database)

我知道我可以有这样的方法,在我的 class 中扩展 \DB\SQL\Mapper:

public function getAll()
{
  $this->upperBody = 'upper(body)';
  $all = $this->find();
  return $all;
}

然后,调用时,该模型将有一个虚拟(计算)属性 upperBody,它将保存数据库中 body 字段的大写值。

但是我只限于底层数据库引擎提供的功能。

是否可以像这样使用这些动态字段:

$this->stripped_tags = strip_tags($this->body);

当然假设我们在数据库中有一个现有的 body 字段?

如果没有,那么我将如何编写自己的过滤器以在模板中使用,从而改为 {{@post.body|strip_tags}}... 或者例如 {{@post.body|excerpt 30}}.

我用 cast():

解决了这个问题
    foreach ($this->model->getAll() as $p) {

        $c = $p->cast();
        $c[ 'excerpt' ] = chop_string( strip_tags($c[ 'body' ], '<p>'), $this->excerpt_limit);
        $all_posts[] = $c;
    }

    $f3->set('posts', $all_posts);

但似乎必须有一种更优雅的方法来做到这一点,因为这里我将数组处理加倍只是为了分配一个新的计算 属性.

那么有没有办法一次搞定呢?

修改 1: 正如@ikkez 建议的那样,我尝试这样做:

在我的模型 class 中,我添加了一个名为 upperTitle 的 属性,如下所示:

public $upperTitle;

然后在控制器中我有这样的代码:

$this->mdlPost->upperTitle = strtoupper($this->mdlPost->title);

使用 $this->mdlPost->paginate(...,...,...) 查询数据库后 var_dumping,upperTitle 是一个空字符串。

修改 2:(@ikkez 在评论中建议)

public function __construct(\DB\SQL $db)
    {
        $this->db = $db;
        parent::__construct($db, 'posts');

        $this->onload(function($self){
            $self->set('upper_title', strtoupper($self->get('title')));
        });
    }

同样,没有成功。

转储映射器对象后,这可能有帮助吗?

所以无论它是什么,它都是临时可见的,但在映射器对象本身上作为 属性 是空的。

我可以看到值是NULL,表达式就是值应该是什么。这就是为什么在模板中执行 @post.upper_title 时得到空输出的原因,那么您将如何解决这个问题。正常情况下,表达式接受查询字符串,值是执行该查询的结果,但这里显然不是这样。

看来最好的方法是在模板中定义自己的过滤器。它被描述为in the user guide。如果您需要比此处提供的代码更多的代码,请随时告诉我。

只需添加要在映射器中动态使用的字段,如 属性 到 class,您应该可以即时使用它:

class ModelA extends \DB\SQL\Mapper {

  public $stripped_tags;

}

Mapper 实现有点复杂,正如人们在不查看源代码的情况下所预料的那样。首先,您不应该尝试设置 non-existing 映射器字段,因为 Mapper 实现将它们解释为需要由数据库引擎派生的虚拟字段。这也是为什么值为 NULL 的原因。相反,您应该使用真实的 class 属性(如 ikkez 所说)。

提示

  • Mapper->cast() 方法会忽略 class 属性,因此如果要使用它,也应该调整 cast() 方法。

  • 定义虚拟字段也只在查询数据库前有效


解决方案

  • 不要通过调用 Mapper->set() 或设置 Mapper->field = …(内部调用 Mapper->set())来创建(或更新)虚拟字段

  • 改为创建 class 属性

  • 使用ONLOAD事件填充属性


片段

以下代码段显示了如何使用 ONLOAD 触发器定义自定义 class 属性以存储以编程方式派生的值。可执行示例位于:https://gist.github.com/Rayne/fd24f5b664788cdf35956222ce790c02

/**
 * @property string title
 * @see https://gist.github.com/Rayne/fd24f5b664788cdf35956222ce790c02
 */
class TestMapper extends \DB\SQL\Mapper {
    /**
     * Custom mapper field which isn't backed and persisted by the database.
     *
     * @var null|string
     */
    public $upper_title;

    /**
     * @param \DB\SQL $sql
     */
    public function __construct(\DB\SQL $sql) {
        parent::__construct($sql, 'test');

        $this->onload(function (TestMapper $mapper) {
            $mapper->upper_title = strtoupper($mapper->title);
        });
    }
}

从数据库读取:

$mapper = new TestMapper($sql);
for ($mapper->load(); !$mapper->dry(); $mapper->next()) {
    printf("title:       %s\n", $mapper->title);
    printf("upper_title: %s\n", $mapper->upper_title);
}

结果:

title:       Hello World
upper_title: HELLO WORLD