1

大家好,节日快乐。

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

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

为简单起见,有 3 个表具有正确的烘焙关系和模型:Projects、、、ToDoExtraToDo

Projects 有很多 ToDoExtraToDo.

ToDoExtraToDo有列TitleComplete

我的目标是根据这三个表来获得每个项目的完成百分比。 我解决这个问题的方法是列的SUM除以CompleteCOUNTComplete。我正在尝试使用 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`

我也很容易让它在 Kohana PHP MVC 框架中工作,我在决定使用 CakePHP 之前尝试过。我很喜欢他们的查询是如何创建的......:

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 和 z 之后。我一直无法理解它们以及它们之间的关系。

提前谢谢你-T6

4

2 回答 2

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 方法,可以像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
        )
    )
);
于 2013-01-03T12:58:36.940 回答
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 -> 回调方法中看到有关 afterFind() 的更多信息

于 2013-01-02T03:06:54.900 回答