sirprize/queried
Composer 安装命令:
composer require sirprize/queried
包简介
Database/ORM-agnostic query construction helper
README 文档
README
Database/ORM-agnostic query construction helper
Description
Parsing input, setting defaults and constructing SELECT queries can quickly become a mess. Queried helps organizing clauses for WHERE, HAVING and ORDER BY, making it easy to apply input as well as defaults when executing the query.
Usage
Creating and activating simple WHERE conditions
First we'll take a look at organizing conditions for the WHERE and HAVING parts of a query statement. The basic idea is to prepare clauses describing specific conditions, register them with a query object and then activate them individually based on application requirements and user input. Each clause is wrapped in a BaseCondition object. Those objects are then registered with a BaseQueryConfigurator object:
use Sirprize\Queried\BaseQueryConfigurator;
use Sirprize\Queried\Condition\BaseCondition;
$publishedCondition = new BaseCondition();
$publishedCondition->setClause("(release.date <= CURRENT_DATE() AND release.published = 1)");
$physicalCondition = new BaseCondition();
$physicalCondition->setClause("(release.format = 'LP' OR release.format = 'CD'");
$digitalCondition = new BaseCondition();
$digitalCondition->setClause("(release.format = 'MP3' OR release.format = 'WAV'");
$queryConfigurator = new BaseQueryConfigurator();
$queryConfigurator->getConditionRegistry()
->registerCondition('published', $publishedCondition)
->registerCondition('physical', $physicalCondition)
->registerCondition('digital', $physicalCondition)
;
Next we'll activate some of the conditions according application requirements. Only published releases with a release-date of today or older must make it into the result:
$queryConfigurator->getConditionRegistry()->activateCondition('published');
Then the user can choose from digital or physical releases by setting the format parameter (eg /releases?format=digital)
$format = (array_key_exists('format', $_GET)) ? $_GET['format'] : null;
if ($format === 'digital')
{
$queryConfigurator->getConditionRegistry()->activateCondition('digital');
}
else {
$queryConfigurator->getConditionRegistry()->activateCondition('physical');
}
And finally we'll collect the activated conditions and add them to our query statement:
foreach ($queryConfigurator->getActiveConditions() as $condition)
{
$clause = $condition->getClause();
// Add clause to query
}
More complex WHERE conditions
For more complex conditions we'll subclass BaseCondition. This allows us to share conditions between queries. It's also the right place to set sensible defaults in case of invalid input:
use Sirprize\Queried\Condition\BaseCondition;
use Sirprize\Queried\Condition\Tokenizer;
class ArtistCondition extends BaseCondition
{
protected $alias = null;
public function __construct($alias = '')
{
$this->alias = $alias;
}
public function build(Tokenizer $tokenizer = null)
{
$this->reset();
$artist = $this->getValue('artist');
if (!$artist)
{
return $this;
}
$token = $tokenizer->make();
$alias = ($this->alias) ? $this->alias . '.' : $this->alias;
$this
->setClause("{$alias}artist LIKE :$token")
->addParam($token, '%'.$artist.'%')
;
return $this;
}
}
Note the $tokenizer parameter in the build() method - this is a simple object with an internal counter, letting us define non-conflicting parameter names. This is useful when combining many BaseCondition objects into one query.
Next we'll instantiate and build the condition, providing values from user input:
$artist = (array_key_exists('artist', $_GET)) ? $_GET['artist'] : null;
$artistCondition = new ArtistCondition('release');
$artistCondition
->addValue('artist', $artist)
->build(new Tokenizer())
;
And when it's time to assemble the query, we have the final clause plus parameters available:
$condition = $artistCondition->getClause(); // "release.artist LIKE :token0"
$params = $artistCondition->getParams(); // array('token0' => '%Rebolledo%')
Defining sorting
Sorting is normally expressed by one or more field names, each with a direction of either ascending or descending (eg ORDER BY release.date DESC, release.title ASC). This information is stored in a Rule object, both for ascending and descending order:
use Sirprize\Queried\Sorting\Rule;
$dateRule = new Rule();
$dateRule
->addAscColumn('release.date', 'desc')
->addAscColumn('release.date', 'asc')
->addDescColumn('release.date', 'asc')
->addDescColumn('release.date', 'asc')
->setDefaultDirection('asc')
;
$columns = $dateRule->getAscColumns(); // array('release.date' => 'desc', 'release.title.asc')
$columns = $dateRule->getDescColumns(); // array('release.date' => 'asc', 'release.title.asc')
The Sorting class maps rule names (eg from user input) to rules while applying defaults, given a non-existing rule name. It makes sure that only valid column definitions make it into the query. Let's put it all together:
use Sirprize\Queried\Sorting\Params;
use Sirprize\Queried\Sorting\Rules;
use Sirprize\Queried\Sorting\Sorting;
$rules = new Rules();
$rules->newRule('title')
->addAscColumn('release.title', 'asc')
->addDescColumn('release.title', 'desc')
->setDefaultDirection('asc')
;
$rules->newRule('date')
->addAscColumn('release.date', 'asc')
->addDescColumn('release.date', 'desc')
->setDefaultDirection('desc')
;
No defaults, no parameters
$params = new Params();
$sorting = new Sorting();
$sorting->setRules($rules);
$sorting->setParams($params);
$columns = $sorting->getColumns(); // array();
Single default
$defaults = new Params('title', 'asc');
$sorting = new Sorting();
$sorting->setRules($rules);
$sorting->setDefaults($defaults);
$columns = $sorting->getColumns(); // array('release.title' => 'asc');
Defaults and valid parameters
$params = new Params('date', 'asc');
$defaults = new Params('title', 'asc');
$sorting = new Sorting();
$sorting->setRules($rules);
$sorting->setParams($params);
$sorting->setDefaults($defaults);
$columns = $sorting->getColumns(); // array('release.date' => 'asc');
No defaults and invalid parameters (non-existing rule name)
$params = new Params('xxx', 'asc');
$sorting = new Sorting();
$sorting->setRules($rules);
$sorting->setParams($params);
$columns = $sorting->getColumns(); // array();
No defaults and invalid parameters (invalid ordering, valid orderings are "asc" or "desc")
$params = new Params('date', 'xxx');
$sorting = new Sorting();
$sorting->setRules($rules);
$sorting->setParams($params);
$columns = $sorting->getColumns(); // array('release.date' => 'desc');
Putting it all together
It's best to manage the construction of the entire query in a subclass of BaseQueryConfigurator. Here's an example of a query built for use with the Doctrine ORM:
use Doctrine\ORM\EntityManager;
use Sirprize\Queried\BaseQueryConfigurator;
class ReleaseQueryConfigurator extends BaseQueryConfigurator
{
protected $queryBuilder = null;
protected $releaseAlias = 'release';
public function __construct(EntityManager $entityManager)
{
$this->queryBuilder = $entityManager->createQueryBuilder();
// register the external condition we built earlier
$this->getConditionRegistry()->registerCondition('artist', new ArtistCondition($this->releaseAlias));
// register an inline condition
$pc = new BaseCondition();
$pc->setClause("({$this->releaseAlias}.date <= CURRENT_DATE() AND {$this->releaseAlias}.published = 1)");
$this->getConditionRegistry()->registerCondition('published', $pc);
// define some sorting rules
$this->getSorting()->getRules()->newRule('title')
->addAscColumn($this->releaseAlias.'.title', 'asc')
->addDescColumn($this->releaseAlias.'.title', 'desc')
->setDefaultDirection('asc')
;
$this->getSorting()->getRules()->newRule('artist')
->addAscColumn($this->releaseAlias.'.artist', 'asc')
->addDescColumn($this->releaseAlias.'.artist', 'desc')
->setDefaultDirection('asc')
;
}
public function getCountQuery()
{
$this->reset();
$this->applyFrom();
$this->applyConditions();
return $this->queryBuilder
->select("COUNT({$this->releaseAlias}.id)")
->getQuery()
;
}
public function getQuery()
{
$this->reset();
$this->applyFrom();
$this->applyConditions();
$this->applySorting();
return $this->queryBuilder
->select($this->releaseAlias)
->getQuery()
;
}
protected function applyFrom()
{
$this->queryBuilder
->from('My\Model\Entity\Product', $this->releaseAlias)
;
}
public function reset()
{
$this->queryBuilder
->resetDQLParts()
->setParameters(new ArrayCollection())
;
}
public function applySorting()
{
foreach ($this->getSorting()->getColumns() as $column => $order)
{
$this->queryBuilder->addOrderBy($column, $order);
}
}
protected function applyConditions()
{
foreach ($this->getConditionRegistry()->getActiveConditions() as $condition)
{
$condition->build($this->getTokenizer());
if (!$condition->getClause())
{
continue;
}
$this->queryBuilder->andWhere($condition->getClause());
foreach ($condition->getParams() as $name => $value)
{
$this->queryBuilder->setParameter($name, $value, $condition->getType($name));
}
}
}
}
Running the query
use Sirprize\Queried\Sorting\Params as SortingParams;
// input
$sort = (array_key_exists('sort', $_GET)) ? $_GET['sort'] : null;
$order = (array_key_exists('order', $_GET)) ? $_GET['order'] : null;
$label = (array_key_exists('label', $_GET)) ? $_GET['label'] : null;
$artist = (array_key_exists('artist', $_GET)) ? $_GET['artist'] : null;
// sorting
$sortingParams = new SortingParams($sort, $order);
$sortingDefaults = new SortingParams('title', 'asc');
// the query
$queryConfigurator = new ReleaseQueryConfigurator($em);
$queryConfigurator->getConditionRegistry()
->activateCondition('published')
->activateCondition('artist', array('artist' => $artist))
;
$queryConfigurator->getSorting()->setParams($sortingParams);
$queryConfigurator->getSorting()->setDefaults($sortingDefaults);
$count = $queryConfigurator->getCountQuery()->getSingleResult();
$releases = $queryConfigurator->getQuery()->getResult();
License
See LICENSE.
sirprize/queried 适用场景与选型建议
sirprize/queried 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 1.95k 次下载、GitHub Stars 达 1, 最近一次更新时间为 2012 年 06 月 09 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「sql」 「query」 「dql」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 sirprize/queried 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 sirprize/queried 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 sirprize/queried 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
Runtime analysis tool for Doctrine ORM integrated into Symfony Web Profiler. Unlike static linters, it analyzes actual query execution at runtime to detect performance bottlenecks, security vulnerabilities, and best practice violations during development with real execution context and data.
Query filtering in your frontend
Anax Database Active Record module for model classes.
Lightweight Symfony Doctrine ORM extensions for PostgreSQL — entity traits, soft-delete filter, repository base classes, decimal type, random() DQL
Doctrine Extensions for MySQL and PostgreSQL. Fork with bigint support
Set of triginometric functions for DQL in Doctrine.
统计信息
- 总下载量: 1.95k
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 1
- 点击次数: 6
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2012-06-09