CakePHP模型查询 - 复杂 - 多表 - 总和 - 计数 - 分组依据

时间:2013-01-02 01:36:12

标签: mysql cakephp database-design model

大家好,祝大家节日快乐。

最近,我的任务是将beta应用程序从纯PHP / jQuery转换为CakePHP / ExtJS(我是新手)。

我的问题是填充主网格的最复杂查询。

为简单起见,有3个表格包含正确的烘焙关系和模型:ProjectsToDoExtraToDo

Projects hasMany ToDoExtraToDo

ToDoExtraToDoTitleComplete

我的目标是根据这三个表格获得每个项目的完成百分比。 我的方法是Complete列的 SUM 除以Complete列的 COUNT 。我正在尝试使用CakePHP方式获取可读性/性能/ otherstuffIdontknowyet。

最初,在原始SQL中,我这样做了:

SELECT
  `idProject`,

  (SELECT
    ROUND((SUM(`Complete`) / COUNT(`Complete`)) * 100),
   FROM
    (SELECT `Complete`, `ProjectID` FROM `ToDo`
      UNION ALL
     SELECT `Complete`, `ProjectID` FROM `ExtraToDo`) orders
   WHERE
    `ProjectID` = `idProject`
  ) AS 'Completion'

FROM 
  `Projects`

在决定使用CakePHP之前,我也很容易在Kohana PHP MVC框架中使用它。我知道他们的查询是如何创建的......:

private function get_completion() {
  $Query = DB::select('ProjectID', array(DB::expr('ROUND((SUM(`Complete`) / COUNT(`Complete`)) * 100)'), 'Completion'))
    ->from(array('ToDo', 'ExtraToDo'))
    ->group_by('ProjectID');

  return $Query;
}

public function get_all() {
  $Query = DB::select()
    ->from('Projects')
    ->join(array(self::get_completion(), 'Completion'))
    ->on('projects.id', '=', 'Completion.ProjectID')
    ->execute()
    ->as_array();

  return $Query;      
}

不幸的是,在使用CakePHP方式时,我已经完全努力让它在CakePHP中运行。

漂亮确定virtualFields是我答案的关键,但在阅读文档并尝试x,y和AND之后。我无法理解它们以及它们之间的联系。

提前谢谢你 -T6

2 个答案:

答案 0 :(得分:1)

我建议为Project模型类创建一个afterFind()函数,或者只是添加一个在需要执行此计算时调用的函数。

执行计算的功能如下:

getPercentageComplete($project){
{
$total_todos = count($project['ToDo']);
$completed_todos = 0;
foreach($project['ToDo'] as $todo){
   if($todo['Complete']) //assuming this is a boolean field
        $completed_todos++;
}
return $completed_todos / $total_todos;
}

然后,你的afterFind看起来像这样:

function afterFind(&$results)
{
  foreach ($results as &$project)
  {
   $project['Project']['percentageComplete'] = $this->Project->getPercentageComplete($project);
   }
return $results;
}

您可以在CakePHP Bakery - > Callback Methods

上查看有关afterFind()的更多信息

答案 1 :(得分:1)

这是很多嵌套选择。 IMO你最好建立一个更好的查询。

这应该让你去。

class Project extends AppModel {
    public $findMethods = array(
        'completion' => true
    );

    // other code

    protected function _findCompletion($state, $query, $results = array()) {
        if ($state == 'before') {
            $this->virtualFields['total'] = 'ROUND((SUM(Todo.Complete + TodoExtra.Complete) / (COUNT(Todo.Complete) + COUNT(TodoExtra.Complete))) * 100)';

            $query['fields'] = array(
                $this->alias . '.' . $this->primaryKey,
                'total'
            );

            $query['joins'] = array(
                array(  
                    'table' => 'todos', 
                    'alias' => 'Todo', 
                    'type' => 'left', 
                    'foreignKey' => false, 
                    'conditions'=> array('Todo.project_id = ' , $this->alias . '.' . $this->primaryKey) 
                ),
                array(  
                    'table' => 'todo_extras', 
                    'alias' => 'TodoExtra', 
                    'type' => 'left', 
                    'foreignKey' => false, 
                    'conditions'=> array('TodoExtra.project_id = ' . $this->alias . '.' . $this->primaryKey) 
                ),
            );

            $query['group'] = array(
                $this->alias . '.' . $this->primaryKey
            );
            return $query;
        }

        return $results;
    }

    // other code
}

现在您有了一个可以像find('first')find('all')一样使用的自定义查找方法。

来自控制器:

$this->Project->find('completion');

或在项目模型中

$this->find('completion');

应该返回这样的内容:

$results = array(
    0 => array(
        'Project' => array(
            'id' => 1,
            'total' => 50
        )
    ),
    1 => array(
        'Project' => array(
            'id' => 2,
            'total' => 75
        )
    )
);