cakephp 3 中通过嵌套关联对查询结果进行排序

Mir*_*915 4 php sorting cakephp associations cakephp-3.x

问题:

我有一个 cakephp 3.x 查询对象,带有两个嵌套关联 ,Organizations.Positions.Skills它被设置为视图变量 $Organizations。我试图按第一个嵌套关联中的列对查询生成的顶级数组进行排序。也就是说,我想$Organizations按 中的列排序Positions,特别是Positions.Ended)。

public function index()
{
    $this->loadModel('Organizations');
    $this->set('Organizations',
            $this->Organizations->find('all')
            ->contain([ //eager loading
            'Positions.Skills'
             ])
    );
}
Run Code Online (Sandbox Code Playgroud)

型号信息

组织有很多职位

职位有很多技能

我所做的研究

订购选项

根据说明书, find() 有一个顺序选项:find()->order(['field']);find('all', ['order'=>'field DESC']); 但是,这仅适用于调用 find() 的表中的字段。在这种情况下,组织。例如,这就是它的典型使用方式。

//This works.
//Sorts Organizations by the name of the organization in DESC.
$this->loadModel('Organizations');
$this->set('Organizations',
        $this->Organizations->find('all')
        ->contain([ //eager loading
            'Positions.Skills'
         ])
        ->order(['organization DESC'])
);
Run Code Online (Sandbox Code Playgroud)

但是,尝试将它用于嵌套关联是行不通的:

$this->set('Organizations',
  this->Organizations->find(...)
    ->contain(...)
    ->order(['Organizations.Positions.ended DESC'])
);

Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'Organizations.Positions.ended' in 'order clause'
Run Code Online (Sandbox Code Playgroud)

并将其更改为引用将嵌套的字段也不起作用:

//also doesn't work.
$this->set('Organizations',
  $this->Organizations->find(...)
    ->contain(...)
    ->order([Positions.ended DESC])
);

Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'Positions.ended' in 'order clause'
Run Code Online (Sandbox Code Playgroud)

在这两种情况下,当 cakephp 执行查询生成的 PDO 语句时,都会创建 sql 错误。

排序选项

类似地,根据Cookbook,急切加载/关联具有“排序”选项: $this->loadModel('Organizations');

$this->set('Organizations',
    $this->Organizations->find('all')
    ->contain([ //eager loading
        'Positions.Skills',
        'Positions' => [
            'sort' => ['Positions.ended'=>'DESC']
        ]
    ])
);
Run Code Online (Sandbox Code Playgroud)

但是,这仅对嵌套关联进行排序。具体来说,它对嵌套关联进行排序。它不会通过嵌套关联对整个结果集进行排序(因此,多维排序)。

例如:

该组织,组织 C(组织 ID 1),有两个职位:

  1. 位置 5。2012 年结束
  2. 位置 4. 2014 年结束

组织 B(组织 ID 2)有两个职位:

  1. 位置 3 结束于 2013 年
  2. 位置 2 结束于 2015 年

评估查询时,上面的代码和数据会生成以下数组:

Organizations => [
  0 => [
    'organization' => 'Organization A',
    positions => [
      0 => [
        'position' => 'Position 1',
        'ended' => '2016'
      ]
    ]
  ],
  1 => [
    'organization' => 'Organization C',
    'positions' => [
      0 => [
        'position' => 'Position 4',
        'ended' => '2014'
      ],
      1 => [
        'position' => 'Position 5',
        'ended' => '2012'
      ]
    ]
  ],
  2 => [
    'organization' => 'Organization B',
    'positions' => [
      0 => [
        'position' => 'Position 2',
        'ended' => '2015'
      ],
      1 => [
        'position' => 'Position 3',
        'ended' => '2013'
      ]
    ]
  ]
]
Run Code Online (Sandbox Code Playgroud)

其他研究

同样,我的研究中也出现了以下 stackoverflow 问题:

http://stackoverflow.com/questions/26859700/cakephp-order-not-working

http://stackoverflow.com/questions/17670986/order-by-doesnt-work-with-custom-model-find-while-paginating

http://stackoverflow.com/questions/18958410/cakephp-paginate-and-sort-2nd-level-association

http://stackoverflow.com/questions/34705257/cakephp-paginate-and-sort-hasmany-association 
Run Code Online (Sandbox Code Playgroud)

此外,我确实知道 PHP 有自己的排序函数,例如sort()multisort();但是,只有在评估查询(通过 foreach)后才能调用它们。或者,可以调用 $organization->toArray() 然后使用 multisort; 但是,这必须在视图中完成,会破坏关注点分离的 MVC 约定(数据和查询由控制器和模型操作,而不是视图!),并且效率相当低,因为它将被称为当页面加载时。

那么,如何通过其嵌套关联对 cakephp 查询进行排序呢?

或者,更简单地说,如何对查询进行排序/排序以在评估时生成以下数组:

Organizations => [
  0 => [
    'organization' => 'Organization A',
    'positions' => [
      0 => [
        'position' => 'Position 1',
        'ended' => '2016'
      ]
    ]
  ],
  0 => [
    'organization' => 'Organization B',
    'positions' => [
      0 => [
        'position' => 'Position 2',
        'ended' => '2015'
      ],
      1 => [
        'position' => 'Position 3',
        'ended' => '2013'
      ]
    ]
  ],
  1 => [
    'organization => 'Organization C',
    'positions' => [
      0 => [
        'position' => 'Position 4',
        'ended' => '2014'
       ],
      1 => [
        'position' => 'Position 5',
        'ended' => '2012'
      ]
    ]
  ]
]
Run Code Online (Sandbox Code Playgroud)

背景和背景:

我正在使用 cakephp 3.2 为自己构建一个[作品集网站][7],以展示我的 Web 开发技能并帮助我寻求开发职业。对于我的简历页面,我使用嵌套手风琴组织大量数据,以模仿招聘人员期望在实际简历上看到的简历风格。结果,我的观点做了以下事情:

  1. 循环遍历顶级视图变量(组织)
    1. 呈现组织详细信息
    2. 循环浏览该组织的职位(仍在 1 内)
      1. 渲染位置详细信息
      2. 循环查看该职位的相关技能
        1. 使用适当的链接呈现每个技能,以按该技能进行过滤。

项目清单

ndm*_*ndm 6

仅通过主查询上的联接检索hasOne和关联。关联是在单独的查询中检索的,因此当您尝试引用 的字段时会出现错误。belongsTohasManyPositions

您想要的问题应该很容易解决,无论是在 SQL 级别还是在 PHP 级别。

SQL级别

在 SQL 级别,您可以加入Positions,并按计算max(Positions.ended)列排序,例如:

$this->Organizations
    ->find('all')
    ->select(['max_ended' => $this->Organizations->query()->func()->max('Positions.ended')])
    ->select($this->Organizations)
    ->contain([
        'Positions.Skills',
        'Positions' => [
            'sort' => ['Positions.ended' => 'DESC']
        ]
    ])
    ->leftJoinWith('Positions')
    ->order([
        'max_ended' => 'DESC'
    ])
    ->group('Organizations.id');
Run Code Online (Sandbox Code Playgroud)

就这样,这应该会给你你想要的结果。查询将类似于:

$this->Organizations
    ->find('all')
    ->select(['max_ended' => $this->Organizations->query()->func()->max('Positions.ended')])
    ->select($this->Organizations)
    ->contain([
        'Positions.Skills',
        'Positions' => [
            'sort' => ['Positions.ended' => 'DESC']
        ]
    ])
    ->leftJoinWith('Positions')
    ->order([
        'max_ended' => 'DESC'
    ])
    ->group('Organizations.id');
Run Code Online (Sandbox Code Playgroud)

PHP级别

在 PHP 级别上,使用集合也很容易解决(请注意,查询是集合 - 某种程度),但是只有当您打算检索所有行,或者必须处理一组无序行时,它才有意义。原因...类似:

$Organizations->map(function ($organization) {
    $organization['positions'] =
        collection($organization['positions'])->sortBy('ended')->toList();
    return $organization;
});
$sorted = $sorted->sortBy(function ($organization) {
    return
        isset($organization['positions'][0]['ended']) ?
            $organization['positions'][0]['ended'] : 0;
});
Run Code Online (Sandbox Code Playgroud)

这也可以在结果格式化程序中实现,因此如果您坚持的话,事情会发生在控制器或模型级别。

$query->formatResults(function ($results) {
    /* @var $results \Cake\Datasource\ResultSetInterface|\Cake\Collection\CollectionInterface */
    $results = $results->map(function ($row) {
        $row['positions'] =
            collection($row['positions'])->sortBy('ended')->toList();
        return $row;
    });
    return $results->sortBy(function ($row) {
        return isset($row['positions'][0]['ended']) ?
            $row['positions'][0]['ended'] : 0;
    });
});
Run Code Online (Sandbox Code Playgroud)