在Web工作流中,我需要将查询从请求传递到另一个占用的PHP会话。
不幸的是,我无法传递教义的查询,因为它包含的资源,这是不可分割的。
目前,我将 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的会话组件。
正如注释中所暗示的,您可以将$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']);
我试图通过根据可序列化值中的应用程序状态指定查询"tuning"1 来遵循原始代码的逻辑,但您可以根据需要轻松提取或重构该代码。
这段代码非常简单,并利用了PHP的灵活性,但它并不安全。我建议将方法名称字符串文字(例如"select","andWhere")替换为实际常量(这是枚举部分),并使用数组来检查该方法是否获得授权,或者用仅导出这些方法的对象包装查询对象。同样,我可能会根据 DQL 语法将查询片段进一步分解为尽可能小的元素(原子),但这会使执行部分合理地复杂化。
1:因为缺乏更好的术语。