I have a blog model, and I suspect I'm not writing my code correctly to best take advantage of CakePHP's MVC structure.
Here are some snippets from my Posts controller.
public function view() {
$posts = $this->Posts->find('all')->contain([
'Comments' => ['Users'],
'Users'
]);
$this->set(compact('posts'));
}
public function index() {
$post = $this->Posts->find('all')->contain([
'Users'
])->limit(20);
$this->set(compact('post'));
}
This is a snippet from the index.ctp
template
foreach ( $posts as $post ) {
<div class="post">
<h1 class="title>
<?= h($post->title) ?>
<small><?php echo $post->getCommentCount(); ?> comments</small>
</h1>
<div class="body">
<?= h($post->body) ?>
</div>
</div>
}
In my Post Entity, I have the following function
public function getCommentCount(){
$count = 0;
foreach ($this->comments as $comment){
if ( $comment->isPublished() ) {
$count += 1;
}
}
return $count;
}
My problem is I need to call the getCommentCount
function from the index.ctp
where the $posts
object has no comment
children (which the function uses).
Am I misunderstanding how the Entity functions should be coded? Instead of accessing expected variables of this object which sometimes aren't there, should I be querying the database from the Entity? Is there another approach I should be doing?
Am I misunderstanding how the Entity functions should be coded?
Yes, you do, because...
Instead of accessing expected variables of this object which sometimes aren't there, should I be querying the database from the Entity? Is there another approach I should be doing?
...an entity should be a dumb data object. When you fetch data from there you add business logic. Any data manipulation should happen somewhere in the model layer, or a service. Most of the time people using CakePHP put code into the table object, which is somewhat OKish. I'm creating additional classes in the model layer for different things and namespace them App\Model\SomeModule
or directly in my app root App\SomeModule
and inject whatever else I need there (request, tables...).
This code is also absolute inefficient:
public function getCommentCount(){
$count = 0;
foreach ($this->comments as $comment){
if ( $comment->isPublished() ) {
$count += 1;
}
}
return $count;
}
What if there are 500 comments? Why aren't you simply doing a count query on the database and filter them by their published status? But this would still require you to do one additional query per record. So for counts it is a lot more efficient and easy if you use the Counter Cache Behavior.
If you really need to manipulate data after you fetched it but before rendering it use map/reduce. You can refactor your getCommentCount() and use map/reduce instead if you would like to stick to your inefficient way of doing it. The linked documentation even contains an example for counting stuff.