Prefix title based on publishing status

Hii there,

I’m trying to cleanup my code a bit (I had an awful lot of business logic in the view template…) but I’m currently struggling a bit.
You see, I want to add a prefix to all titles on my blog based on it’s publication status.
to do this, I have written the following code ($articles is actually a QueryBuilder ResultSet turned into an array):

$articles = ...->toArray();

foreach($articles as $key => $article) {
  // Check if we need to add a prefix to the title
  switch($article->published) {
    case 0:
      $articles[$key]->title = "Private: " . $article->title;
      break;
    case 2:
      $articles[$key]->title = "Unlisted: " . $article->title;
      break;
  }
}

However, I wondered if there is a way to do this more cleanly…
Mostly because I don’t want to turn it into an array, just for this (as I’ll have to paginate it later down the road).

I hope somebody can help me out!

Try mysql CASE function (together with mysql CONCAT) with cakephp virtual field mechanism.
https://www.w3schools.com/sql/func_mysql_case.asp

I’m currently fiddling with the CASE function but I can’t find anywhere how I can modify the field like this…
This is the code I currently have:

$articles = ...->where(function(QueryExpression $exp, Query $q) {
          $test = $exp->addCase(
            [
              $q->newExpr()->eq('published', 0),
              $q->newExpr()->eq('published', 2)
            ],
            ['Private','Unlisted',''],
            ['string','string','string']
          );
          return $exp->or_([$test]);
        });

You need the result query to be something similar to this:

SELECT Article.id, … ,
CASE
WHEN Article.published=0 THEN CONCAT("Private: ", Article.title)
WHEN Article.published=2 THEN CONCAT("Unlisted: ", Article.title)
END AS Article__titlewithstatus
FROM articles AS Article
WHERE …

** OR **

Use the Entity virtual field mechanism: https://book.cakephp.org/3.0/en/orm/entities.html#creating-virtual-fields

class Article extends Entity
{
    protected function _getTitleWithStatus()
    {
        switch($this->published) {
            case 0:
                $articleprefix = "Private: ";
                break;
            case 2:
                $articleprefix = "Unlisted: ";
                break;
        }
        return $articleprefix . $this->title;
    }
}

Later you could use this virtualfield as $article->title_with_status

Taking the CASE-way of doing this, how much more efficient is this in compared to the for-loop I’m using right now?
Is there a different way of achieving this for if I need to do other stuff with the results later?
Because I still need to modify the body field of the table later down the road as well to strip HTML tags n such.

LOL, much more efficient. Mysql binary efficiency compared to php for-loop is like bicycle to rocket.

I mean… for going to work, I think a bicycle is more efficient :^)

But yea, I get the idea :slight_smile:
I’ll continue fiddling with it tomorrow at the office :slight_smile:

I’ve fiddled around with it and this is what I currently have:

$articles = ...;
$articles
   ->select($this->Articles)
   ->select([
      'title' => $articles->func()->concat([
      'Private: ',
        'Articles.title' => 'identifier'
       ])
   ]);

But now I need to add the CASE but I don’t know where (and how) to add the case (this current code only is able to add the private prefix but not the unlisted one…

Or simple update protected function _getTitle()

Unfortunately, I can’t use a method in my model for this :\

Can you add a virtual property like decorated_title for this, via _getDecoratedTitle(), and then use that in the places where you need it?

I’ll have to give it a try when back at the office tomorrow.
What I could do is make an entity in the app that extends the entity of the plugin right?
Would there be any other way to do it cleanly or is this the way I should go?

This is the first time you’ve mentioned that anything about this is in a plugin. The Articles entity is from a plugin that you don’t control?

Oh whoops… I thought I did mention it… my apologies…

Well… I do control the plugin but considering what I’m trying to do is fairly “specific” to the app I’m making (eg. my team doesn’t want this in the plugin itself - yet), just assume that I don’t have control over the plugin.

I haven’t tried to do something like that. You might need to make your own table and entity classes, extending the plugin ones? That seems like a lot of work, and things to maintain, just to add such a tiny bit of functionality. Given all that, I’d probably go with an element instead.

echo $this->element('decorated_title', compact('article'));

It’s not quite as DRY as an accessor would be, but better than repeating the if/else everywhere, and maybe better than adding the case stuff into all your related queries?

Hm… so basically you say: “just do it in the view instead”?
Well… actually… maybe I can make a little helper for it that handles it?

Made a little helper function for this, which works like a charm :slight_smile:
10/10 would overthink stuff again

Sometimes, doing it in the view really is the right way. You can’t possibly remove all business logic from your views. But it’s worth thinking through scenarios before you decide to do it. If not for the plugin architecture here, an accessor would probably be the way to go.

2 Likes