在 Doctrine DBAL 中重用 QueryBuilder
Reusing QueryBuilder in Doctrine DBAL
以下示例显示了代码示例的一些摘录。在那里调用 Doctrine DBAL 的 QueryBuilder
两次 - 一次执行 SELECT(*)
语句,然后执行 COUNT(*)
语句。
table、条件、排序顺序和结果限制等常用设置应用于重复使用的 QueryBuilder
对象。
问题
- 隐式重用
$queryBuilder
是否有缺点,如示例所示?
- 是否建议只为单独的
QueryBuilder
个实例复制粘贴代码?
- 使用
clone $queryBuilder
有副作用吗?
代码示例
/**
* @param array $arguments
* @return string
*/
private function getOutput(array $arguments)
{
/** @var \Doctrine\DBAL\Connection $connection */
$connection = $this->getConnection();
$queryBuilder = $connection
->createQueryBuilder()
->from('some_table')
->orderBy('sorting')
->setMaxResults(100);
$condition = $queryBuilder->expr()->andX();
// ... build conditions
$queryBuilder->where($condition);
$count = $queryBuilder->select('COUNT(*)')->execute()->fetchColumn(0);
if ($count === 0) {
return 'There is nothing to show';
}
if ($count > 100) {
$output = 'Showing first 100 results only:' . PHP_EOL;
} else {
$output = 'Showing all results:' . PHP_EOL;
}
// implicitly reusing previously defined settings
// (table, where, orderBy & maxResults)
$statement = $queryBuilder->select('*')->execute();
foreach ($statement as $item) {
$output .= $this->renderItem($item) . PHP_EOL;
}
return $output;
}
Doctrine DBAL 中的 QueryBuilder 可以动态地用于定义 SQL 查询,也可以再次覆盖查询部分。因此,通常在同一个 QueryBuilder 实例上调用 select()
方法两次会覆盖之前的 select
查询部分。构建器内部有一个 属性 表示干净或脏状态 - 一旦状态为脏,就必须重新创建 SQL 字符串。例如覆盖查询部分会触发脏状态。
因此,从简单的技术角度来看,通常重用 QueryBuilder 是可能的并且很好。但是,QueryBuilder 不验证跨数据库的特定逻辑。这意味着,必须手动清除多余的查询部分,以避免在执行语句时查询失败。
更好的方法是将构建查询的过程分成不同的逻辑 class 方法 - 一个用于分配通用查询约束,其他用于特定上下文(例如计算结果与排序和限制结果)放)。最后,初始代码示例可能如下所示:
引入了两种额外的方法来丰富查询
/**
* @param QueryBuilder $queryBuilder
*/
private function addConstraints(QueryBuilder $queryBuilder)
{
$condition = $queryBuilder->expr()->andX();
// ... build conditions
$queryBuilder->where($condition);
}
/**
* @param QueryBuilder $queryBuilder
*/
private function addResultSettings(QueryBuilder $queryBuilder)
{
$queryBuilder
->orderBy('sorting')
->setMaxResults(100);
}
现在有两个 QueryBuilder 实例,但是查询基本上是在前面显示的两个新方法中定义的。
/**
* @param array $arguments
* @return string
*/
private function getOutput(array $arguments)
{
/** @var \Doctrine\DBAL\Connection $connection */
$connection = $this->getConnection();
// first query builder instance for counting records
$queryBuilder = $connection->createQueryBuilder()->from('some_table');
$this->addConstraints($queryBuilder);
$statement = $queryBuilder->select('COUNT(*)')->execute();
$count = $statement->fetchColumn(0);
if ($count === 0) {
return 'There is nothing to show';
}
if ($count > 100) {
$output = 'Showing first 100 results only:' . PHP_EOL;
} else {
$output = 'Showing all results:' . PHP_EOL;
}
// second query builder instance to actually retrieve result set
$queryBuilder = $connection->createQueryBuilder()->from('some_table');
$this->addConstraints($queryBuilder);
$this->addResultSettings($queryBuilder);
$statement = $queryBuilder->select('*')->execute();
foreach ($statement as $item) {
$output .= $this->renderItem($item) . PHP_EOL;
}
return $output;
}
以下示例显示了代码示例的一些摘录。在那里调用 Doctrine DBAL 的 QueryBuilder
两次 - 一次执行 SELECT(*)
语句,然后执行 COUNT(*)
语句。
table、条件、排序顺序和结果限制等常用设置应用于重复使用的 QueryBuilder
对象。
问题
- 隐式重用
$queryBuilder
是否有缺点,如示例所示? - 是否建议只为单独的
QueryBuilder
个实例复制粘贴代码? - 使用
clone $queryBuilder
有副作用吗?
代码示例
/**
* @param array $arguments
* @return string
*/
private function getOutput(array $arguments)
{
/** @var \Doctrine\DBAL\Connection $connection */
$connection = $this->getConnection();
$queryBuilder = $connection
->createQueryBuilder()
->from('some_table')
->orderBy('sorting')
->setMaxResults(100);
$condition = $queryBuilder->expr()->andX();
// ... build conditions
$queryBuilder->where($condition);
$count = $queryBuilder->select('COUNT(*)')->execute()->fetchColumn(0);
if ($count === 0) {
return 'There is nothing to show';
}
if ($count > 100) {
$output = 'Showing first 100 results only:' . PHP_EOL;
} else {
$output = 'Showing all results:' . PHP_EOL;
}
// implicitly reusing previously defined settings
// (table, where, orderBy & maxResults)
$statement = $queryBuilder->select('*')->execute();
foreach ($statement as $item) {
$output .= $this->renderItem($item) . PHP_EOL;
}
return $output;
}
Doctrine DBAL 中的 QueryBuilder 可以动态地用于定义 SQL 查询,也可以再次覆盖查询部分。因此,通常在同一个 QueryBuilder 实例上调用 select()
方法两次会覆盖之前的 select
查询部分。构建器内部有一个 属性 表示干净或脏状态 - 一旦状态为脏,就必须重新创建 SQL 字符串。例如覆盖查询部分会触发脏状态。
因此,从简单的技术角度来看,通常重用 QueryBuilder 是可能的并且很好。但是,QueryBuilder 不验证跨数据库的特定逻辑。这意味着,必须手动清除多余的查询部分,以避免在执行语句时查询失败。
更好的方法是将构建查询的过程分成不同的逻辑 class 方法 - 一个用于分配通用查询约束,其他用于特定上下文(例如计算结果与排序和限制结果)放)。最后,初始代码示例可能如下所示:
引入了两种额外的方法来丰富查询
/**
* @param QueryBuilder $queryBuilder
*/
private function addConstraints(QueryBuilder $queryBuilder)
{
$condition = $queryBuilder->expr()->andX();
// ... build conditions
$queryBuilder->where($condition);
}
/**
* @param QueryBuilder $queryBuilder
*/
private function addResultSettings(QueryBuilder $queryBuilder)
{
$queryBuilder
->orderBy('sorting')
->setMaxResults(100);
}
现在有两个 QueryBuilder 实例,但是查询基本上是在前面显示的两个新方法中定义的。
/**
* @param array $arguments
* @return string
*/
private function getOutput(array $arguments)
{
/** @var \Doctrine\DBAL\Connection $connection */
$connection = $this->getConnection();
// first query builder instance for counting records
$queryBuilder = $connection->createQueryBuilder()->from('some_table');
$this->addConstraints($queryBuilder);
$statement = $queryBuilder->select('COUNT(*)')->execute();
$count = $statement->fetchColumn(0);
if ($count === 0) {
return 'There is nothing to show';
}
if ($count > 100) {
$output = 'Showing first 100 results only:' . PHP_EOL;
} else {
$output = 'Showing all results:' . PHP_EOL;
}
// second query builder instance to actually retrieve result set
$queryBuilder = $connection->createQueryBuilder()->from('some_table');
$this->addConstraints($queryBuilder);
$this->addResultSettings($queryBuilder);
$statement = $queryBuilder->select('*')->execute();
foreach ($statement as $item) {
$output .= $this->renderItem($item) . PHP_EOL;
}
return $output;
}