I know I can have a metod like this, in my class that extends \DB\SQL\Mapper
:
public function getAll()
{
$this->upperBody = 'upper(body)';
$all = $this->find();
return $all;
}
Then, when called, this model will have a virtual(computed) property upperBody
which will hold the uppercased value of body
field in database.
But then I am limited only to what the underlying database engine offers in terms of functions.
Is it possible somehow to use these on-the-fly fields like this:
$this->stripped_tags = strip_tags($this->body);
Assuming of course we have an existing body
field in a database?
If not, then how would I go about writing my own filter to use in templates,thus having instead {{@post.body|strip_tags}}
... or for example {{@post.body|excerpt 30}}
.
I solved this, by using 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);
But it seems there must be a more elegant way to do this, as here I double the array processing just to assign one new computed property.
So is there a way to do it all in one go?
AMEND 1: As @ikkez suggested I tried doing this:
In my model class I added a property called upperTitle
like so:
public $upperTitle;
Then in controller I have code like this:
$this->mdlPost->upperTitle = strtoupper($this->mdlPost->title);
After querying a database using $this->mdlPost->paginate(...,...,...)
and var_dumping, upperTitle
is an empty string.
AMEND 2: (suggested in comment by @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')));
});
}
Again, no success.
After dumping a mapper object, maybe this could help?
So it's visible as adhoc whatever that might be, but null as a property on a mapper object itself.
I can see that value is NULL
, and the expression is what the value should be. That's why a get an empty output when doing @post.upper_title
in a template, so how would you resolve this. When doing things normally expression takes the query string, and the value is the result of that query being executed, but here obviously that's not the case.
The Mapper
implementation is a little more complex as one would expect it without looking at the source code. First you shouldn't try to set non-existing mapper fields as the Mapper
implementation interprets them as virtual fields which need to be derived by the database engine. That's also the reason why the values are NULL
. Instead, you should use real class attributes (as said by ikkez).
Hints
The Mapper->cast()
method ignores class attributes so if one wants to use it one should adapt the cast()
method, too.
Defining virtual fields is also only effective before querying the database.
Solution
Don't create (or update) virtual fields by calling Mapper->set()
or setting Mapper->field = …
(which calls internally Mapper->set()
)
Create class attributes instead
Use the ONLOAD
event to populate the attributes
Snippet
The following snippet shows how to use the ONLOAD
trigger to define custom class attributes for storing programmatically derived values. An executable example is available at: 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);
});
}
}
Read from the database:
$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);
}
Result:
title: Hello World
upper_title: HELLO WORLD