在 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
我知道我可以有这样的方法,在我的 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