所以,使用CakeORM 3.2,我有一个对象(Cabinet
),它有Siblings
,它们是位于相同位置的其他Cabinets
(Cabinets
相同的x
,y
和location_id
)。在hasMany
关联中,我试图从Siblings
中过滤出原始对象。这就是我所拥有的
class CabinetsTable extends Table {
public function initialize(arary $config)
{
$this->hasMany('Siblings', [
'className' => CabinetsTable::class,
'foreignKey' => ['x', 'y', 'location_id'],
'bindingKey' => ['x', 'y', 'location_id']
]);
}
}
我已尝试添加
'conditions' => [
'cabinet_id <> Cabinets.cabinet_id'
]
作为选项,但我收到错误Unknown column 'Cabinets.cabinet_id' in 'where clause'
所以我正在寻找的是如何在条件中引用父Cabinet
对象。我该怎么说Siblings that don't have the same ID as the parent
?
答案 0 :(得分:1)
正如评论中已经提到的那样,你想要做的事情是不可能开箱即用的。
目前,ORM将创建一个新查询来检索关联的记录,并且它不会将有关父/主查询的任何信息传递给可能用于过滤关联记录的关联条件。然而,后者是必需的,因为没有关于实际父母的信息,就不可能将它们排除在外。
可以使用自定义/扩展关联类来解决问题,这使得父查询可用。或者,可以组合使用自定义/扩展的eager加载器,它将提取并传递父查询结果的主键,但这将需要重新实现一堆逻辑,这不是很好,所以让&# 39; s跳过这一点。
自定义关联类可以挂钩例如\Cake\ORM\Association\SelectableAssociationTrait::_buildQuery()
,它将接收一个选项数组,该数组在query
键处保存父/主查询。然后可以将该值传递给conditions
回调,您可以在其中提取主键,或使用查询作为子查询来过滤掉您不想要的父Cabinets
收到Siblings
。
我前段时间做了类似的事情,所以我只是在这里转储一个稍微修改过的版本作为例子:
<强>的src /型号/协会/ ParentAwareHasMany.php 强>
namespace App\Model\Association;
use Cake\ORM\Association\HasMany;
class ParentAwareHasMany extends HasMany
{
public function find($type = null, array $options = [])
{
$conditions = $this->conditions();
if (!is_callable($conditions)) {
return parent::find($type, $options);
}
$this->conditions([]);
$query = parent::find($type, $options);
$this->conditions($conditions);
return $query;
}
protected function _buildQuery($options)
{
$fetcherQuery = parent::_buildQuery($options);
$callback = $this->conditions();
if (is_callable($callback)) {
$fetcherQuery->andWhere(function ($exp, $query) use ($callback, $options) {
return $callback($exp, $query, $options['query']);
});
}
return $fetcherQuery;
}
}
这应该是相当自我解释的,这个类的作用是避免Association::find()
调用conditions
选项中定义的可能回调,而是在_buildQuery
方法中调用它,在包装的回调中,通过第三个参数使父查询可用于内部回调,因此通过conditions
选项定义的回调将接收表达式对象,Siblings
fetcher查询和{{ 1}} fetcher父查询。
Cabinets
类需要使用新的关联类,例如覆盖CabinetsTable
Table::hasMany()
然后您可以在use App\Model\Association\ParentAwareHasMany;
// ...
public function hasMany($associated, array $options = [])
{
$options += ['sourceTable' => $this];
$association = new ParentAwareHasMany($associated, $options);
return $this->_associations->add($association->name(), $association);
}
关联配置的conditions
选项回调中进行魔术。如上所述,例如从条件中提取主键:
Siblings
使用父查询作为子查询:
use Cake\Database\Expression\Comparison;
use Cake\Database\ExpressionInterface;
use Cake\ORM\Query;
// ...
$this->hasMany('Siblings', [
'className' => CabinetsTable::class,
'foreignKey' => ['x', 'y', 'location_id'],
'bindingKey' => ['x', 'y', 'location_id'],
'conditions' => function (ExpressionInterface $exp, Query $query, Query $parentQuery) {
$ids = [];
/* @var $where ExpressionInterface */
$where = $parentQuery->clause('where');
$where->traverse(function (ExpressionInterface $expression) use (&$ids) {
if (
$expression instanceof Comparison &&
$expression->getField() === 'Cabinets.id'
) {
$ids = (array)$expression->getValue();
}
});
// note that an empty set of IDs will trigger an error
return [
'Siblings.id NOT IN' => $ids
];
}
]);
或者甚至可以事先执行查询(再次)并从结果中提取主键:
'conditions' => function (ExpressionInterface $exp, Query $query, Query $parentQuery) {
$parentQuery = $parentQuery->cleanCopy();
// the usage with `IN` requires only a single column to be selected
$parentQuery->select('Cabinets.id', true);
return [
'Siblings.id NOT IN' => $parentQuery
];
}
请注意,这些示例都有点脆弱,因为复杂的父查询可能会破坏事物,例如当涉及'conditions' => function (ExpressionInterface $exp, Query $query, Query $parentQuery) {
$parentQuery = $parentQuery->cleanCopy();
// the contained `Siblings` would cause an infite loop
$parentQuery->contain([], true);
$ids = $parentQuery->all()->extract('id')->toArray();
return [
'Siblings.id NOT IN' => $ids
];
}
回调时,当附加条件依赖于特定选定字段时,当存在其他比较表达式时也正在使用主键,等等。但最后这些只是让你开始的例子。
答案 1 :(得分:0)
如果你想创建Tree Cabinets(Cabinets belongsTo Cabinets),这可能会帮助你:
重要:数据库机柜必须包含字段 parent_id :整数
<强>模型/表/ CabinetsTable.php 强>
class CabinetsTable extends Table
{
public function initialize(array $config)
{
parent::initialize($config);
$this -> table('cabinets');
$this -> addBehavior('Tree'); // <---
$this -> belongsTo('ParentCabinets', [ // <---
'className' => 'Cabinets',
'foreignKey' => 'parent_id'
]);
$this -> hasMany('ChildCabinets', [ // <---
'className' => 'Cabinets',
'foreignKey' => 'parent_id'
]);
}
}
Controller / CabinetsController.php
中的查看方法示例class CabinetsController extends AppController
{
...
public function view($id = null)
{
$cabinet = $this -> Cabinets-> get($id, [
'contain' => ['ParentCabinets', 'ChildCabinets']
]);
$this -> set(compact('cabinet'));
}
...
}
您可以在http://book.cakephp.org/3.0/en/orm/associations.html(分类示例)
中获得更多信息