在Web工作流程中,我需要将查询从请求传递到另一个trought PHP会话。
不幸的是,我无法通过一个Doctrine的查询,因为它包含一个不可串行化的资源。
目前我将我的Doctrine查询对象转换为SQL字符串,我将其保存到我的会话中,并带有要绑定的参数。但是使用这种方法,当我从会话中获取查询时,我无法添加SQL条件(我的查询最后可以有ORDER BY
,GROUP BY
或其他语句...)。
如果我成功找回了Doctrine的查询对象,它解决了我的问题,在使用查询构建器时,语句的顺序并不重要。
你知道绕过这个问题的方法吗?
查询的例子我想坚持:
$query = $this->app['db']->createQueryBuilder();
$query->select('e.n AS Name'
, 'SUM(nb) AS Nombre'
, 'ROUND(SUM(CASE WHEN data = 2 THEN nb END) * CAST(100 AS float) / SUM(nb), 2) AS [Data type 2]')
->from('myDb.dbo.tableA', 'i')
->leftJoin('i', 'myDb.dbo.tableB', 'e', 'e.id = i.id')
->where("ind = :ind")->setParameter('ind', $this->ind)
->andWhere("(DATEPART(wk, date) = DATEPART(wk, GETDATE()) AND YEAR(date) = YEAR(GETDATE()))")
->andWhere("e.country= :country")->setParameter('country', 'UK')
->groupBy("e.n")
->orderBy("e.n");
if(!$app['isAdmin'])
$query->andWhere("e.userPermission = :userPermission")->setParameter('userPermission', $app['user']);
$qb = $this->app['session']->set('query', $query);
我使用Symfony的会话组件。
答案 0 :(得分:1)
正如评论中暗示的那样,您可以将$query
上的方法调用链转换为可序列化的内容。其他语言的语言结构称为代数数据类型,它允许您精确编码一个类型中的一组备选方案(即可能的值)(C中存在一个更简单的构造,称为enum
)。这在PHP中并不存在,但可以使用常量进行模拟。
// original code
$query = $this->app['db']->createQueryBuilder();
$query->select('e.n AS Name' , 'SUM(nb) AS Nombre' , 'ROUND(SUM(CASE WHEN data = 2 THEN nb END) * CAST(100 AS float) / SUM(nb), 2) AS [Data type 2]')
->from('myDb.dbo.tableA', 'i')
->leftJoin('i', 'myDb.dbo.tableB', 'e', 'e.id = i.id')
->where("ind = :ind")
->setParameter('ind', $this->ind)
->andWhere("(DATEPART(wk, date) = DATEPART(wk, GETDATE()) AND YEAR(date) = YEAR(GETDATE()))")
->andWhere("e.country= :country")
->setParameter('country', 'UK')
->groupBy("e.n")
->orderBy("e.n");
if(!$app['isAdmin'])
$query->andWhere("e.userPermission = :userPermission")
->setParameter('userPermission', $app['user']);
$qb = $this->app['session']->set('query', $query);
// algebraic representation, which is serializable
$query = [
"default" => [
"select" => ['e.n AS Name' , 'SUM(nb) AS Nombre' , 'ROUND(SUM(CASE WHEN data = 2 THEN nb END) * CAST(100 AS float) / SUM(nb), 2) AS [Data type 2]'],
"from" => ['myDb.dbo.tableA', 'i'],
"leftJoin" => ['i', 'myDb.dbo.tableB', 'e', 'e.id = i.id'],
"where" => ["ind = :ind"],
"andWhere" => [
["(DATEPART(wk, date) = DATEPART(wk, GETDATE()) AND YEAR(date) = YEAR(GETDATE()))"],
["e.country= :country"]
],
"groupBy" => ["e.n"],
"orderBy" => ["e.n"]
],
"tuning" => [
"not_admin" => [
"andWhere" => ["e.userPermission = :userPermission"],
]
]
];
对标签使用string
值,为参数使用数组,我们实际上最终得到了相当可读的东西。
现在,要将其转回原始调用链,我们只需要使用call_user_function_array
。
// executing the above
function chaincall($query, $method, $params) {
if (is_array($params[0]))
foreach ($params as $call)
call_user_func_array([$query,$method], $call);
else
call_user_func_array([$query,$method], $params);
}
$query = $this->app['db']->createQueryBuilder();
// baseline query
foreach ($query_data['default'] as $meth => $params) {
chaincall($meth, $params);
}
// non admin user query tuning
if(!$app['isAdmin'] && isset($query_data['tuning']['not_admin']){
foreach ($query_data['not_admin'] as $meth => $params) {
chaincall($meth, $params);
}
// other kind of query tuning?
// ...
// parameters
$query->setParameter('ind', $this->ind)
->setParameter('userPermission', $app['user']);
我试图通过根据可序列化值中的应用程序状态指定查询" 1 来遵循原始代码的逻辑,但是您可以轻松地提取或重构你觉得合适的代码。
此代码非常简单,并且利用了PHP的灵活性,但它并不安全。我建议用实常数( enum 部分)替换方法名称字符串文字(例如"选择","以及"),以及使用数组检查方法是否已获得授权,或使用仅导出这些方法的对象包装查询对象。 同样,我可能会根据DQL语法将查询片段进一步分解为最小的可能元素(原子),但这会使执行部分明智地复杂化。
1:缺乏更好的术语。