我在客户端有一个使用st-table显示数据的表。在进行过滤操作时,我使用st-pipe插件。客户端将传递ajax get请求,如下所示:
search= {"assigned_to.user_info.full_name":"alonso","created_by.user_info.full_name":"buena"}
将其传递给服务器,控制器处理它并生成一个数组:
$matchingStatements = [
'AssignedTos.UserInfos' => [
'OR' => [
"UserInfos.firstname LIKE" => "%alonso%",
"UserInfos.lastname LIKE" => "%alonso%"
]
],
'CreatedBies.UserInfos' => [
'OR' => [
"UserInfos.firstname LIKE" => "%buena%",
"UserInfos.lastname LIKE" => "%buena%"
]
]
];
通过这样做,过滤器语句的数量是动态的。 这个$ matchingStatements将在foreach循环中运行,在每个传递中它将调用$ leads->匹配(...)语句,如下所示:
$userFields = [
'fields' => ['id', 'username'],
'UserInfos' => ['fields' => ['user_id', 'firstname', 'lastname']]
];
$option = [
'contain' => [
'Contacts',
'AssignedTos' => $userFields,
'LeadTypes',
'LeadStatuses',
'Sources',
'PropertyRequirements' => ['Locations', 'PropertyCategories', 'Listings'],
'Activities',
'Events',
'CreatedBies'=> $userFields,
'UpdatedBies'=> $userFields
]
];
$leads = $leadsTable->find('all', $option);
foreach ($matchingStatements as $target=>$where) {
$leads = $leads->matching($target, function ($q) use ($where) {
return $q->andWhere($where);
});
}
...
}
但是,似乎$ lead只会成为仅使用$ matchingStatements [0]调用匹配的结果。 所以匹配的第二个匹配('CreatedBies.UserInfos')...不适用。
示例输出类似于以下json:
"leads": [
{
"id": 1,
"created_by": {
"id": 327,
"username": "Cedric61",
"user_info": {
"firstname": "Buena",
"lastname": "Causey",
"full_name": "Buena Causey"
}
},
"assigned_to": {
"id": 1,
"username": "Grigsby1995",
"user_info": {
"firstname": "Alonso",
"lastname": "Stauffer",
"full_name": "Alonso Stauffer"
}
},
},
{
"id": 2,
"created_by": {
"id": 327,
"username": "Cedric61",
"user_info": {
"firstname": "Buena",
"lastname": "Causey",
"full_name": "Buena Causey"
}
},
"assigned_to": {
"id": 2,
"username": "ccp123",
"user_info": {
"firstname": "Christoper",
"lastname": "Pedros",
"full_name": "Christoper Pedros"
}
},
}
];
但它假设只输出第一个对象。
关于我应该如何获得我想要的结果的任何想法都非常感谢。
谢谢!
更新: 由于@JohnWayne:
,我将foreach循环更新为以下内容 foreach ($matchingStatements as $target=>$where) {
$leads = $leads->innerJoinWith($target, function ($q) use ($where) {
return $q->andWhere($where);
});
}
当输出$ leads-> sql()时,它会显示两个内连接被调用但是对于第二个连接,它不应用where()条件:
...
FROM
leads Leads
INNER JOIN
users AssignedTos
ON
AssignedTos.id = (Leads.user_id)
INNER JOIN
user_infos UserInfos
ON
(UserInfos.firstname like :c0 AND AssignedTos.id = (UserInfos.user_id))
INNER JOIN
users CreatedBies
ON
CreatedBies.id = (Leads.created_by_id)
INNER JOIN
contacts Contacts
...
AssignedTos加入检查条件但不在CreatedBies加入。
我还缺少什么?
更新2:
如果我手动将sql更改为以下并在shell上手动运行查询是我所期望的我只是不知道如何做到这一点我cakephp:
...
FROM
leads Leads
INNER JOIN
users AssignedTos
ON
AssignedTos.id = (Leads.user_id)
INNER JOIN
users CreatedBies
ON
CreatedBies.id = (Leads.created_by_id)
INNER JOIN
user_infos AS AssignedTosUserInfos
ON
(AssignedTosUserInfos.firstname like '%tamara%' AND AssignedTos.id = (AssignedTosUserInfos.user_id))
INNER JOIN
user_infos AS UpdatedBiesUserInfos
ON
(UpdatedBiesUserInfos.firstname like '%gertude%' AND UpdatedBies.id = (UpdatedBiesUserInfos.user_id))
INNER JOIN
user_infos AS CreatedBiesUserInfos
ON
(CreatedBiesUserInfos.firstname like '%gertude%' AND CreatedBies.id = (CreatedBiesUserInfos.user_id))
INNER JOIN
contacts Contacts
ON
Contacts.id = (Leads.contact_id)
...
答案 0 :(得分:0)
$leads = $leadsTable->find('all', $option);
然后收集一个列表中的所有条件
$myList = [];
foreach ($matchingStatements as $key => $val) {
$myList[$key] = $val;
}
然后进行一次匹配
$leads->matching($target, function ($q) use ($myList) {
return $q->andWhere(['myField IN' => $myList]);
});
如果你有更多关系而不是 - >匹配使用 - > innerJoinWith喜欢
$leads->innerJoinWith('TableName', function ($q) {
return $q->where(['TableName.FieldName IN' => 'conditions']);
});
等等
答案 1 :(得分:0)
最后我找到了一个解决方案,我只是不确定它是否优雅。 我决定使用join()方法并手动构建sql join。 当客户端传递搜索谓词时,它会将其转换为一个数组,该数组将声明以下sql foreach找到的谓词:
...
INNER JOIN
associated_table AssociatedTable
ON
AssociatedTable.id = (MainTable.user_id)
INNER JOIN
deeper_table AS AssociatedTableDeeperTable
ON
(AssociatedTableDeeperTable.field like '%something%' AND AssociatedTable.id = (AssociatedTableDeeperTable.user_id))
...
并将其添加到要传递给join(...)的谓词数组中:
$myJoins = [];
foreach ( ... ) {
$search_text = ... //taken from client-side passed data
$table = ... // table = 'AssociatedTable' take from client-side passed data that has been normalized
$myJoins[$table] = [
'table' => 'associated_table',
'type' => 'INNER',
'conditions' => "$table.id = main_table.id"
];
$myJoins["{$table}DeeperTable"] = [
'table' => 'deeper_table',
'type' => 'INNER',
'conditions' => "({$table}DeeperTable.firstname LIKE '%{$search_text}%' AND $table.id = ({$table}DeeperTable.user_id))"
];
}
$leads = $leadsTable->find('all',...)
if (count($myJoins)>0) {
$leads->join($myJoins);
}
如果有人有更好的答案,那就知道了。 谢谢!
答案 2 :(得分:0)
首先要做的事情。从您的示例中可以看出,控制器对构建条件数组的确切做法并不清楚,因此我要指出在处理用户提供的表时应用白名单或类似技术非常重要/列名称,因为key => value
条件数组中的键按原样插入到查询中!
从SQL输出判断,问题是非唯一UserInfos
关联别名,您不能多次包含同一个别名,查询构建器没有任何自动重命名功能或类似
如果您希望能够使用速记匹配和/或连接方法,那么您可以通过将关联重命名为类似于您在工作SQL示例中显示的内容来解决问题,例如{{ 1}},AssignedTosUserInfos
等
CreatedBiesUserInfos
条件阵列需要相应地改变:
// ...
class AssignedTosTable extends Table
{
public function initialize(array $config)
{
// ...
$this->belongsTo('AssignedTosUserInfos', [
'className' => 'UserInfos'
]);
}
}
如果您没有$matchingStatements = [
'AssignedTos.AssignedTosUserInfos' => [
'OR' => [
"AssignedTosUserInfos.firstname LIKE" => "%alonso%",
"AssignedTosUserInfos.lastname LIKE" => "%alonso%"
]
],
// ...
];
,AssignedTos
等关联的具体表类,并且您正在使用共享的CreatedBies
类,则可以创建关联使用当前表别名动态别名,这样就可以根据为父关联设置的别名创建正确的关联:
UsersTable
如果父级,即// ...
class UsersTable extends Table
{
public function initialize(array $config)
{
// ...
$this->belongsTo($this->registryAlias() . 'UserInfos', [
'className' => 'UserInfos'
]);
}
}
类,将创建如下的关联:
LeadsTable
然后上面的$this->belongsTo('AssignedTos', [
'className' => 'Users'
]);
类中的动态关联将被命名为UsersTable
。对于AssignedTosUserInfos
关联,它将是CreatedBies
,依此类推。