【问题标题】:CakePHP 3: Properly writing functions for the Entity ModelCakePHP 3:为实体模型正确编写函数
【发布时间】:2017-05-17 23:43:44
【问题描述】:

我有一个博客模型,但我怀疑我没有正确编写代码以充分利用 CakePHP 的 MVC 结构。

这里有一些来自我的 Posts 控制器的 sn-ps。

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'));
}

这是来自index.ctp 模板的sn-p

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>
 }

在我的帖子实体中,我有以下功能

public function getCommentCount(){
    $count = 0;
    foreach ($this->comments as $comment){
        if ( $comment->isPublished() ) {
            $count += 1;
        }
    }
    return $count;
}

我的问题是我需要从index.ctp 调用getCommentCount 函数,其中$posts 对象没有comment 子级(该函数使用)。

我是否误解了实体函数的编码方式?我应该从实体查询数据库,而不是访问该对象的 expected 变量(有时不存在)?我应该采取另一种方法吗?

【问题讨论】:

  • 添加你的 index.ctp sn-p 显示 getCommentCount() 的调用
  • @RayannNayran 完成!
  • 你得到了什么结果?
  • @RayannNayran Invalid arguments supplied for foreach 在实体类中。这是有道理的,因为在这种情况下,post 变量中没有提供任何 comments

标签: cakephp model-view-controller


【解决方案1】:

我是否误解了实体函数的编码方式?

是的,你知道,因为...

我是否应该从实体查询数据库,而不是访问有时不存在的该对象的预期变量?我应该采取另一种方法吗?

...实体应该是哑数据对象。当您从那里获取数据时,您会添加业务逻辑。任何数据操作都应该发生在模型层或服务的某个地方。大多数情况下,使用 CakePHP 的人将代码放入表对象中,这有点正常。我正在模型层中为不同的事物创建额外的类,并将它们命名为App\Model\SomeModule 或直接在我的应用程序根App\SomeModule 中,并在那里注入我需要的任何其他内容(请求、表格......)。

这段代码也绝对低效:

public function getCommentCount(){
    $count = 0;
    foreach ($this->comments as $comment){
        if ( $comment->isPublished() ) {
            $count += 1;
        }
    }
    return $count;
}
  • 假设 所有 cmets 已加载
  • 需要获取所有个 cmets 以获得准确的计数
  • 它遍历所有 cmets 以过滤已发布的 cmets
  • 它在实体内部进行

如果有 500 cmets 怎么办?为什么不简单地对数据库进行计数查询并按其发布状态过滤它们?但这仍然需要您对每条记录进行一次额外的查询。因此,如果您使用Counter Cache Behavior,那么计数会更加高效和简单。

如果您真的需要在获取数据后但在渲染数据之前对其进行操作,请使用map/reduce。如果您想坚持低效的做法,您可以重构您的 getCommentCount() 并使用 map/reduce。链接的文档甚至包含一个计算东西的示例。

【讨论】:

    【解决方案2】:

    在您的索引函数中您设置了$post,但在index.ctp 中您使用了$posts,并且您错过了在查询中包含Comments 关系。

    我为你改了:

    public function index() {
        $posts = $this->Posts->find('all')->contain([
            'Users',
            'Comments'
        ])
        ->limit(20);
    
        $this->set(compact('posts'));
    }
    

    根据您的评论,您可以在这样的查询中获得总 cmets:

    public function index() {
        $posts = $this->Posts->find();
        $posts->select([
            'total_comments' => $posts->func()->sum('Comments.id')
        ])
        ->select($this->Posts)
        ->contain([
            'Users',
            'Comments'
        ])
        ->limit(20);
    
        $this->set(compact('posts'));
    }
    

    请参阅Returning the Total Count of Records 了解更多信息。

    【讨论】:

    • 因此,从语法上讲,这解决了我的问题并使错误消失。但是我确实想了解index.ctp 的数量。我不确定是否应该以这种方式实现 Entity 类中的函数,或者我是否应该查询数据库。我宁愿不在索引中的所有帖子中都包含所有 cmets,只是为了获得计数,因为这似乎是一个相当数据密集型的查询。我希望有另一种解决方法。
    【解决方案3】:

    你可以这样做:

    $posts = $this->Posts->find('all' , [
            'fields' => array_merge(
                    $this->Posts->schema()->columns(), [
                        'countCommentsAlias' => '(
                            SELECT COUNT(comments.id) 
                            FROM comments 
                            WHERE comments.post_id = Posts.id
                        )', 
                        'Users.name', 
                        'Users.email'
                    ]
                ), 
    
            'contain' => ['Users']   
        ])
    ->toArray();
    

    【讨论】:

      猜你喜欢
      • 2016-07-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多