This commit is contained in:
Xes
2025-08-14 22:41:49 +02:00
parent 2de81ccc46
commit 8ce45119b6
39774 changed files with 4309466 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
/tests/phpunit.xml
/tests/temp/*.php
/vendor
tags
/composer.lock

View File

@@ -0,0 +1,30 @@
language: php
sudo: false
cache:
directories:
- $HOME/.composer/cache
php:
- 5.4
- 5.5
- 5.6
- 7.0
- hhvm
matrix:
include:
- php: 5.3
dist: precise
allow_failures:
- php: 7.0
- php: hhvm
before_script:
# The mongo extension is not yet available on PHP 7
- if [[ "$TRAVIS_PHP_VERSION" != "7.0" ]] && [[ "$TRAVIS_PHP_VERSION" != "hhvm" ]]; then echo 'extension=mongo.so' >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini; fi
- if [[ "$TRAVIS_PHP_VERSION" = "7.0" ]] || [[ "$TRAVIS_PHP_VERSION" = "hhvm" ]]; then composer remove --dev --no-update doctrine/mongodb-odm; fi
- composer install --prefer-dist
script: phpunit

21
vendor/knplabs/knp-components/LICENSE vendored Normal file
View File

@@ -0,0 +1,21 @@
Copyright (c) 2011 KnpLabs
The MIT license, reference http://www.opensource.org/licenses/mit-license.php
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

21
vendor/knplabs/knp-components/README.md vendored Normal file
View File

@@ -0,0 +1,21 @@
# Knp Component library
[![Build Status](https://secure.travis-ci.org/KnpLabs/knp-components.png)](http://travis-ci.org/KnpLabs/knp-components)
## Components in this library:
- [Pager](https://github.com/knplabs/knp-components/tree/master/doc/pager/intro.md)
fancy paginator component
## Running unit tests
PHPUnit 3.5 or newer is required.
To setup and run tests follow these steps:
- go to the root directory of components
- run:
```bash
composer install
composer test
```

View File

@@ -0,0 +1,57 @@
{
"name": "knplabs/knp-components",
"type": "library",
"description": "Knplabs component library",
"keywords": ["components", "paginator", "pager", "knp", "knplabs"],
"homepage": "http://github.com/KnpLabs/knp-components",
"license": "MIT",
"authors": [
{
"name": "KnpLabs Team",
"homepage": "http://knplabs.com"
},
{
"name": "Symfony Community",
"homepage": "http://github.com/KnpLabs/knp-components/contributors"
}
],
"require": {
"php": ">=5.3.2"
},
"require-dev": {
"symfony/event-dispatcher": "~2.5",
"doctrine/orm": "~2.4",
"doctrine/mongodb-odm": "~1.0@beta",
"doctrine/phpcr-odm": "~1.2",
"jackalope/jackalope-doctrine-dbal": "~1.2",
"phpunit/phpunit": "~4.2",
"ruflin/elastica": "~1.0",
"symfony/property-access": ">=2.3"
},
"suggest": {
"symfony/property-access": "To allow sorting arrays"
},
"extra": {
"branch-alias": {
"dev-master": "1.3.x-dev"
}
},
"autoload": {
"psr-0": {
"Knp\\Component": "src"
}
},
"autoload-dev": {
"psr-0": {
"Test": "tests"
}
},
"scripts": {
"test": "phpunit"
}
}

View File

@@ -0,0 +1,54 @@
## Configuration
Some subscribers will take into account some options.
These options can be passed as the 4th argument of `Paginator::paginate()`.
For example, Doctrine ORM subscriber will generate different sql queries if the `distinct` options changes.
The list of existing options are:
| name | type | default value | subscribers |
| -------------------------- | -------------- | ------------- | ----------------------------------------------- |
| wrap-queries | bool | false | orm QuerySubscriber, orm QueryBuilderSubscriber |
| distinct | bool | true | QuerySubscriber, QueryBuilderSubscriber |
| pageParameterName | string | page | SortableSubscriber |
| defaultSortFieldName | string\|array* | | SortableSubscriber |
| defaultSortDirection | string | asc | SortableSubscriber |
| sortFieldWhitelist | array | [] | SortableSubscriber |
| sortFieldParameterName | string | sort | SortableSubscriber |
| sortDirectionParameterName | string | direction | SortableSubscriber |
| defaultFilterFields | string\|array* | | FiltrationSubscriber |
| filterFieldWhitelist | array | | FiltrationSubscriber |
| filterFieldParameterName | string | filterParam | FiltrationSubscriber |
| filterValueParameterName | string | filterValue | FiltrationSubscriber |
## Noticeable behaviors of some options
### `distinct`
If set to true, will add a distinct sql keyword on orm queries to ensuire unicity of counted results
### `wrap-queries`
If set to true, will take advantage of doctrine 2.3 output walkers by using subqueries to handle composite keys and HAVING queries.
This can considerably impact performances depending on the query and the platform, you will have to consider it at some point.
If you want to order your results by a column from a fetch joined t-many association,
you have to set `wrap-queries` to `true`. Otherwise you will get an exception with this warning:
*"Cannot select distinct identifiers from query with LIMIT and ORDER BY on a column from a fetch joined to-many association. Use output walkers."*
### `defaultSortFieldName`
Used as default field name for the sorting. It can take an array for sorting by multiple fields.
\* **Attention**: Arrays are only supported for *Doctrine's ORM*.
### `defaultFilterFields`
Used as default field names for the filtration. It can take an array for filtering by multiple fields.

View File

@@ -0,0 +1,61 @@
# Intro to Knp Pager Component
This is a PHP 5.3 paginator with a totally different core concept.
**Note:** it is still experimental, any ideas on structural design are very welcome
How is it different? First of all, it uses Symfony's **event dispatcher** to paginate whatever is needed.
The pagination process involves triggering events which hit the **subscribers**. If the subscriber
knows how to paginate the given object, it does. Finally, some subscriber must initialize the
**pagination view** object, which will be the result of pagination request. Pagination view
can be anything which will be responsible for how to render the pagination.
**Magic?** no! only KISS principle
Why reinvent the wheel? Can someone tell me what's the definition of **wheel** in the software world?
## Requirements:
- Symfony EventDispatcher component
- Namespace based autoloader for this library
## Features:
- Can be customized in any way needed, etc.: pagination view, event subscribers.
- Possibility to add custom filtering, sorting functionality depending on request parameters.
- Pagination view extensions based on event.
- Paginator extensions based on events, etc.: another object pagination compatibilities.
- Supports multiple paginations during one request
- Separation of concern, paginator is responsible for generating the pagination view only,
pagination view - for representation purposes.
- Does not require initializing specific adapters
- [configurable](config.md)
## Usage examples:
### Controller
```php
$paginator = new Knp\Component\Pager\Paginator;
$target = range('a', 'u');
// uses event subscribers to paginate $target
$pagination = $paginator->paginate($target, 2/*page*/, 10/*limit*/);
// iterate paginated items
foreach ($pagination as $item) {
//...
}
echo $pagination; // renders pagination
// overriding view rendering
$pagination->renderer = function($data) use ($template) {
return $twig->render($template, $data);
};
echo $pagination;
// or paginate Doctrine ORM query
$pagination = $paginator->paginate($em->createQuery('SELECT a FROM Entity\Article a'), 1, 10);
```

View File

@@ -0,0 +1,113 @@
# Usage of Pager component
This tutorial will cover installation and usage examples.
## Installation
composer require "knplabs/knp-components:~1.2"
## Basic usage
As mentioned in [introduction](https://github.com/knplabs/knp-components/tree/master/doc/pager/intro.md)
paginator uses event listeners to paginate the given data. First we will start from the simplest data - array.
Lets add some code in **index.php** and see it in action:
``` php
<?php
// file: index.php
include 'autoloader.php';
// usage examples will continue here
use Knp\Component\Pager\Paginator; // used class name
// end of line and tab definition
define('EOL', php_sapi_name() === 'cli' ? PHP_EOL : '<br/>');
define('TAB', php_sapi_name() === 'cli' ? "\t" : '<span style="margin-left:25px"/>');
$paginator = new Paginator; // initializes default event dispatcher, with standard listeners
$target = range('a', 'z'); // an array to paginate
// paginate target and generate representation class, it can be overrided by event listener
$pagination = $paginator->paginate($target, 1/*page number*/, 10/*limit per page*/);
echo 'total count: '.$pagination->getTotalItemCount().EOL;
echo 'pagination items of page: '.$pagination->getCurrentPageNumber().EOL;
// iterate items
foreach ($pagination as $item) {
//...
echo TAB.'paginated item: '.$item.EOL;
}
$pagination = $paginator->paginate($target, 3/*page number*/, 10/*limit per page*/);
echo 'pagination items of page: '.$pagination->getCurrentPageNumber().EOL;
// iterate items
foreach ($pagination as $item) {
//...
echo TAB.'paginated item: '.$item.EOL;
}
```
### Rendering pagination
**$paginator->paginate($target...)** will return pagination class, which is by
default **SlidingPagination** it executes a **$pagination->renderer** callback
with all arguments reguired in view template. Its your decision to implement
it whatever way you like.
**Note:** this is the default method. There will be more examples on how to render pagination templates
So if you by default print the pagination you should see something like:
``` php
<?php
// continuing in file: index.php
// ...
echo $pagination; // outputs: "override in order to render a template"
```
Now if we override the renderer callback
``` php
<?php
// continuing in file: index.php
// ...
$pagination->renderer = function($data) {
return EOL.TAB.'page range: '.implode(' ', $data['pagesInRange']).EOL;
};
echo $pagination; // outputs: "page range: 1 2 3"
```
## Sorting database query results by multiple columns (only Doctrine ORM)
It is not uncommonly that the result of a database query should be sorted by multiple columns.
For example users should be sorted by lastname and by firstname:
```php
$query = $entityManager->createQuery('SELECT u FROM User');
$pagination = $paginator->paginate($query, 1/*page number*/, 20/*limit per page*/, array(
'defaultSortFieldName' => array('u.lastname', 'u.firstname'),
'defaultSortDirection' => 'asc',
));
```
The Paginator will add an `ORDER BY` automatically for each attribute for the
`defaultSortFieldName` option.
## Filtering database query results by multiple columns (only Doctrine ORM and Propel)
You can also filter the result of a database query by multiple columns.
For example users should be filtered by lastname or by firstname:
```php
$query = $entityManager->createQuery('SELECT u FROM User');
$pagination = $paginator->paginate($query, 1/*page number*/, 20/*limit per page*/, array(
'defaultFilterFields' => array('u.lastname', 'u.firstname'),
));
```
If the `filterValue` parameter is set, the Paginator will add an `WHERE` condition automatically
for each attribute for the `defaultFilterFields` option. The conditions are `OR`-linked.

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="tests/bootstrap.php" colors="true">
<testsuites>
<testsuite name="Pager tests">
<directory suffix="Test.php">tests/Test/Pager/</directory>
</testsuite>
</testsuites>
</phpunit>

View File

@@ -0,0 +1,24 @@
<?php
namespace Knp\Component\Pager\Event;
use Symfony\Component\EventDispatcher\Event;
use Knp\Component\Pager\Pagination\PaginationInterface;
/**
* Specific Event class for paginator
*/
class AfterEvent extends Event
{
private $pagination;
public function __construct(PaginationInterface $paginationView)
{
$this->pagination = $paginationView;
}
public function getPaginationView()
{
return $this->pagination;
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace Knp\Component\Pager\Event;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\EventDispatcher;
/**
* Specific Event class for paginator
*/
class BeforeEvent extends Event
{
private $eventDispatcher;
public function __construct($eventDispatcher)
{
$this->eventDispatcher = $eventDispatcher;
}
public function getEventDispatcher()
{
return $this->eventDispatcher;
}
}

View File

@@ -0,0 +1,76 @@
<?php
namespace Knp\Component\Pager\Event;
use Symfony\Component\EventDispatcher\Event;
/**
* Specific Event class for paginator
*/
class ItemsEvent extends Event
{
/**
* A target being paginated
*
* @var mixed
*/
public $target;
/**
* List of options
*
* @var array
*/
public $options;
/**
* Items result
*
* @var mixed
*/
public $items;
/**
* Count result
*
* @var integer
*/
public $count;
private $offset;
private $limit;
private $customPaginationParams = array();
public function __construct($offset, $limit)
{
$this->offset = $offset;
$this->limit = $limit;
}
public function setCustomPaginationParameter($name, $value)
{
$this->customPaginationParams[$name] = $value;
}
public function getCustomPaginationParameters()
{
return $this->customPaginationParams;
}
public function unsetCustomPaginationParameter($name)
{
if (isset($this->customPaginationParams[$name])) {
unset($this->customPaginationParams[$name]);
}
}
public function getLimit()
{
return $this->limit;
}
public function getOffset()
{
return $this->offset;
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace Knp\Component\Pager\Event;
use Symfony\Component\EventDispatcher\Event;
use Knp\Component\Pager\Pagination\PaginationInterface;
/**
* Specific Event class for paginator
*/
class PaginationEvent extends Event
{
/**
* A target being paginated
*
* @var mixed
*/
public $target;
/**
* List of options
*
* @var array
*/
public $options;
private $pagination;
public function setPagination(PaginationInterface $pagination)
{
$this->pagination = $pagination;
}
public function getPagination()
{
return $this->pagination;
}
}

View File

@@ -0,0 +1,229 @@
<?php
namespace Knp\Component\Pager\Event\Subscriber\Filtration\Doctrine\ORM\Query;
use Doctrine\ORM\Query\TreeWalkerAdapter;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\AST\SelectStatement;
use Doctrine\ORM\Query\AST\WhereClause;
use Doctrine\ORM\Query\AST\PathExpression;
use Doctrine\ORM\Query\AST\LikeExpression;
use Doctrine\ORM\Query\AST\ComparisonExpression;
use Doctrine\ORM\Query\AST\Literal;
use Doctrine\ORM\Query\AST\ConditionalExpression;
use Doctrine\ORM\Query\AST\ConditionalFactor;
use Doctrine\ORM\Query\AST\ConditionalPrimary;
use Doctrine\ORM\Query\AST\ConditionalTerm;
/**
* Where Query TreeWalker for Filtration functionality
* in doctrine paginator
*/
class WhereWalker extends TreeWalkerAdapter
{
/**
* Filter key columns hint name
*/
const HINT_PAGINATOR_FILTER_COLUMNS = 'knp_paginator.filter.columns';
/**
* Filter value hint name
*/
const HINT_PAGINATOR_FILTER_VALUE = 'knp_paginator.filter.value';
/**
* Walks down a SelectStatement AST node, modifying it to
* filter the query like requested by url
*
* @param SelectStatement $AST
* @return void
*/
public function walkSelectStatement(SelectStatement $AST)
{
$query = $this->_getQuery();
$queriedValue = $query->getHint(self::HINT_PAGINATOR_FILTER_VALUE);
$columns = $query->getHint(self::HINT_PAGINATOR_FILTER_COLUMNS);
$components = $this->_getQueryComponents();
$filterExpressions = array();
$expressions = array();
foreach ($columns as $column) {
$alias = false;
$parts = explode('.', $column);
$field = end($parts);
if (2 <= count($parts)) {
$alias = trim(reset($parts));
if (!array_key_exists($alias, $components)) {
throw new \UnexpectedValueException("There is no component aliased by [{$alias}] in the given Query");
}
$meta = $components[$alias];
if (!$meta['metadata']->hasField($field)) {
throw new \UnexpectedValueException("There is no such field [{$field}] in the given Query component, aliased by [$alias]");
}
$pathExpression = new PathExpression(PathExpression::TYPE_STATE_FIELD, $alias, $field);
$pathExpression->type = PathExpression::TYPE_STATE_FIELD;
} else {
if (!array_key_exists($field, $components)) {
throw new \UnexpectedValueException("There is no component field [{$field}] in the given Query");
}
$pathExpression = $components[$field]['resultVariable'];
}
$expression = new ConditionalPrimary();
if (isset($meta) && $meta['metadata']->getTypeOfField($field) === 'boolean') {
if (in_array(strtolower($queriedValue), array('true', 'false'))) {
$expression->simpleConditionalExpression = new ComparisonExpression($pathExpression, '=', new Literal(Literal::BOOLEAN, $queriedValue));
} elseif (is_numeric($queriedValue)) {
$expression->simpleConditionalExpression = new ComparisonExpression($pathExpression, '=', new Literal(Literal::BOOLEAN, $queriedValue == '1' ? 'true' : 'false'));
} else {
continue;
}
unset($meta);
} elseif (is_numeric($queriedValue)) {
$expression->simpleConditionalExpression = new ComparisonExpression($pathExpression, '=', new Literal(Literal::NUMERIC, $queriedValue));
} else {
$expression->simpleConditionalExpression = new LikeExpression($pathExpression, new Literal(Literal::STRING, $queriedValue));
}
$filterExpressions[] = $expression->simpleConditionalExpression;
$expressions[] = $expression;
}
if (count($expressions) > 1) {
$conditionalPrimary = new ConditionalExpression($expressions);
} elseif (count($expressions) > 0) {
$conditionalPrimary = reset($expressions);
} else {
return;
}
if ($AST->whereClause) {
if ($AST->whereClause->conditionalExpression instanceof ConditionalTerm) {
if (!$this->termContainsFilter($AST->whereClause->conditionalExpression, $filterExpressions)) {
array_unshift(
$AST->whereClause->conditionalExpression->conditionalFactors,
$this->createPrimaryFromNode($conditionalPrimary)
);
}
} elseif ($AST->whereClause->conditionalExpression instanceof ConditionalPrimary) {
if (!$this->primaryContainsFilter($AST->whereClause->conditionalExpression, $filterExpressions)) {
$AST->whereClause->conditionalExpression = new ConditionalTerm(array(
$this->createPrimaryFromNode($conditionalPrimary),
$AST->whereClause->conditionalExpression,
));
}
} elseif ($AST->whereClause->conditionalExpression instanceof ConditionalExpression) {
if (!$this->expressionContainsFilter($AST->whereClause->conditionalExpression, $filterExpressions)) {
$previousPrimary = new ConditionalPrimary();
$previousPrimary->conditionalExpression = $AST->whereClause->conditionalExpression;
$AST->whereClause->conditionalExpression = new ConditionalTerm(array(
$this->createPrimaryFromNode($conditionalPrimary),
$previousPrimary,
));
}
}
} else {
$AST->whereClause = new WhereClause(
$conditionalPrimary
);
}
}
/**
* @param ConditionalExpression $node
* @param Node[] $filterExpressions
* @return bool
*/
private function expressionContainsFilter(ConditionalExpression $node, $filterExpressions)
{
foreach ($node->conditionalTerms as $conditionalTerm) {
if ($conditionalTerm instanceof ConditionalTerm && $this->termContainsFilter($conditionalTerm, $filterExpressions)) {
return true;
} elseif ($conditionalTerm instanceof ConditionalPrimary && $this->primaryContainsFilter($conditionalTerm, $filterExpressions)) {
return true;
}
}
return false;
}
/**
* @param ConditionalTerm $node
* @param Node[] $filterExpressions
* @return bool|void
*/
private function termContainsFilter(ConditionalTerm $node, $filterExpressions)
{
foreach ($node->conditionalFactors as $conditionalFactor) {
if ($conditionalFactor instanceof ConditionalFactor) {
if ($this->factorContainsFilter($conditionalFactor, $filterExpressions)) {
return true;
}
} elseif ($conditionalFactor instanceof ConditionalPrimary) {
if ($this->primaryContainsFilter($conditionalFactor, $filterExpressions)) {
return true;
}
}
}
return false;
}
/**
* @param ConditionalFactor $node
* @param Node[] $filterExpressions
* @return bool
*/
private function factorContainsFilter(ConditionalFactor $node, $filterExpressions)
{
if ($node->conditionalPrimary instanceof ConditionalPrimary && $node->not === false) {
return $this->primaryContainsFilter($node->conditionalPrimary, $filterExpressions);
}
return false;
}
/**
* @param ConditionalPrimary $node
* @param Node[] $filterExpressions
* @return bool
*/
private function primaryContainsFilter(ConditionalPrimary $node, $filterExpressions)
{
if ($node->isSimpleConditionalExpression() && ($node->simpleConditionalExpression instanceof LikeExpression || $node->simpleConditionalExpression instanceof ComparisonExpression)) {
return $this->isExpressionInFilterExpressions($node->simpleConditionalExpression, $filterExpressions);
}
if ($node->isConditionalExpression()) {
return $this->expressionContainsFilter($node->conditionalExpression, $filterExpressions);
}
return false;
}
/**
* @param Node $node
* @param Node[] $filterExpressions
* @return bool
*/
private function isExpressionInFilterExpressions(Node $node, $filterExpressions)
{
foreach ($filterExpressions as $filterExpression) {
if ((string) $filterExpression === (string) $node) {
return true;
}
}
return false;
}
/**
* @param Node $node
* @return ConditionalPrimary
*/
private function createPrimaryFromNode($node)
{
if ($node instanceof ConditionalPrimary) {
$conditionalPrimary = $node;
} else {
$conditionalPrimary = new ConditionalPrimary();
$conditionalPrimary->conditionalExpression = $node;
}
return $conditionalPrimary;
}
}

View File

@@ -0,0 +1,55 @@
<?php
namespace Knp\Component\Pager\Event\Subscriber\Filtration\Doctrine\ORM;
use Doctrine\ORM\Query;
use Knp\Component\Pager\Event\ItemsEvent;
use Knp\Component\Pager\Event\Subscriber\Filtration\Doctrine\ORM\Query\WhereWalker;
use Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine\ORM\Query\Helper as QueryHelper;
use Knp\Component\Pager\PaginatorInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class QuerySubscriber implements EventSubscriberInterface
{
public function items(ItemsEvent $event)
{
if ($event->target instanceof Query) {
if (!isset($_GET[$event->options[PaginatorInterface::FILTER_VALUE_PARAMETER_NAME]]) || (empty($_GET[$event->options[PaginatorInterface::FILTER_VALUE_PARAMETER_NAME]]) && $_GET[$event->options[PaginatorInterface::FILTER_VALUE_PARAMETER_NAME]] !== "0")) {
return;
}
if (!empty($_GET[$event->options[PaginatorInterface::FILTER_FIELD_PARAMETER_NAME]])) {
$columns = $_GET[$event->options[PaginatorInterface::FILTER_FIELD_PARAMETER_NAME]];
} elseif (!empty($event->options[PaginatorInterface::DEFAULT_FILTER_FIELDS])) {
$columns = $event->options[PaginatorInterface::DEFAULT_FILTER_FIELDS];
} else {
return;
}
$value = $_GET[$event->options[PaginatorInterface::FILTER_VALUE_PARAMETER_NAME]];
if (false !== strpos($value, '*')) {
$value = str_replace('*', '%', $value);
}
if (is_string($columns) && false !== strpos($columns, ',')) {
$columns = explode(',', $columns);
}
$columns = (array) $columns;
if (isset($event->options[PaginatorInterface::FILTER_FIELD_WHITELIST])) {
foreach ($columns as $column) {
if (!in_array($column, $event->options[PaginatorInterface::FILTER_FIELD_WHITELIST])) {
throw new \UnexpectedValueException("Cannot filter by: [{$column}] this field is not in whitelist");
}
}
}
$event->target
->setHint(WhereWalker::HINT_PAGINATOR_FILTER_VALUE, $value)
->setHint(WhereWalker::HINT_PAGINATOR_FILTER_COLUMNS, $columns);
QueryHelper::addCustomTreeWalker($event->target, 'Knp\Component\Pager\Event\Subscriber\Filtration\Doctrine\ORM\Query\WhereWalker');
}
}
public static function getSubscribedEvents()
{
return array(
'knp_pager.items' => array('items', 0),
);
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace Knp\Component\Pager\Event\Subscriber\Filtration;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Knp\Component\Pager\Event\BeforeEvent;
class FiltrationSubscriber implements EventSubscriberInterface
{
/**
* Lazy-load state tracker
* @var bool
*/
private $isLoaded = false;
public function before(BeforeEvent $event)
{
// Do not lazy-load more than once
if ($this->isLoaded) {
return;
}
$disp = $event->getEventDispatcher();
// hook all standard filtration subscribers
$disp->addSubscriber(new Doctrine\ORM\QuerySubscriber());
$disp->addSubscriber(new PropelQuerySubscriber());
$this->isLoaded = true;
}
public static function getSubscribedEvents()
{
return array(
'knp_pager.before' => array('before', 1),
);
}
}

View File

@@ -0,0 +1,58 @@
<?php
namespace Knp\Component\Pager\Event\Subscriber\Filtration;
use Knp\Component\Pager\PaginatorInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Knp\Component\Pager\Event\ItemsEvent;
class PropelQuerySubscriber implements EventSubscriberInterface
{
public function items(ItemsEvent $event)
{
$query = $event->target;
if ($query instanceof \ModelCriteria) {
if (empty($_GET[$event->options[PaginatorInterface::FILTER_VALUE_PARAMETER_NAME]])) {
return;
}
if (!empty($_GET[$event->options[PaginatorInterface::FILTER_FIELD_PARAMETER_NAME]])) {
$columns = $_GET[$event->options[PaginatorInterface::FILTER_FIELD_PARAMETER_NAME]];
} elseif (!empty($event->options[PaginatorInterface::DEFAULT_FILTER_FIELDS])) {
$columns = $event->options[PaginatorInterface::DEFAULT_FILTER_FIELDS];
} else {
return;
}
if (is_string($columns) && false !== strpos($columns, ',')) {
$columns = explode(',', $columns);
}
$columns = (array) $columns;
if (isset($event->options[PaginatorInterface::FILTER_FIELD_WHITELIST])) {
foreach ($columns as $column) {
if (!in_array($column, $event->options[PaginatorInterface::FILTER_FIELD_WHITELIST])) {
throw new \UnexpectedValueException("Cannot filter by: [{$column}] this field is not in whitelist");
}
}
}
$value = $_GET[$event->options[PaginatorInterface::FILTER_VALUE_PARAMETER_NAME]];
$criteria = \Criteria::EQUAL;
if (false !== strpos($value, '*')) {
$value = str_replace('*', '%', $value);
$criteria = \Criteria::LIKE;
}
foreach ($columns as $column) {
if (false !== strpos($column, '.')) {
$query->where($column.$criteria.'?', $value);
} else {
$query->{'filterBy'.$column}($value, $criteria);
}
}
}
}
public static function getSubscribedEvents()
{
return array(
'knp_pager.items' => array('items', 0),
);
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace Knp\Component\Pager\Event\Subscriber\Paginate;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Knp\Component\Pager\Event\ItemsEvent;
use ArrayObject;
class ArraySubscriber implements EventSubscriberInterface
{
public function items(ItemsEvent $event)
{
if (is_array($event->target)) {
$event->count = count($event->target);
$event->items = array_slice(
$event->target,
$event->getOffset(),
$event->getLimit()
);
$event->stopPropagation();
} elseif ($event->target instanceof ArrayObject) {
$event->count = $event->target->count();
$event->items = new ArrayObject(array_slice(
$event->target->getArrayCopy(),
$event->getOffset(),
$event->getLimit()
));
$event->stopPropagation();
}
}
public static function getSubscribedEvents()
{
return array(
'knp_pager.items' => array('items', -1/* other data arrays should be analized first*/)
);
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Knp\Component\Pager\Event\ItemsEvent;
use ArrayObject;
use Doctrine\Common\Collections\Collection;
class CollectionSubscriber implements EventSubscriberInterface
{
public function items(ItemsEvent $event)
{
if ($event->target instanceof Collection) {
$event->count = $event->target->count();
$event->items = new ArrayObject($event->target->slice(
$event->getOffset(),
$event->getLimit()
));
$event->stopPropagation();
}
}
public static function getSubscribedEvents()
{
return array(
'knp_pager.items' => array('items', 0)
);
}
}

View File

@@ -0,0 +1,69 @@
<?php
namespace Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Knp\Component\Pager\Event\ItemsEvent;
use Doctrine\DBAL\Query\QueryBuilder;
/**
* DBALQueryBuilderSubscriber.php
*
* @author Vladimir Chub <v@chub.com.ua>
*/
class DBALQueryBuilderSubscriber implements EventSubscriberInterface
{
public function items(ItemsEvent $event)
{
if ($event->target instanceof QueryBuilder) {
/** @var $target QueryBuilder */
$target = $event->target;
// count results
$qb = clone $target;
//reset count orderBy since it can break query and slow it down
$qb
->resetQueryPart('orderBy')
;
// get the query
$sql = $qb->getSQL();
$qb
->resetQueryParts()
->select('count(*) as cnt')
->from('(' . $sql . ')', 'dbal_count_tbl')
;
$event->count = $qb
->execute()
->fetchColumn(0)
;
// if there is results
$event->items = array();
if ($event->count) {
$qb = clone $target;
$qb
->setFirstResult($event->getOffset())
->setMaxResults($event->getLimit())
;
$event->items = $qb
->execute()
->fetchAll()
;
}
$event->stopPropagation();
}
}
public static function getSubscribedEvents()
{
return array(
'knp_pager.items' => array('items', 10 /*make sure to transform before any further modifications*/)
);
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine\ODM\MongoDB;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Knp\Component\Pager\Event\ItemsEvent;
use Doctrine\ODM\MongoDB\Query\Builder;
class QueryBuilderSubscriber implements EventSubscriberInterface
{
public function items(ItemsEvent $event)
{
if ($event->target instanceof Builder) {
// change target into query
$event->target = $event->target->getQuery();
}
}
public static function getSubscribedEvents()
{
return array(
'knp_pager.items' => array('items', 10/*make sure to transform before any further modifications*/)
);
}
}

View File

@@ -0,0 +1,52 @@
<?php
namespace Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine\ODM\MongoDB;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Knp\Component\Pager\Event\ItemsEvent;
use Doctrine\ODM\MongoDB\Query\Query;
class QuerySubscriber implements EventSubscriberInterface
{
public function items(ItemsEvent $event)
{
if ($event->target instanceof Query) {
// items
$type = $event->target->getType();
if ($type !== Query::TYPE_FIND) {
throw new \UnexpectedValueException('ODM query must be a FIND type query');
}
static $reflectionProperty;
if (is_null($reflectionProperty)) {
$reflectionClass = new \ReflectionClass('Doctrine\MongoDB\Query\Query');
$reflectionProperty = $reflectionClass->getProperty('query');
$reflectionProperty->setAccessible(true);
}
$queryOptions = $reflectionProperty->getValue($event->target);
$queryOptions['limit'] = $event->getLimit();
$queryOptions['skip'] = $event->getOffset();
$resultQuery = clone $event->target;
$reflectionProperty->setValue($resultQuery, $queryOptions);
$cursor = $resultQuery->execute();
// set the count from the cursor
$event->count = $cursor->count();
$event->items = array();
// iterator_to_array for GridFS results in 1 item
foreach ($cursor as $item) {
$event->items[] = $item;
}
$event->stopPropagation();
}
}
public static function getSubscribedEvents()
{
return array(
'knp_pager.items' => array('items', 0)
);
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine\ODM\PHPCR;
use Doctrine\ODM\PHPCR\Query\Builder\QueryBuilder;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Knp\Component\Pager\Event\ItemsEvent;
/**
* @author Martin Hasoň <martin.hason@gmail.com>
*/
class QueryBuilderSubscriber implements EventSubscriberInterface
{
public function items(ItemsEvent $event)
{
if (!$event->target instanceof QueryBuilder) {
return;
}
$event->target = $event->target->getQuery();
}
public static function getSubscribedEvents()
{
return array(
'knp_pager.items' => array('items', 10/*make sure to transform before any further modifications*/)
);
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine\ODM\PHPCR;
use Doctrine\ODM\PHPCR\Query\Query;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Knp\Component\Pager\Event\ItemsEvent;
/**
* @author Martin Hasoň <martin.hason@gmail.com>
*/
class QuerySubscriber implements EventSubscriberInterface
{
public function items(ItemsEvent $event)
{
if (!$event->target instanceof Query) {
return;
}
$queryCount = clone $event->target;
$event->count = $queryCount->execute(null, Query::HYDRATE_PHPCR)->getRows()->count();
$query = $event->target;
$query->setMaxResults($event->getLimit());
$query->setFirstResult($event->getOffset());
$event->items = $query->execute();
$event->stopPropagation();
}
public static function getSubscribedEvents()
{
return array(
'knp_pager.items' => array('items', 0)
);
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine\ORM\Query;
use Doctrine\ORM\Internal\Hydration\AbstractHydrator;
/**
* As is hydrator to fetch count query result without resultSetMappings etc.
*
* @author Vladimir Chub <v@chub.com.ua>
*/
class AsIsHydrator extends AbstractHydrator
{
/**
* Hydrates all rows from the current statement instance at once.
*/
protected function hydrateAllData()
{
return $this->_stmt->fetchAll(\PDO::FETCH_ASSOC);
}
}

View File

@@ -0,0 +1,80 @@
<?php
/**
* DoctrineExtensions Paginate
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to kontakt@beberlei.de so I can send you a copy immediately.
*/
namespace Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine\ORM\Query;
use Doctrine\ORM\Query\TreeWalkerAdapter,
Doctrine\ORM\Query\AST\SelectStatement,
Doctrine\ORM\Query\AST\SelectExpression,
Doctrine\ORM\Query\AST\PathExpression,
Doctrine\ORM\Query\AST\AggregateExpression;
/**
* Replaces the selectClause of the AST with a COUNT statement
*
* @category DoctrineExtensions
* @package DoctrineExtensions\Paginate
* @author David Abdemoulaie <dave@hobodave.com>
* @copyright Copyright (c) 2010 David Abdemoulaie (http://hobodave.com/)
* @license http://hobodave.com/license.txt New BSD License
* @deprecated
*/
class CountWalker extends TreeWalkerAdapter
{
/**
* Distinct mode hint name
*/
const HINT_DISTINCT = 'knp_paginator.distinct';
/**
* Walks down a SelectStatement AST node, modifying it to retrieve a COUNT
*
* @param SelectStatement $AST
* @return void
*/
public function walkSelectStatement(SelectStatement $AST)
{
$rootComponents = array();
foreach ($this->_getQueryComponents() AS $dqlAlias => $qComp) {
$isParent = array_key_exists('parent', $qComp)
&& $qComp['parent'] === null
&& $qComp['nestingLevel'] == 0
;
if ($isParent) {
$rootComponents[] = array($dqlAlias => $qComp);
}
}
if (count($rootComponents) > 1) {
throw new \RuntimeException("Cannot count query which selects two FROM components, cannot make distinction");
}
$root = reset($rootComponents);
$parentName = key($root);
$parent = current($root);
$pathExpression = new PathExpression(
PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $parentName,
$parent['metadata']->getSingleIdentifierFieldName()
);
$pathExpression->type = PathExpression::TYPE_STATE_FIELD;
$distinct = $this->_getQuery()->getHint(self::HINT_DISTINCT);
$AST->selectClause->selectExpressions = array(
new SelectExpression(
new AggregateExpression('count', $pathExpression, $distinct), null
)
);
// ORDER BY is not needed, only increases query execution through unnecessary sorting.
$AST->orderByClause = null;
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine\ORM\Query;
use Doctrine\ORM\Query;
/**
* ORM Query helper for cloning
* and hint processing
*/
class Helper
{
/**
* Clones the given $query and copies all used
* parameters and hints
*
* @param Query $query
* @return Query
*/
public static function cloneQuery(Query $query)
{
$clonedQuery = clone $query;
$clonedQuery->setParameters(clone $query->getParameters());
// attach hints
foreach ($query->getHints() as $name => $hint) {
$clonedQuery->setHint($name, $hint);
}
return $clonedQuery;
}
/**
* Add a custom TreeWalker $walker class name to
* be included in the CustomTreeWalker hint list
* of the given $query
*
* @param Query $query
* @param string $walker
* @return void
*/
public static function addCustomTreeWalker(Query $query, $walker)
{
$customTreeWalkers = $query->getHint(Query::HINT_CUSTOM_TREE_WALKERS);
if ($customTreeWalkers !== false && is_array($customTreeWalkers)) {
$customTreeWalkers = array_merge($customTreeWalkers, array($walker));
} else {
$customTreeWalkers = array($walker);
}
$query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, $customTreeWalkers);
}
}

View File

@@ -0,0 +1,115 @@
<?php
/**
* DoctrineExtensions Paginate
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE. This license can also be viewed
* at http://hobodave.com/license.txt
*
* @category DoctrineExtensions
* @package DoctrineExtensions\Paginate
* @author David Abdemoulaie <dave@hobodave.com>
* @copyright Copyright (c) 2010 David Abdemoulaie (http://hobodave.com/)
* @license http://hobodave.com/license.txt New BSD License
* @deprecated
*/
namespace Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine\ORM\Query;
use Doctrine\DBAL\Types\Type;
use Doctrine\ORM\Query\TreeWalkerAdapter,
Doctrine\ORM\Query\AST\SelectStatement,
Doctrine\ORM\Query\AST\SelectExpression,
Doctrine\ORM\Query\AST\PathExpression,
Doctrine\ORM\Query\AST\AggregateExpression;
/**
* Replaces the selectClause of the AST with a SELECT DISTINCT root.id equivalent
*
* @category DoctrineExtensions
* @package DoctrineExtensions\Paginate
* @author David Abdemoulaie <dave@hobodave.com>
* @copyright Copyright (c) 2010 David Abdemoulaie (http://hobodave.com/)
* @license http://hobodave.com/license.txt New BSD License
*/
class LimitSubqueryWalker extends TreeWalkerAdapter
{
/**
* ID type hint
*/
const IDENTIFIER_TYPE = 'knp_paginator.id.type';
/**
* @var int Counter for generating unique order column aliases
*/
private $_aliasCounter = 0;
/**
* Walks down a SelectStatement AST node, modifying it to retrieve DISTINCT ids
* of the root Entity
*
* @param SelectStatement $AST
* @return void
*/
public function walkSelectStatement(SelectStatement $AST)
{
$parent = null;
$parentName = null;
$selectExpressions = array();
foreach ($this->_getQueryComponents() AS $dqlAlias => $qComp) {
// preserve mixed data in query for ordering
if (isset($qComp['resultVariable'])) {
$selectExpressions[] = new SelectExpression($qComp['resultVariable'], $dqlAlias);
continue;
}
if ($qComp['parent'] === null && $qComp['nestingLevel'] == 0) {
$parent = $qComp;
$parentName = $dqlAlias;
continue;
}
}
$identifier = $parent['metadata']->getSingleIdentifierFieldName();
$this->_getQuery()->setHint(
self::IDENTIFIER_TYPE,
Type::getType($parent['metadata']->getTypeOfField($identifier))
);
$pathExpression = new PathExpression(
PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION,
$parentName,
$identifier
);
$pathExpression->type = PathExpression::TYPE_STATE_FIELD;
array_unshift($selectExpressions, new SelectExpression($pathExpression, '_dctrn_id'));
$AST->selectClause->selectExpressions = $selectExpressions;
if (isset($AST->orderByClause)) {
foreach ($AST->orderByClause->orderByItems as $item) {
if ($item->expression instanceof PathExpression) {
$pathExpression = new PathExpression(
PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION,
$item->expression->identificationVariable,
$item->expression->field
);
$pathExpression->type = PathExpression::TYPE_STATE_FIELD;
$AST->selectClause->selectExpressions[] = new SelectExpression(
$pathExpression,
'_dctrn_ord' . $this->_aliasCounter++
);
}
}
}
$AST->selectClause->isDistinct = true;
}
}

View File

@@ -0,0 +1,140 @@
<?php
/**
* DoctrineExtensions Paginate
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE. This license can also be viewed
* at http://hobodave.com/license.txt
*
* @category DoctrineExtensions
* @package DoctrineExtensions\Paginate
* @author David Abdemoulaie <dave@hobodave.com>
* @copyright Copyright (c) 2010 David Abdemoulaie (http://hobodave.com/)
* @license http://hobodave.com/license.txt New BSD License
*/
namespace Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine\ORM\Query;
use Doctrine\ORM\Query\AST\ArithmeticExpression;
use Doctrine\ORM\Query\AST\SimpleArithmeticExpression;
use Doctrine\ORM\Query\TreeWalkerAdapter,
Doctrine\ORM\Query\AST\SelectStatement,
Doctrine\ORM\Query\AST\PathExpression,
Doctrine\ORM\Query\AST\InExpression,
Doctrine\ORM\Query\AST\NullComparisonExpression,
Doctrine\ORM\Query\AST\InputParameter,
Doctrine\ORM\Query\AST\ConditionalPrimary,
Doctrine\ORM\Query\AST\ConditionalTerm,
Doctrine\ORM\Query\AST\ConditionalExpression,
Doctrine\ORM\Query\AST\ConditionalFactor,
Doctrine\ORM\Query\AST\WhereClause;
/**
* Replaces the whereClause of the AST with a WHERE id IN (:foo_1, :foo_2) equivalent
*
* @category DoctrineExtensions
* @package DoctrineExtensions\Paginate
* @author David Abdemoulaie <dave@hobodave.com>
* @copyright Copyright (c) 2010 David Abdemoulaie (http://hobodave.com/)
* @license http://hobodave.com/license.txt New BSD License
* @deprecated
*/
class WhereInWalker extends TreeWalkerAdapter
{
/**
* ID Count hint name
*/
const HINT_PAGINATOR_ID_COUNT = 'knp_paginator.id.count';
/**
* Primary key alias for query
*/
const PAGINATOR_ID_ALIAS = 'dpid';
/**
* Replaces the whereClause in the AST
*
* Generates a clause equivalent to WHERE IN (:dpid_1, :dpid_2, ...)
*
* The parameter namespace (dpid) is defined by
* the PAGINATOR_ID_ALIAS
*
* The total number of parameters is retrieved from
* the HINT_PAGINATOR_ID_COUNT query hint
*
* @param SelectStatement $AST
* @return void
*/
public function walkSelectStatement(SelectStatement $AST)
{
$rootComponents = array();
foreach ($this->_getQueryComponents() AS $dqlAlias => $qComp) {
$isParent = array_key_exists('parent', $qComp)
&& $qComp['parent'] === null
&& $qComp['nestingLevel'] == 0
;
if ($isParent) {
$rootComponents[] = array($dqlAlias => $qComp);
}
}
if (count($rootComponents) > 1) {
throw new \RuntimeException("Cannot count query which selects two FROM components, cannot make distinction");
}
$root = reset($rootComponents);
$parentName = key($root);
$parent = current($root);
$pathExpression = new PathExpression(
PathExpression::TYPE_STATE_FIELD, $parentName, $parent['metadata']->getSingleIdentifierFieldName()
);
$pathExpression->type = PathExpression::TYPE_STATE_FIELD;
$count = $this->_getQuery()->getHint(self::HINT_PAGINATOR_ID_COUNT);
if ($count > 0) {
$arithmeticExpression = new ArithmeticExpression();
$arithmeticExpression->simpleArithmeticExpression = new SimpleArithmeticExpression(array($pathExpression));
$expression = new InExpression($arithmeticExpression);
$ns = self::PAGINATOR_ID_ALIAS;
for ($i = 1; $i <= $count; $i++) {
$expression->literals[] = new InputParameter(":{$ns}_$i");
}
} else {
$expression = new NullComparisonExpression($pathExpression);
$expression->not = false;
}
$conditionalPrimary = new ConditionalPrimary;
$conditionalPrimary->simpleConditionalExpression = $expression;
if ($AST->whereClause) {
if ($AST->whereClause->conditionalExpression instanceof ConditionalTerm) {
$AST->whereClause->conditionalExpression->conditionalFactors[] = $conditionalPrimary;
} elseif ($AST->whereClause->conditionalExpression instanceof ConditionalPrimary) {
$AST->whereClause->conditionalExpression = new ConditionalExpression(array(
new ConditionalTerm(array(
$AST->whereClause->conditionalExpression,
$conditionalPrimary
))
));
} elseif ($AST->whereClause->conditionalExpression instanceof ConditionalExpression) {
$tmpPrimary = new ConditionalPrimary;
$tmpPrimary->conditionalExpression = $AST->whereClause->conditionalExpression;
$AST->whereClause->conditionalExpression = new ConditionalTerm(array(
$tmpPrimary,
$conditionalPrimary
));
}
} else {
$AST->whereClause = new WhereClause(
new ConditionalExpression(array(
new ConditionalTerm(array(
$conditionalPrimary
))
))
);
}
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine\ORM;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Knp\Component\Pager\Event\ItemsEvent;
use Doctrine\ORM\QueryBuilder;
class QueryBuilderSubscriber implements EventSubscriberInterface
{
public function items(ItemsEvent $event)
{
if ($event->target instanceof QueryBuilder) {
// change target into query
$event->target = $event->target->getQuery();
}
}
public static function getSubscribedEvents()
{
return array(
'knp_pager.items' => array('items', 10/*make sure to transform before any further modifications*/)
);
}
}

View File

@@ -0,0 +1,148 @@
<?php
namespace Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine\ORM;
use Knp\Component\Pager\PaginatorInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Doctrine\ORM\Query\Parameter;
use Knp\Component\Pager\Event\ItemsEvent;
use Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine\ORM\Query\Helper as QueryHelper;
use Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine\ORM\Query\CountWalker;
use Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine\ORM\Query\WhereInWalker;
use Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine\ORM\Query\LimitSubqueryWalker;
use Doctrine\ORM\Query;
use Doctrine\ORM\Tools\Pagination\CountWalker as DoctrineCountWalker;
use Doctrine\ORM\Tools\Pagination\WhereInWalker as DoctrineWhereInWalker;
use Doctrine\ORM\Tools\Pagination\LimitSubqueryWalker as DoctrineLimitSubqueryWalker;
/**
* @deprecated see UsesPaginator
**/
class QuerySubscriber implements EventSubscriberInterface
{
/**
* Used if user set the count manually
*/
const HINT_COUNT = 'knp_paginator.count';
public function items(ItemsEvent $event)
{
if ($event->target instanceof Query) {
// process count
$useDoctrineWalkers = false;
$useDoctrineOutputWalker = false;
if (version_compare(\Doctrine\ORM\Version::VERSION, '2.3.0', '>=')) {
$useDoctrineWalkers = true;
$useDoctrineOutputWalker = true;
} else if (version_compare(\Doctrine\ORM\Version::VERSION, '2.2.0', '>=')) {
$useDoctrineWalkers = true;
}
if (($count = $event->target->getHint(self::HINT_COUNT)) !== false) {
$event->count = intval($count);
} else {
$countQuery = QueryHelper::cloneQuery($event->target);
if ($useDoctrineOutputWalker) {
$treeWalker = 'Doctrine\ORM\Tools\Pagination\CountOutputWalker';
$countQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, $treeWalker);
} else if ($useDoctrineWalkers) {
QueryHelper::addCustomTreeWalker($countQuery, 'Doctrine\ORM\Tools\Pagination\CountWalker');
} else {
$treeWalker = 'Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine\ORM\Query\CountWalker';
QueryHelper::addCustomTreeWalker($countQuery, $treeWalker);
}
if ($useDoctrineWalkers) {
$countQuery->setHint(
DoctrineCountWalker::HINT_DISTINCT,
$event->options[PaginatorInterface::DISTINCT]
);
} else {
$countQuery->setHint(
CountWalker::HINT_DISTINCT,
$event->options[PaginatorInterface::DISTINCT]
);
}
$countQuery
->setFirstResult(null)
->setMaxResults(null)
;
$countQuery->getEntityManager()->getConfiguration()->addCustomHydrationMode('asIs',
'Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine\ORM\Query\AsIsHydrator');
$countResult = $countQuery->getResult('asIs');
$event->count = intval(current(current($countResult)));
}
// process items
$result = null;
if ($event->count) {
if ($event->options[PaginatorInterface::DISTINCT]) {
$limitSubQuery = QueryHelper::cloneQuery($event->target);
$limitSubQuery
->setFirstResult($event->getOffset())
->setMaxResults($event->getLimit())
->useQueryCache(false)
;
QueryHelper::addCustomTreeWalker($limitSubQuery, $useDoctrineWalkers ?
'Doctrine\ORM\Tools\Pagination\LimitSubqueryWalker' :
'Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine\ORM\Query\LimitSubqueryWalker'
);
$ids = array_map('current', $limitSubQuery->getScalarResult());
// create where-in query
$whereInQuery = QueryHelper::cloneQuery($event->target);
QueryHelper::addCustomTreeWalker($whereInQuery, $useDoctrineWalkers ?
'Doctrine\ORM\Tools\Pagination\WhereInWalker' :
'Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine\ORM\Query\WhereInWalker'
);
$whereInQuery
->setHint($useDoctrineWalkers ?
DoctrineWhereInWalker::HINT_PAGINATOR_ID_COUNT :
WhereInWalker::HINT_PAGINATOR_ID_COUNT, count($ids)
)
->setFirstResult(null)
->setMaxResults(null)
;
if (version_compare(\Doctrine\ORM\Version::VERSION, '2.3.0', '>=') && count($ids) > 0) {
$whereInQuery->setParameter(WhereInWalker::PAGINATOR_ID_ALIAS, $ids);
} else {
$type = $limitSubQuery->getHint($useDoctrineWalkers ?
DoctrineLimitSubqueryWalker::IDENTIFIER_TYPE :
LimitSubqueryWalker::IDENTIFIER_TYPE
);
$idAlias = $useDoctrineWalkers ?
DoctrineWhereInWalker::PAGINATOR_ID_ALIAS :
WhereInWalker::PAGINATOR_ID_ALIAS
;
foreach ($ids as $i => $id) {
$whereInQuery->setParameter(
$idAlias . '_' . ++$i,
$id,
$type->getName()
);
}
}
$result = $whereInQuery->execute();
} else {
$event->target
->setFirstResult($event->getOffset())
->setMaxResults($event->getLimit())
;
$result = $event->target->execute();
}
} else {
$result = array(); // count is 0
}
$event->items = $result;
$event->stopPropagation();
}
}
public static function getSubscribedEvents()
{
return array(
'knp_pager.items' => array('items', 0)
);
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine\ORM\QuerySubscriber;
use Knp\Component\Pager\PaginatorInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Knp\Component\Pager\Event\ItemsEvent;
use Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine\ORM\QuerySubscriber;
use Doctrine\ORM\Query;
use Doctrine\ORM\Tools\Pagination\Paginator;
use Doctrine\ORM\Tools\Pagination\CountWalker;
class UsesPaginator implements EventSubscriberInterface
{
const HINT_FETCH_JOIN_COLLECTION = 'knp_paginator.fetch_join_collection';
public function items(ItemsEvent $event)
{
if (!class_exists('Doctrine\ORM\Tools\Pagination\Paginator')) {
return;
}
if (!$event->target instanceof Query) {
return;
}
$event->stopPropagation();
$useOutputWalkers = false;
if (isset($event->options['wrap-queries'])) {
$useOutputWalkers = $event->options['wrap-queries'];
}
$event->target
->setFirstResult($event->getOffset())
->setMaxResults($event->getLimit())
->setHint(CountWalker::HINT_DISTINCT, $event->options[PaginatorInterface::DISTINCT])
;
$fetchJoinCollection = true;
if ($event->target->hasHint(self::HINT_FETCH_JOIN_COLLECTION)) {
$fetchJoinCollection = $event->target->getHint(self::HINT_FETCH_JOIN_COLLECTION);
} else if (isset($event->options[PaginatorInterface::DISTINCT])) {
$fetchJoinCollection = $event->options[PaginatorInterface::DISTINCT];
}
$paginator = new Paginator($event->target, $fetchJoinCollection);
$paginator->setUseOutputWalkers($useOutputWalkers);
if (($count = $event->target->getHint(QuerySubscriber::HINT_COUNT)) !== false) {
$event->count = intval($count);
} else {
$event->count = count($paginator);
}
$event->items = iterator_to_array($paginator);
}
public static function getSubscribedEvents()
{
return array(
'knp_pager.items' => array('items', 0)
);
}
}

View File

@@ -0,0 +1,43 @@
<?php
namespace Knp\Component\Pager\Event\Subscriber\Paginate;
use Elastica\Query;
use Elastica\SearchableInterface;
use Knp\Component\Pager\Event\ItemsEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Elastica query pagination.
*
*/
class ElasticaQuerySubscriber implements EventSubscriberInterface
{
public function items(ItemsEvent $event)
{
if (is_array($event->target) && 2 === count($event->target) && reset($event->target) instanceof SearchableInterface && end($event->target) instanceof Query) {
list($searchable, $query) = $event->target;
$query->setFrom($event->getOffset());
$query->setSize($event->getLimit());
$results = $searchable->search($query);
$event->count = $results->getTotalHits();
if ($results->hasAggregations()) {
$event->setCustomPaginationParameter('aggregations', $results->getAggregations());
}
$event->setCustomPaginationParameter('resultSet', $results);
$event->items = $results->getResults();
$event->stopPropagation();
}
}
public static function getSubscribedEvents()
{
return array(
'knp_pager.items' => array('items', 0) /* triggers before a standard array subscriber*/
);
}
}

View File

@@ -0,0 +1,57 @@
<?php
namespace Knp\Component\Pager\Event\Subscriber\Paginate;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Knp\Component\Pager\Event\PaginationEvent;
use Knp\Component\Pager\Event\BeforeEvent;
use Knp\Component\Pager\Pagination\SlidingPagination;
class PaginationSubscriber implements EventSubscriberInterface
{
/**
* Lazy-load state tracker
* @var bool
*/
private $isLoaded = false;
public function pagination(PaginationEvent $event)
{
$event->setPagination(new SlidingPagination);
$event->stopPropagation();
}
public function before(BeforeEvent $event)
{
// Do not lazy-load more than once
if ($this->isLoaded) {
return;
}
$disp = $event->getEventDispatcher();
// hook all standard subscribers
$disp->addSubscriber(new ArraySubscriber);
$disp->addSubscriber(new Doctrine\ORM\QueryBuilderSubscriber);
$disp->addSubscriber(new Doctrine\ORM\QuerySubscriber\UsesPaginator);
$disp->addSubscriber(new Doctrine\ORM\QuerySubscriber);
$disp->addSubscriber(new Doctrine\ODM\MongoDB\QueryBuilderSubscriber);
$disp->addSubscriber(new Doctrine\ODM\MongoDB\QuerySubscriber);
$disp->addSubscriber(new Doctrine\ODM\PHPCR\QueryBuilderSubscriber);
$disp->addSubscriber(new Doctrine\ODM\PHPCR\QuerySubscriber);
$disp->addSubscriber(new Doctrine\CollectionSubscriber);
$disp->addSubscriber(new Doctrine\DBALQueryBuilderSubscriber);
$disp->addSubscriber(new PropelQuerySubscriber);
$disp->addSubscriber(new SolariumQuerySubscriber());
$disp->addSubscriber(new ElasticaQuerySubscriber());
$this->isLoaded = true;
}
public static function getSubscribedEvents()
{
return array(
'knp_pager.before' => array('before', 0),
'knp_pager.pagination' => array('pagination', 0)
);
}
}

View File

@@ -0,0 +1,52 @@
<?php
namespace Knp\Component\Pager\Event\Subscriber\Paginate;
use ModelCriteria;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Knp\Component\Pager\Event\ItemsEvent;
use Knp\Component\Pager\PaginatorInterface;
class PropelQuerySubscriber implements EventSubscriberInterface
{
public function items(ItemsEvent $event)
{
if ($event->target instanceof ModelCriteria) {
// process count
$countQuery = clone $event->target;
$countQuery
->limit(0)
->offset(0)
;
if ($event->options[PaginatorInterface::DISTINCT]) {
$countQuery->distinct();
}
$event->count = intval($countQuery->count());
// process items
$result = null;
if ($event->count) {
$resultQuery = clone $event->target;
if ($event->options[PaginatorInterface::DISTINCT]) {
$resultQuery->distinct();
}
$resultQuery
->offset($event->getOffset())
->limit($event->getLimit())
;
$result = $resultQuery->find();
} else {
$result = array(); // count is 0
}
$event->items = $result;
$event->stopPropagation();
}
}
public static function getSubscribedEvents()
{
return array(
'knp_pager.items' => array('items', 0)
);
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace Knp\Component\Pager\Event\Subscriber\Paginate;
use Knp\Component\Pager\Event\ItemsEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Solarium query pagination.
*
* @author Paweł Jędrzejewski <pjedrzejewski@diweb.pl>
*/
class SolariumQuerySubscriber implements EventSubscriberInterface
{
public function items(ItemsEvent $event)
{
if (is_array($event->target) && 2 == count($event->target)) {
$values = array_values($event->target);
list($client, $query) = $values;
if ($client instanceof \Solarium\Client && $query instanceof \Solarium\QueryType\Select\Query\Query) {
$query->setStart($event->getOffset())->setRows($event->getLimit());
$solrResult = $client->select($query);
$event->items = $solrResult->getIterator();
$event->count = $solrResult->getNumFound();
$event->setCustomPaginationParameter('result', $solrResult);
$event->stopPropagation();
}
}
}
public static function getSubscribedEvents()
{
return array(
'knp_pager.items' => array('items', 0) /* triggers before a standard array subscriber*/
);
}
}

View File

@@ -0,0 +1,128 @@
<?php
namespace Knp\Component\Pager\Event\Subscriber\Sortable;
use Knp\Component\Pager\Event\ItemsEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
use Knp\Component\Pager\PaginatorInterface;
class ArraySubscriber implements EventSubscriberInterface
{
/**
* @var string the field used to sort current object array list
*/
private $currentSortingField;
/**
* @var string the sorting direction
*/
private $sortDirection;
/**
* @var PropertyAccessorInterface
*/
private $propertyAccessor;
public function __construct(PropertyAccessorInterface $accessor = null)
{
if (!$accessor && class_exists('Symfony\Component\PropertyAccess\PropertyAccess')) {
$accessor = PropertyAccess::createPropertyAccessorBuilder()->enableMagicCall()->getPropertyAccessor();
}
$this->propertyAccessor = $accessor;
}
public function items(ItemsEvent $event)
{
// Check if the result has already been sorted by an other sort subscriber
$customPaginationParameters = $event->getCustomPaginationParameters();
if (!empty($customPaginationParameters['sorted']) ) {
return;
}
if (!is_array($event->target) || empty($_GET[$event->options[PaginatorInterface::SORT_FIELD_PARAMETER_NAME]])) {
return;
}
$event->setCustomPaginationParameter('sorted', true);
if (isset($event->options[PaginatorInterface::SORT_FIELD_WHITELIST]) && !in_array($_GET[$event->options[PaginatorInterface::SORT_FIELD_PARAMETER_NAME]], $event->options[PaginatorInterface::SORT_FIELD_WHITELIST])) {
throw new \UnexpectedValueException("Cannot sort by: [{$_GET[$event->options[PaginatorInterface::SORT_FIELD_PARAMETER_NAME]]}] this field is not in whitelist");
}
$sortFunction = isset($event->options['sortFunction']) ? $event->options['sortFunction'] : array($this, 'proxySortFunction');
$sortField = $_GET[$event->options[PaginatorInterface::SORT_FIELD_PARAMETER_NAME]];
// compatibility layer
if ($sortField[0] === '.') {
$sortField = substr($sortField, 1);
}
call_user_func_array($sortFunction, array(
&$event->target,
$sortField,
isset($_GET[$event->options[PaginatorInterface::SORT_DIRECTION_PARAMETER_NAME]]) && strtolower($_GET[$event->options[PaginatorInterface::SORT_DIRECTION_PARAMETER_NAME]]) === 'asc' ? 'asc' : 'desc'
));
}
private function proxySortFunction(&$target, $sortField, $sortDirection) {
$this->currentSortingField = $sortField;
$this->sortDirection = $sortDirection;
return usort($target, array($this, 'sortFunction'));
}
/**
* @param mixed $object1 first object to compare
* @param mixed $object2 second object to compare
*
* @return int
*/
private function sortFunction($object1, $object2)
{
if (!$this->propertyAccessor) {
throw new \UnexpectedValueException('You need symfony/property-access component to use this sorting function');
}
try {
$fieldValue1 = $this->propertyAccessor->getValue($object1, $this->currentSortingField);
} catch (UnexpectedTypeException $e) {
return -1 * $this->getSortCoefficient();
}
try {
$fieldValue2 = $this->propertyAccessor->getValue($object2, $this->currentSortingField);
} catch (UnexpectedTypeException $e) {
return 1 * $this->getSortCoefficient();
}
if (is_string($fieldValue1)) {
$fieldValue1 = mb_strtolower($fieldValue1);
}
if (is_string($fieldValue2)) {
$fieldValue2 = mb_strtolower($fieldValue2);
}
if ($fieldValue1 === $fieldValue2) {
return 0;
}
return ($fieldValue1 > $fieldValue2 ? 1 : -1) * $this->getSortCoefficient();
}
private function getSortCoefficient()
{
return $this->sortDirection === 'asc' ? 1 : -1;
}
public static function getSubscribedEvents()
{
return array(
'knp_pager.items' => array('items', 1)
);
}
}

View File

@@ -0,0 +1,53 @@
<?php
namespace Knp\Component\Pager\Event\Subscriber\Sortable\Doctrine\ODM\MongoDB;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Knp\Component\Pager\Event\ItemsEvent;
use Doctrine\ODM\MongoDB\Query\Query;
use Knp\Component\Pager\PaginatorInterface;
class QuerySubscriber implements EventSubscriberInterface
{
public function items(ItemsEvent $event)
{
// Check if the result has already been sorted by an other sort subscriber
$customPaginationParameters = $event->getCustomPaginationParameters();
if (!empty($customPaginationParameters['sorted']) ) {
return;
}
if ($event->target instanceof Query) {
$event->setCustomPaginationParameter('sorted', true);
if (isset($_GET[$event->options[PaginatorInterface::SORT_FIELD_PARAMETER_NAME]])) {
$field = $_GET[$event->options[PaginatorInterface::SORT_FIELD_PARAMETER_NAME]];
$dir = strtolower($_GET[$event->options[PaginatorInterface::SORT_DIRECTION_PARAMETER_NAME]]) == 'asc' ? 1 : -1;
if (isset($event->options[PaginatorInterface::SORT_FIELD_WHITELIST])) {
if (!in_array($field, $event->options[PaginatorInterface::SORT_FIELD_WHITELIST])) {
throw new \UnexpectedValueException("Cannot sort by: [{$field}] this field is not in whitelist");
}
}
static $reflectionProperty;
if (is_null($reflectionProperty)) {
$reflectionClass = new \ReflectionClass('Doctrine\MongoDB\Query\Query');
$reflectionProperty = $reflectionClass->getProperty('query');
$reflectionProperty->setAccessible(true);
}
$queryOptions = $reflectionProperty->getValue($event->target);
//@todo: seems like does not support multisort ??
$queryOptions['sort'] = array($field => $dir);
$reflectionProperty->setValue($event->target, $queryOptions);
}
}
}
public static function getSubscribedEvents()
{
return array(
'knp_pager.items' => array('items', 1)
);
}
}

View File

@@ -0,0 +1,96 @@
<?php
namespace Knp\Component\Pager\Event\Subscriber\Sortable\Doctrine\ORM\Query;
use Doctrine\ORM\Query\TreeWalkerAdapter,
Doctrine\ORM\Query\AST\SelectStatement,
Doctrine\ORM\Query\AST\PathExpression,
Doctrine\ORM\Query\AST\OrderByItem,
Doctrine\ORM\Query\AST\OrderByClause;
/**
* OrderBy Query TreeWalker for Sortable functionality
* in doctrine paginator
*/
class OrderByWalker extends TreeWalkerAdapter
{
/**
* Sort key alias hint name
*/
const HINT_PAGINATOR_SORT_ALIAS = 'knp_paginator.sort.alias';
/**
* Sort key field hint name
*/
const HINT_PAGINATOR_SORT_FIELD = 'knp_paginator.sort.field';
/**
* Sort direction hint name
*/
const HINT_PAGINATOR_SORT_DIRECTION = 'knp_paginator.sort.direction';
/**
* Walks down a SelectStatement AST node, modifying it to
* sort the query like requested by url
*
* @param SelectStatement $AST
* @return void
*/
public function walkSelectStatement(SelectStatement $AST)
{
$query = $this->_getQuery();
$fields = (array)$query->getHint(self::HINT_PAGINATOR_SORT_FIELD);
$aliases = (array)$query->getHint(self::HINT_PAGINATOR_SORT_ALIAS);
$components = $this->_getQueryComponents();
foreach ($fields as $index => $field) {
if (!$field) {
continue;
}
$alias = $aliases[$index];
if ($alias !== false) {
if (!array_key_exists($alias, $components)) {
throw new \UnexpectedValueException("There is no component aliased by [{$alias}] in the given Query");
}
$meta = $components[$alias];
if (!$meta['metadata']->hasField($field)) {
throw new \UnexpectedValueException("There is no such field [{$field}] in the given Query component, aliased by [$alias]");
}
} else {
if (!array_key_exists($field, $components)) {
throw new \UnexpectedValueException("There is no component field [{$field}] in the given Query");
}
}
$direction = $query->getHint(self::HINT_PAGINATOR_SORT_DIRECTION);
if ($alias !== false) {
$pathExpression = new PathExpression(PathExpression::TYPE_STATE_FIELD, $alias, $field);
$pathExpression->type = PathExpression::TYPE_STATE_FIELD;
} else {
$pathExpression = $field;
}
$orderByItem = new OrderByItem($pathExpression);
$orderByItem->type = $direction;
if ($AST->orderByClause) {
$set = false;
foreach ($AST->orderByClause->orderByItems as $item) {
if ($item->expression instanceof PathExpression) {
if ($item->expression->identificationVariable === $alias && $item->expression->field === $field) {
$item->type = $direction;
$set = true;
break;
}
}
}
if (!$set) {
array_unshift($AST->orderByClause->orderByItems, $orderByItem);
}
} else {
$AST->orderByClause = new OrderByClause(array($orderByItem));
}
}
}
}

View File

@@ -0,0 +1,63 @@
<?php
namespace Knp\Component\Pager\Event\Subscriber\Sortable\Doctrine\ORM;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Knp\Component\Pager\Event\ItemsEvent;
use Knp\Component\Pager\Event\Subscriber\Sortable\Doctrine\ORM\Query\OrderByWalker;
use Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine\ORM\Query\Helper as QueryHelper;
use Doctrine\ORM\Query;
use Knp\Component\Pager\PaginatorInterface;
class QuerySubscriber implements EventSubscriberInterface
{
public function items(ItemsEvent $event)
{
// Check if the result has already been sorted by an other sort subscriber
$customPaginationParameters = $event->getCustomPaginationParameters();
if (!empty($customPaginationParameters['sorted']) ) {
return;
}
if ($event->target instanceof Query) {
$event->setCustomPaginationParameter('sorted', true);
if (isset($_GET[$event->options[PaginatorInterface::SORT_FIELD_PARAMETER_NAME]])) {
$dir = isset($_GET[$event->options[PaginatorInterface::SORT_DIRECTION_PARAMETER_NAME]]) && strtolower($_GET[$event->options[PaginatorInterface::SORT_DIRECTION_PARAMETER_NAME]]) === 'asc' ? 'asc' : 'desc';
if (isset($event->options[PaginatorInterface::SORT_FIELD_WHITELIST])) {
if (!in_array($_GET[$event->options[PaginatorInterface::SORT_FIELD_PARAMETER_NAME]], $event->options[PaginatorInterface::SORT_FIELD_WHITELIST])) {
throw new \UnexpectedValueException("Cannot sort by: [{$_GET[$event->options[PaginatorInterface::SORT_FIELD_PARAMETER_NAME]]}] this field is not in whitelist");
}
}
$sortFieldParameterNames = $_GET[$event->options[PaginatorInterface::SORT_FIELD_PARAMETER_NAME]];
$fields = array();
$aliases = array();
foreach (explode('+', $sortFieldParameterNames) as $sortFieldParameterName) {
$parts = explode('.', $sortFieldParameterName, 2);
// We have to prepend the field. Otherwise OrderByWalker will add
// the order-by items in the wrong order
array_unshift($fields, end($parts));
array_unshift($aliases, 2 <= count($parts) ? reset($parts) : false);
}
$event->target
->setHint(OrderByWalker::HINT_PAGINATOR_SORT_DIRECTION, $dir)
->setHint(OrderByWalker::HINT_PAGINATOR_SORT_FIELD, $fields)
->setHint(OrderByWalker::HINT_PAGINATOR_SORT_ALIAS, $aliases)
;
QueryHelper::addCustomTreeWalker($event->target, 'Knp\Component\Pager\Event\Subscriber\Sortable\Doctrine\ORM\Query\OrderByWalker');
}
}
}
public static function getSubscribedEvents()
{
return array(
'knp_pager.items' => array('items', 1)
);
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace Knp\Component\Pager\Event\Subscriber\Sortable;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Knp\Component\Pager\Event\ItemsEvent;
use Elastica\Query;
use Elastica\SearchableInterface;
use Knp\Component\Pager\PaginatorInterface;
class ElasticaQuerySubscriber implements EventSubscriberInterface
{
public function items(ItemsEvent $event)
{
// Check if the result has already been sorted by an other sort subscriber
$customPaginationParameters = $event->getCustomPaginationParameters();
if (!empty($customPaginationParameters['sorted']) ) {
return;
}
if (is_array($event->target) && 2 === count($event->target) && reset($event->target) instanceof SearchableInterface && end($event->target) instanceof Query) {
$event->setCustomPaginationParameter('sorted', true);
list($searchable, $query) = $event->target;
if (isset($_GET[$event->options[PaginatorInterface::SORT_FIELD_PARAMETER_NAME]])) {
$field = $_GET[$event->options[PaginatorInterface::SORT_FIELD_PARAMETER_NAME]];
$dir = isset($_GET[$event->options[PaginatorInterface::SORT_DIRECTION_PARAMETER_NAME]]) && strtolower($_GET[$event->options[PaginatorInterface::SORT_DIRECTION_PARAMETER_NAME]]) === 'asc' ? 'asc' : 'desc';
if (isset($event->options[PaginatorInterface::SORT_FIELD_WHITELIST]) && !in_array($field, $event->options[PaginatorInterface::SORT_FIELD_WHITELIST])) {
throw new \UnexpectedValueException(sprintf('Cannot sort by: [%s] this field is not in whitelist',$field));
}
$query->setSort(array(
$field => array('order' => $dir),
));
}
}
}
public static function getSubscribedEvents()
{
return array(
'knp_pager.items' => array('items', 1)
);
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace Knp\Component\Pager\Event\Subscriber\Sortable;
use Knp\Component\Pager\PaginatorInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Knp\Component\Pager\Event\ItemsEvent;
class PropelQuerySubscriber implements EventSubscriberInterface
{
public function items(ItemsEvent $event)
{
// Check if the result has already been sorted by an other sort subscriber
$customPaginationParameters = $event->getCustomPaginationParameters();
if (!empty($customPaginationParameters['sorted']) ) {
return;
}
$query = $event->target;
if ($query instanceof \ModelCriteria) {
$event->setCustomPaginationParameter('sorted', true);
if (isset($_GET[$event->options[PaginatorInterface::SORT_FIELD_PARAMETER_NAME]])) {
$part = $_GET[$event->options[PaginatorInterface::SORT_FIELD_PARAMETER_NAME]];
$directionParam = $event->options[PaginatorInterface::SORT_DIRECTION_PARAMETER_NAME];
$direction = (isset($_GET[$directionParam]) && strtolower($_GET[$directionParam]) === 'asc')
? 'asc' : 'desc';
if (isset($event->options[PaginatorInterface::SORT_FIELD_WHITELIST])) {
if (!in_array($_GET[$event->options[PaginatorInterface::SORT_FIELD_PARAMETER_NAME]], $event->options[PaginatorInterface::SORT_FIELD_WHITELIST])) {
throw new \UnexpectedValueException("Cannot sort by: [{$_GET[$event->options[PaginatorInterface::SORT_FIELD_PARAMETER_NAME]]}] this field is not in whitelist");
}
}
$query->orderBy($part, $direction);
}
}
}
public static function getSubscribedEvents()
{
return array(
'knp_pager.items' => array('items', 1)
);
}
}

View File

@@ -0,0 +1,57 @@
<?php
namespace Knp\Component\Pager\Event\Subscriber\Sortable;
use Knp\Component\Pager\Event\ItemsEvent;
use Knp\Component\Pager\PaginatorInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Solarium query sorting
*
* @author Marek Kalnik <marekk@theodo.fr>
*/
class SolariumQuerySubscriber implements EventSubscriberInterface
{
public function items(ItemsEvent $event)
{
// Check if the result has already been sorted by an other sort subscriber
$customPaginationParameters = $event->getCustomPaginationParameters();
if (!empty($customPaginationParameters['sorted']) ) {
return;
}
if (is_array($event->target) && 2 == count($event->target)) {
$event->setCustomPaginationParameter('sorted', true);
$values = array_values($event->target);
list($client, $query) = $values;
if ($client instanceof \Solarium\Client && $query instanceof \Solarium\QueryType\Select\Query\Query) {
if (isset($_GET[$event->options[PaginatorInterface::SORT_FIELD_PARAMETER_NAME]])) {
if (isset($event->options[PaginatorInterface::SORT_FIELD_WHITELIST])) {
if (!in_array($_GET[$event->options[PaginatorInterface::SORT_FIELD_PARAMETER_NAME]], $event->options[PaginatorInterface::SORT_FIELD_WHITELIST])) {
throw new \UnexpectedValueException("Cannot sort by: [{$_GET[$event->options[PaginatorInterface::SORT_FIELD_PARAMETER_NAME]]}] this field is not in whitelist");
}
}
$query->addSort($_GET[$event->options[PaginatorInterface::SORT_FIELD_PARAMETER_NAME]], $this->getSortDirection($event));
}
}
}
}
public static function getSubscribedEvents()
{
return array(
// trigger before the pagination subscriber
'knp_pager.items' => array('items', 1),
);
}
private function getSortDirection($event)
{
return isset($_GET[$event->options[PaginatorInterface::SORT_DIRECTION_PARAMETER_NAME]]) &&
strtolower($_GET[$event->options[PaginatorInterface::SORT_DIRECTION_PARAMETER_NAME]]) === 'asc' ? 'asc' : 'desc';
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace Knp\Component\Pager\Event\Subscriber\Sortable;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Knp\Component\Pager\Event\BeforeEvent;
class SortableSubscriber implements EventSubscriberInterface
{
/**
* Lazy-load state tracker
* @var bool
*/
private $isLoaded = false;
public function before(BeforeEvent $event)
{
// Do not lazy-load more than once
if ($this->isLoaded) {
return;
}
$disp = $event->getEventDispatcher();
// hook all standard sortable subscribers
$disp->addSubscriber(new Doctrine\ORM\QuerySubscriber());
$disp->addSubscriber(new Doctrine\ODM\MongoDB\QuerySubscriber());
$disp->addSubscriber(new ElasticaQuerySubscriber());
$disp->addSubscriber(new PropelQuerySubscriber());
$disp->addSubscriber(new SolariumQuerySubscriber());
$disp->addSubscriber(new ArraySubscriber());
$this->isLoaded = true;
}
public static function getSubscribedEvents()
{
return array(
'knp_pager.before' => array('before', 1)
);
}
}

View File

@@ -0,0 +1,185 @@
<?php
namespace Knp\Component\Pager\Pagination;
use Countable, Iterator, ArrayAccess;
abstract class AbstractPagination implements PaginationInterface, Countable, Iterator, ArrayAccess
{
protected $currentPageNumber;
protected $numItemsPerPage;
protected $items = array();
protected $totalCount;
protected $paginatorOptions;
protected $customParameters;
/**
* {@inheritDoc}
*/
public function rewind() {
reset($this->items);
}
/**
* {@inheritDoc}
*/
public function current() {
return current($this->items);
}
/**
* {@inheritDoc}
*/
public function key() {
return key($this->items);
}
/**
* {@inheritDoc}
*/
public function next() {
next($this->items);
}
/**
* {@inheritDoc}
*/
public function valid() {
return key($this->items) !== null;
}
/**
* {@inheritDoc}
*/
public function count()
{
return count($this->items);
}
public function setCustomParameters(array $parameters)
{
$this->customParameters = $parameters;
}
public function getCustomParameter($name)
{
return isset($this->customParameters[$name]) ? $this->customParameters[$name] : null;
}
/**
* {@inheritDoc}
*/
public function setCurrentPageNumber($pageNumber)
{
$this->currentPageNumber = $pageNumber;
}
/**
* Get currently used page number
*
* @return integer
*/
public function getCurrentPageNumber()
{
return $this->currentPageNumber;
}
/**
* {@inheritDoc}
*/
public function setItemNumberPerPage($numItemsPerPage)
{
$this->numItemsPerPage = $numItemsPerPage;
}
/**
* Get number of items per page
*
* @return integer
*/
public function getItemNumberPerPage()
{
return $this->numItemsPerPage;
}
/**
* {@inheritDoc}
*/
public function setTotalItemCount($numTotal)
{
$this->totalCount = $numTotal;
}
/**
* Get total item number available
*
* @return integer
*/
public function getTotalItemCount()
{
return $this->totalCount;
}
/**
* {@inheritDoc}
*/
public function setPaginatorOptions($options)
{
$this->paginatorOptions = $options;
}
/**
* Get pagination alias
*
* @return string
*/
public function getPaginatorOption($name)
{
return isset($this->paginatorOptions[$name]) ? $this->paginatorOptions[$name] : null;
}
/**
* {@inheritDoc}
*/
public function setItems($items)
{
if (!is_array($items) && !$items instanceof \Traversable) {
throw new \UnexpectedValueException("Items must be an array type");
}
$this->items = $items;
}
/**
* Get current items
*
* @return array
*/
public function getItems()
{
return $this->items;
}
public function offsetExists($offset)
{
return array_key_exists($offset, $this->items);
}
public function offsetGet($offset)
{
return $this->items[$offset];
}
public function offsetSet($offset, $value)
{
if (null === $offset) {
$this->items[] = $value;
} else {
$this->items[$offset] = $value;
}
}
public function offsetUnset($offset)
{
unset($this->items[$offset]);
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace Knp\Component\Pager\Pagination;
/**
* Pagination interface strictly defines
* the methods - paginator will use to populate the
* pagination data
*/
interface PaginationInterface
{
/**
* @param integer $pageNumber
*/
function setCurrentPageNumber($pageNumber);
/**
* @param integer $numItemsPerPage
*/
function setItemNumberPerPage($numItemsPerPage);
/**
* @param integer $numTotal
*/
function setTotalItemCount($numTotal);
/**
* @param mixed $items
*/
function setItems($items);
/**
* @param string $options
*/
function setPaginatorOptions($options);
/**
* @param array $parameters
*/
function setCustomParameters(array $parameters);
}

View File

@@ -0,0 +1,99 @@
<?php
namespace Knp\Component\Pager\Pagination;
use Closure;
/**
* @todo: find a way to avoid exposing private member setters
*
* Sliding pagination
*/
class SlidingPagination extends AbstractPagination
{
/**
* Pagination page range
*
* @var integer
*/
private $range = 5;
/**
* Closure which is executed to render pagination
*
* @var Closure
*/
public $renderer;
public function setPageRange($range)
{
$this->range = intval(abs($range));
}
/**
* Renders the pagination
*/
public function __toString()
{
$data = $this->getPaginationData();
$output = '';
if (!$this->renderer instanceof Closure) {
$output = 'override in order to render a template';
} else {
$output = call_user_func($this->renderer, $data);
}
return $output;
}
public function getPaginationData()
{
$pageCount = intval(ceil($this->totalCount / $this->numItemsPerPage));
$current = $this->currentPageNumber;
if ($this->range > $pageCount) {
$this->range = $pageCount;
}
$delta = ceil($this->range / 2);
if ($current - $delta > $pageCount - $this->range) {
$pages = range($pageCount - $this->range + 1, $pageCount);
} else {
if ($current - $delta < 0) {
$delta = $current;
}
$offset = $current - $delta;
$pages = range($offset + 1, $offset + $this->range);
}
$viewData = array(
'last' => $pageCount,
'current' => $current,
'numItemsPerPage' => $this->numItemsPerPage,
'first' => 1,
'pageCount' => $pageCount,
'totalCount' => $this->totalCount,
);
$viewData = array_merge($viewData, $this->paginatorOptions, $this->customParameters);
if ($current - 1 > 0) {
$viewData['previous'] = $current - 1;
}
if ($current + 1 <= $pageCount) {
$viewData['next'] = $current + 1;
}
$viewData['pagesInRange'] = $pages;
$viewData['firstPageInRange'] = min($pages);
$viewData['lastPageInRange'] = max($pages);
if ($this->getItems() !== null) {
$viewData['currentItemCount'] = $this->count();
$viewData['firstItemNumber'] = (($current - 1) * $this->numItemsPerPage) + 1;
$viewData['lastItemNumber'] = $viewData['firstItemNumber'] + $viewData['currentItemCount'] - 1;
}
return $viewData;
}
}

View File

@@ -0,0 +1,162 @@
<?php
namespace Knp\Component\Pager;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Knp\Component\Pager\Event\Subscriber\Paginate\PaginationSubscriber;
use Knp\Component\Pager\Event\Subscriber\Sortable\SortableSubscriber;
use Knp\Component\Pager\Event;
/**
* Paginator uses event dispatcher to trigger pagination
* lifecycle events. Subscribers are expected to paginate
* wanted target and finally it generates pagination view
* which is only the result of paginator
*/
class Paginator implements PaginatorInterface
{
/**
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
*/
protected $eventDispatcher;
/**
* Default options of paginator
*
* @var array
*/
protected $defaultOptions = array(
self::PAGE_PARAMETER_NAME => 'page',
self::SORT_FIELD_PARAMETER_NAME => 'sort',
self::SORT_DIRECTION_PARAMETER_NAME => 'direction',
self::FILTER_FIELD_PARAMETER_NAME => 'filterParam',
self::FILTER_VALUE_PARAMETER_NAME => 'filterValue',
self::DISTINCT => true
);
/**
* Initialize paginator with event dispatcher
* Can be a service in concept. By default it
* hooks standard pagination subscriber
*
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $eventDispatcher
*/
public function __construct(EventDispatcherInterface $eventDispatcher = null)
{
$this->eventDispatcher = $eventDispatcher;
if (is_null($this->eventDispatcher)) {
$this->eventDispatcher = new EventDispatcher;
$this->eventDispatcher->addSubscriber(new PaginationSubscriber);
$this->eventDispatcher->addSubscriber(new SortableSubscriber);
}
}
/**
* Override the default paginator options
* to be reused for paginations
*
* @param array $options
*/
public function setDefaultPaginatorOptions(array $options)
{
$this->defaultOptions = array_merge($this->defaultOptions, $options);
}
/**
* Paginates anything (depending on event listeners)
* into Pagination object, which is a view targeted
* pagination object (might be aggregated helper object)
* responsible for the pagination result representation
*
* @param mixed $target - anything what needs to be paginated
* @param integer $page - page number, starting from 1
* @param integer $limit - number of items per page
* @param array $options - less used options:
* boolean $distinct - default true for distinction of results
* string $alias - pagination alias, default none
* array $whitelist - sortable whitelist for target fields being paginated
* @throws \LogicException
* @return \Knp\Component\Pager\Pagination\PaginationInterface
*/
public function paginate($target, $page = 1, $limit = 10, array $options = array())
{
$page = (int) $page;
$limit = intval(abs($limit));
if (!$limit) {
throw new \LogicException("Invalid item per page number, must be a positive number");
}
$offset = abs($page - 1) * $limit;
$options = array_merge($this->defaultOptions, $options);
// normalize default sort field
if (isset($options[self::DEFAULT_SORT_FIELD_NAME]) && is_array($options[self::DEFAULT_SORT_FIELD_NAME])) {
$options[self::DEFAULT_SORT_FIELD_NAME] = implode('+', $options[self::DEFAULT_SORT_FIELD_NAME]);
}
// default sort field and direction are set based on options (if available)
if (!isset($_GET[$options[self::SORT_FIELD_PARAMETER_NAME]]) && isset($options[self::DEFAULT_SORT_FIELD_NAME])) {
$_GET[$options[self::SORT_FIELD_PARAMETER_NAME]] = $options[self::DEFAULT_SORT_FIELD_NAME];
if (!isset($_GET[$options[self::SORT_DIRECTION_PARAMETER_NAME]])) {
$_GET[$options[self::SORT_DIRECTION_PARAMETER_NAME]] = isset($options[self::DEFAULT_SORT_DIRECTION]) ? $options[self::DEFAULT_SORT_DIRECTION] : 'asc';
}
}
// before pagination start
$beforeEvent = new Event\BeforeEvent($this->eventDispatcher);
$this->eventDispatcher->dispatch('knp_pager.before', $beforeEvent);
// items
$itemsEvent = new Event\ItemsEvent($offset, $limit);
$itemsEvent->options = &$options;
$itemsEvent->target = &$target;
$this->eventDispatcher->dispatch('knp_pager.items', $itemsEvent);
if (!$itemsEvent->isPropagationStopped()) {
throw new \RuntimeException('One of listeners must count and slice given target');
}
// pagination initialization event
$paginationEvent = new Event\PaginationEvent;
$paginationEvent->target = &$target;
$paginationEvent->options = &$options;
$this->eventDispatcher->dispatch('knp_pager.pagination', $paginationEvent);
if (!$paginationEvent->isPropagationStopped()) {
throw new \RuntimeException('One of listeners must create pagination view');
}
// pagination class can be different, with different rendering methods
$paginationView = $paginationEvent->getPagination();
$paginationView->setCustomParameters($itemsEvent->getCustomPaginationParameters());
$paginationView->setCurrentPageNumber($page);
$paginationView->setItemNumberPerPage($limit);
$paginationView->setTotalItemCount($itemsEvent->count);
$paginationView->setPaginatorOptions($options);
$paginationView->setItems($itemsEvent->items);
// after
$afterEvent = new Event\AfterEvent($paginationView);
$this->eventDispatcher->dispatch('knp_pager.after', $afterEvent);
return $paginationView;
}
/**
* Hooks in the given event subscriber
*
* @param \Symfony\Component\EventDispatcher\EventSubscriberInterface $subscriber
*/
public function subscribe(EventSubscriberInterface $subscriber)
{
$this->eventDispatcher->addSubscriber($subscriber);
}
/**
* Hooks the listener to the given event name
*
* @param string $eventName
* @param object $listener
* @param integer $priority
*/
public function connect($eventName, $listener, $priority = 0)
{
$this->eventDispatcher->addListener($eventName, $listener, $priority);
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace Knp\Component\Pager;
/**
* PaginatorInterface
*/
interface PaginatorInterface
{
const DEFAULT_SORT_FIELD_NAME = 'defaultSortFieldName';
const DEFAULT_SORT_DIRECTION = 'defaultSortDirection';
const DEFAULT_FILTER_FIELDS = 'defaultFilterFields';
const SORT_FIELD_PARAMETER_NAME = 'sortFieldParameterName';
const SORT_FIELD_WHITELIST = 'sortFieldWhitelist';
const SORT_DIRECTION_PARAMETER_NAME = 'sortDirectionParameterName';
const PAGE_PARAMETER_NAME = 'pageParameterName';
const FILTER_FIELD_PARAMETER_NAME = 'filterFieldParameterName';
const FILTER_VALUE_PARAMETER_NAME = 'filterValueParameterName';
const FILTER_FIELD_WHITELIST = 'filterFieldWhitelist';
const DISTINCT = 'distinct';
/**
* Paginates anything (depending on event listeners)
* into Pagination object, which is a view targeted
* pagination object (might be aggregated helper object)
* responsible for the pagination result representation
*
* @param mixed $target - anything what needs to be paginated
* @param integer $page - page number, starting from 1
* @param integer $limit - number of items per page
* @param array $options - less used options:
* boolean $distinct - default true for distinction of results
* string $alias - pagination alias, default none
* array $whitelist - sortable whitelist for target fields being paginated
* @throws \LogicException
* @return \Knp\Component\Pager\Pagination\PaginationInterface
*/
function paginate($target, $page = 1, $limit = 10, array $options = array());
}

View File

@@ -0,0 +1,36 @@
<?php
namespace Test\Fixture\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
/**
* @ODM\Document
*/
class Article
{
/**
* @ODM\Id
*/
private $id;
/**
* @ODM\Field(type="string")
*/
private $title;
public function getId()
{
return $this->id;
}
public function setTitle($title)
{
$this->title = $title;
}
public function getTitle()
{
return $this->title;
}
}

View File

@@ -0,0 +1,63 @@
<?php
namespace Test\Fixture\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
/**
* @ODM\Document
*/
class Image
{
/**
* @ODM\Id
*/
private $id;
/**
* @ODM\Field
*/
private $title;
/**
* @ODM\File
*/
private $file;
/**
* Set file.
*
* @param integer $file
* @return Image
*/
public function setFile($file)
{
$this->file = $file;
return $this;
}
/**
* Get file.
*
* @return integer
*/
public function getFile()
{
return $this->file;
}
public function getId()
{
return $this->id;
}
public function setTitle($title)
{
$this->title = $title;
}
public function getTitle()
{
return $this->title;
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace Test\Fixture\Document\PHPCR;
use Doctrine\ODM\PHPCR\Mapping\Annotations as PHPCR;
/**
* @PHPCR\Document
*/
class Article
{
/**
* @PHPCR\Id
*/
private $id;
/**
* @PHPCR\ParentDocument
*/
private $parent;
/**
* @PHPCR\Field(type="string")
*/
private $title;
public function getId()
{
return $this->id;
}
public function getParent()
{
return $this->parent;
}
public function setParent($parent)
{
$this->parent = $parent;
}
public function setTitle($title)
{
$this->title = $title;
}
public function getTitle()
{
return $this->title;
}
}

View File

@@ -0,0 +1,53 @@
<?php
namespace Test\Fixture\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
*/
class Article
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(length=64)
*/
private $title;
/**
* @ORM\Column(type="boolean")
*/
private $enabled = true;
public function getId()
{
return $this->id;
}
public function setTitle($title)
{
$this->title = $title;
}
public function getTitle()
{
return $this->title;
}
public function setEnabled($enabled)
{
$this->enabled = $enabled;
}
public function isEnabled()
{
return $this->enabled;
}
}

View File

@@ -0,0 +1,78 @@
<?php
namespace Test\Fixture\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
*/
class Composite
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(length=64)
*/
private $title;
/**
* @ORM\Id
* @ORM\Column(type="string")
*/
private $uid;
/**
* Sets uid.
*
* @param mixed $uid
*/
public function setUid($uid)
{
$this->uid = $uid;
}
/**
* Returns uid.
*
* @return mixed
*/
public function getUid()
{
return $this->uid;
}
/**
* Sets Id.
*
* @param $id
*/
public function setId($id)
{
$this->id = $id;
}
/**
* Returns Id.
*
* @return integer
*/
public function getId()
{
return $this->id;
}
public function setTitle($title)
{
$this->title = $title;
}
public function getTitle()
{
return $this->title;
}
}

View File

@@ -0,0 +1,95 @@
<?php
namespace Test\Fixture\Entity\Shop;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* @ORM\Entity
*/
class Product
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(length=64)
*/
private $title;
/**
* @ORM\Column(length=255, nullable=true)
*/
private $description;
/**
* @ORM\Column(type="float", nullable=false)
*/
private $price;
/**
* @ORM\ManyToMany(targetEntity="Tag")
*/
private $tags;
/**
* @ORM\Column(type="integer")
*/
private $numTags = 0;
public function __construct()
{
$this->tags = new ArrayCollection;
}
public function getId()
{
return $this->id;
}
public function setTitle($title)
{
$this->title = $title;
}
public function getTitle()
{
return $this->title;
}
public function setDescription($description)
{
$this->description = $description;
}
public function getDescription()
{
return $this->description;
}
public function setPrice($price)
{
$this->price = $price;
}
public function getPrice()
{
return $this->price;
}
public function addTag(Tag $tag)
{
$this->numTags++;
$this->tags[] = $tag;
}
public function getTags()
{
return $this->tags;
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace Test\Fixture\Entity\Shop;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
*/
class Tag
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(length=64)
*/
private $name;
public function getId()
{
return $this->id;
}
public function setName($name)
{
$this->name = $name;
}
public function getName()
{
return $this->name;
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace Test\Mock;
use Knp\Component\Pager\Event\ItemsEvent;
use Knp\Component\Pager\Event\Subscriber\Paginate\ArraySubscriber;
class CustomParameterSubscriber extends ArraySubscriber
{
static function getSubscribedEvents()
{
return array(
'knp_pager.items' => array('items', 10)
);
}
function items(ItemsEvent $e)
{
$e->setCustomPaginationParameter('test', 'val');
parent::items($e);
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace Test\Mock;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Knp\Component\Pager\Event\PaginationEvent;
use Knp\Component\Pager\Pagination\SlidingPagination;
class PaginationSubscriber implements EventSubscriberInterface
{
static function getSubscribedEvents()
{
return array(
'knp_pager.pagination' => array('pagination', 0)
);
}
function pagination(PaginationEvent $e)
{
$e->setPagination(new SlidingPagination);
$e->stopPropagation();
}
}

View File

@@ -0,0 +1,63 @@
<?php
use Test\Tool\BaseTestCase;
use Knp\Component\Pager\Paginator;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Test\Mock\PaginationSubscriber as MockPaginationSubscriber;
use Test\Mock\CustomParameterSubscriber;
use Knp\Component\Pager\Event\Subscriber\Paginate\ArraySubscriber;
use Knp\Component\Pager\PaginatorInterface;
class AbstractPaginationTest extends BaseTestCase
{
/**
* @test
*/
function shouldCustomizeParameterNames()
{
$dispatcher = new EventDispatcher;
$dispatcher->addSubscriber(new MockPaginationSubscriber); // pagination view
$dispatcher->addSubscriber(new ArraySubscriber);
$p = new Paginator($dispatcher);
$items = array('first', 'second');
$view = $p->paginate($items, 1, 10);
// test default names first
$this->assertEquals('page', $view->getPaginatorOption(PaginatorInterface::PAGE_PARAMETER_NAME));
$this->assertEquals('sort', $view->getPaginatorOption(PaginatorInterface::SORT_FIELD_PARAMETER_NAME));
$this->assertEquals('direction', $view->getPaginatorOption(PaginatorInterface::SORT_DIRECTION_PARAMETER_NAME));
$this->assertTrue($view->getPaginatorOption(PaginatorInterface::DISTINCT));
$this->assertNull($view->getPaginatorOption(PaginatorInterface::SORT_FIELD_WHITELIST));
// now customize
$options = array(
PaginatorInterface::PAGE_PARAMETER_NAME => 'p',
PaginatorInterface::SORT_FIELD_PARAMETER_NAME => 's',
PaginatorInterface::SORT_DIRECTION_PARAMETER_NAME => 'd',
PaginatorInterface::DISTINCT => false,
PaginatorInterface::SORT_FIELD_WHITELIST => array('a.f', 'a.d')
);
$view = $p->paginate($items, 1, 10, $options);
$this->assertEquals('p', $view->getPaginatorOption(PaginatorInterface::PAGE_PARAMETER_NAME));
$this->assertEquals('s', $view->getPaginatorOption(PaginatorInterface::SORT_FIELD_PARAMETER_NAME));
$this->assertEquals('d', $view->getPaginatorOption(PaginatorInterface::SORT_DIRECTION_PARAMETER_NAME));
$this->assertFalse($view->getPaginatorOption(PaginatorInterface::DISTINCT));
$this->assertEquals(array('a.f', 'a.d'), $view->getPaginatorOption(PaginatorInterface::SORT_FIELD_WHITELIST));
// change default paginator options
$p->setDefaultPaginatorOptions(array(
PaginatorInterface::PAGE_PARAMETER_NAME => 'pg',
PaginatorInterface::SORT_FIELD_PARAMETER_NAME => 'srt',
PaginatorInterface::SORT_DIRECTION_PARAMETER_NAME => 'dir'
));
$view = $p->paginate($items, 1, 10);
$this->assertEquals('pg', $view->getPaginatorOption(PaginatorInterface::PAGE_PARAMETER_NAME));
$this->assertEquals('srt', $view->getPaginatorOption(PaginatorInterface::SORT_FIELD_PARAMETER_NAME));
$this->assertEquals('dir', $view->getPaginatorOption(PaginatorInterface::SORT_DIRECTION_PARAMETER_NAME));
$this->assertTrue($view->getPaginatorOption(PaginatorInterface::DISTINCT));
}
}

View File

@@ -0,0 +1,27 @@
<?php
use Test\Tool\BaseTestCase;
use Knp\Component\Pager\Paginator;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Test\Mock\PaginationSubscriber as MockPaginationSubscriber;
use Test\Mock\CustomParameterSubscriber;
class CustomParameterTest extends BaseTestCase
{
/**
* @test
*/
function shouldGiveCustomParametersToPaginationView()
{
$dispatcher = new EventDispatcher;
$dispatcher->addSubscriber(new CustomParameterSubscriber);
$dispatcher->addSubscriber(new MockPaginationSubscriber); // pagination view
$p = new Paginator($dispatcher);
$items = array('first', 'second');
$view = $p->paginate($items, 1, 10);
$this->assertEquals('val', $view->getCustomParameter('test'));
$this->assertNull($view->getCustomParameter('nonExisting'));
}
}

View File

@@ -0,0 +1,106 @@
<?php
use Test\Tool\BaseTestCase;
use Knp\Component\Pager\Paginator;
use Knp\Component\Pager\Pagination\SlidingPagination;
use Knp\Component\Pager\Pagination\PaginationInterface;
use Knp\Component\Pager\Event\Subscriber\Paginate\PaginationSubscriber;
class SlidingTest extends BaseTestCase
{
/**
* @test
*/
function shouldBeAbleToProducePagination()
{
$p = new Paginator;
$items = range(1, 23);
$view = $p->paginate($items, 1, 10);
$view->renderer = function($data) {
return 'custom';
};
$this->assertEquals('custom', (string)$view);
$pagination = $view->getPaginationData();
$this->assertEquals(3, $pagination['last']);
$this->assertEquals(1, $pagination['first']);
$this->assertEquals(1, $pagination['current']);
$this->assertEquals(10, $pagination['numItemsPerPage']);
$this->assertEquals(3, $pagination['pageCount']);
$this->assertEquals(23, $pagination['totalCount']);
$this->assertEquals(2, $pagination['next']);
$this->assertEquals(array(1, 2, 3), $pagination['pagesInRange']);
$this->assertEquals(1, $pagination['firstPageInRange']);
$this->assertEquals(3, $pagination['lastPageInRange']);
$this->assertEquals(10, $pagination['currentItemCount']);
$this->assertEquals(1, $pagination['firstItemNumber']);
$this->assertEquals(10, $pagination['lastItemNumber']);
}
/**
* @test
*/
function shouldBeAbleToProduceWiderPagination()
{
$p = new Paginator;
$items = range(1, 43);
$view = $p->paginate($items, 4, 5);
$pagination = $view->getPaginationData();
$this->assertEquals(9, $pagination['last']);
$this->assertEquals(1, $pagination['first']);
$this->assertEquals(4, $pagination['current']);
$this->assertEquals(5, $pagination['numItemsPerPage']);
$this->assertEquals(9, $pagination['pageCount']);
$this->assertEquals(43, $pagination['totalCount']);
$this->assertEquals(5, $pagination['next']);
$this->assertEquals(3, $pagination['previous']);
$this->assertEquals(array(2, 3, 4, 5, 6), $pagination['pagesInRange']);
$this->assertEquals(2, $pagination['firstPageInRange']);
$this->assertEquals(6, $pagination['lastPageInRange']);
$this->assertEquals(5, $pagination['currentItemCount']);
$this->assertEquals(16, $pagination['firstItemNumber']);
$this->assertEquals(20, $pagination['lastItemNumber']);
}
/**
* @test
*/
function shouldHandleOddAndEvenRange()
{
$p = new Paginator;
$items = range(1, 43);
$view = $p->paginate($items, 4, 5);
$view->setPageRange(4);
$pagination = $view->getPaginationData();
$this->assertEquals(3, $pagination['previous']);
$this->assertEquals(array(3, 4, 5, 6), $pagination['pagesInRange']);
$this->assertEquals(3, $pagination['firstPageInRange']);
$this->assertEquals(6, $pagination['lastPageInRange']);
$view->setPageRange(3);
$pagination = $view->getPaginationData();
$this->assertEquals(3, $pagination['previous']);
$this->assertEquals(array(3, 4, 5), $pagination['pagesInRange']);
$this->assertEquals(3, $pagination['firstPageInRange']);
$this->assertEquals(5, $pagination['lastPageInRange']);
}
/**
* @test
*/
function shouldNotFallbackToPageInCaseIfExceedsItemLimit()
{
$p = new Paginator;
$view = $p->paginate(range(1, 9), 2, 10);
$items = $view->getItems();
$this->assertTrue(empty($items));
}
}

View File

@@ -0,0 +1,33 @@
<?php
use Test\Tool\BaseTestCase;
use Knp\Component\Pager\Paginator;
use Knp\Component\Pager\Pagination\SlidingPagination;
use Knp\Component\Pager\Pagination\PaginationInterface;
use Knp\Component\Pager\Event\Subscriber\Paginate\PaginationSubscriber;
class TraversableItemsTest extends BaseTestCase
{
/**
* @test
*/
function shouldBeAbleToUseTraversableItems()
{
$p = new Paginator;
$items = new \ArrayObject(range(1, 23));
$view = $p->paginate($items, 3, 10);
$view->renderer = function($data) {
return 'custom';
};
$this->assertEquals('custom', (string)$view);
$items = $view->getItems();
$this->assertTrue($items instanceof \ArrayObject);
$i = 21;
foreach ($view as $item) {
$this->assertEquals($i++, $item);
}
}
}

View File

@@ -0,0 +1,33 @@
<?php
use Test\Tool\BaseTestCase;
use Knp\Component\Pager\Paginator;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Knp\Component\Pager\Pagination\PaginationInterface;
use Knp\Component\Pager\Event\Subscriber\Paginate\PaginationSubscriber;
class PaginatorTest extends BaseTestCase
{
/**
* @test
* @expectedException RuntimeException
*/
function shouldNotBeAbleToPaginateWithoutListeners()
{
$p = new Paginator(new EventDispatcher);
$p->paginate(array());
}
/**
* @test
* @expectedException RuntimeException
*/
function shouldFailToPaginateUnsupportedValue()
{
$dispatcher = new EventDispatcher;
$dispatcher->addSubscriber(new PaginationSubscriber);
$p = new Paginator($dispatcher);
$view = $p->paginate(null, 1, 10);
}
}

View File

@@ -0,0 +1,706 @@
<?php
namespace Test\Pager\Subscriber\Filtration\Doctrine\ORM;
use Test\Tool\BaseTestCaseORM;
use Knp\Component\Pager\Paginator;
use Knp\Component\Pager\Pagination\SlidingPagination;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Knp\Component\Pager\Event\Subscriber\Paginate\PaginationSubscriber;
use Knp\Component\Pager\Event\Subscriber\Filtration\FiltrationSubscriber as Filtration;
use Test\Fixture\Entity\Article;
use Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine\ORM\QuerySubscriber\UsesPaginator;
use Knp\Component\Pager\PaginatorInterface;
class QueryTest extends BaseTestCaseORM
{
/**
* @test
*/
public function shouldHandleApcQueryCache()
{
if (!extension_loaded('apc') || !ini_get('apc.enable_cli')) {
$this->markTestSkipped('APC extension is not loaded.');
}
$config = new \Doctrine\ORM\Configuration();
$config->setMetadataCacheImpl(new \Doctrine\Common\Cache\ApcCache());
$config->setQueryCacheImpl(new \Doctrine\Common\Cache\ApcCache());
$config->setProxyDir(__DIR__);
$config->setProxyNamespace('Gedmo\Mapping\Proxy');
$config->getAutoGenerateProxyClasses(false);
$config->setMetadataDriverImpl($this->getMetadataDriverImplementation());
$conn = array(
'driver' => 'pdo_sqlite',
'memory' => true,
);
$em = \Doctrine\ORM\EntityManager::create($conn, $config);
$schema = array_map(function ($class) use ($em) {
return $em->getClassMetadata($class);
}, (array) $this->getUsedEntityFixtures());
$schemaTool = new \Doctrine\ORM\Tools\SchemaTool($em);
$schemaTool->dropSchema(array());
$schemaTool->createSchema($schema);
$this->populate($em);
$_GET['filterField'] = 'a.title';
$_GET['filterValue'] = 'summer';
$query = $em->createQuery('SELECT a FROM Test\Fixture\Entity\Article a');
$p = new Paginator();
$view = $p->paginate($query, 1, 10);
$query = $em->createQuery('SELECT a FROM Test\Fixture\Entity\Article a');
$view = $p->paginate($query, 1, 10);
}
/**
* @test
*/
public function shouldFilterSimpleDoctrineQuery()
{
$em = $this->getMockSqliteEntityManager();
$this->populate($em);
$dispatcher = new EventDispatcher();
$dispatcher->addSubscriber(new PaginationSubscriber());
$dispatcher->addSubscriber(new Filtration());
$p = new Paginator($dispatcher);
$_GET['filterParam'] = 'a.title';
$_GET['filterValue'] = '*er';
$this->startQueryLog();
$query = $this->em->createQuery('SELECT a FROM Test\Fixture\Entity\Article a');
$query->setHint(UsesPaginator::HINT_FETCH_JOIN_COLLECTION, false);
$view = $p->paginate($query, 1, 10);
$items = $view->getItems();
$this->assertEquals(2, count($items));
$this->assertEquals('summer', $items[0]->getTitle());
$this->assertEquals('winter', $items[1]->getTitle());
$_GET['filterValue'] = 'summer';
$view = $p->paginate($query, 1, 10);
$items = $view->getItems();
$this->assertEquals(1, count($items));
$this->assertEquals('summer', $items[0]->getTitle());
$this->assertEquals(4, $this->queryAnalyzer->getNumExecutedQueries());
$executed = $this->queryAnalyzer->getExecutedQueries();
// Different aliases separators according to Doctrine version
if (version_compare(\Doctrine\ORM\Version::VERSION, '2.5', '<')) {
$this->assertEquals('SELECT a0_.id AS id0, a0_.title AS title1, a0_.enabled AS enabled2 FROM Article a0_ WHERE a0_.title LIKE \'%er\' LIMIT 10 OFFSET 0', $executed[1]);
$this->assertEquals('SELECT a0_.id AS id0, a0_.title AS title1, a0_.enabled AS enabled2 FROM Article a0_ WHERE a0_.title LIKE \'summer\' LIMIT 10 OFFSET 0', $executed[3]);
} else {
$this->assertEquals('SELECT a0_.id AS id_0, a0_.title AS title_1, a0_.enabled AS enabled_2 FROM Article a0_ WHERE a0_.title LIKE \'%er\' LIMIT 10 OFFSET 0', $executed[1]);
$this->assertEquals('SELECT a0_.id AS id_0, a0_.title AS title_1, a0_.enabled AS enabled_2 FROM Article a0_ WHERE a0_.title LIKE \'summer\' LIMIT 10 OFFSET 0', $executed[3]);
}
}
/**
* @test
*/
public function shouldFilterBooleanFilterValues()
{
$em = $this->getMockSqliteEntityManager();
$this->populate($em);
$dispatcher = new EventDispatcher();
$dispatcher->addSubscriber(new PaginationSubscriber());
$dispatcher->addSubscriber(new Filtration());
$p = new Paginator($dispatcher);
$this->startQueryLog();
$_GET['filterParam'] = 'a.enabled';
$_GET['filterValue'] = '1';
$query = $this->em->createQuery('SELECT a FROM Test\Fixture\Entity\Article a');
$query->setHint(UsesPaginator::HINT_FETCH_JOIN_COLLECTION, false);
$view = $p->paginate($query, 1, 10);
$items = $view->getItems();
$this->assertEquals(2, count($items));
$this->assertEquals('summer', $items[0]->getTitle());
$this->assertEquals('winter', $items[1]->getTitle());
$_GET['filterValue'] = 'true';
$query = $this->em->createQuery('SELECT a FROM Test\Fixture\Entity\Article a');
$query->setHint(UsesPaginator::HINT_FETCH_JOIN_COLLECTION, false);
$view = $p->paginate($query, 1, 10);
$items = $view->getItems();
$this->assertEquals(2, count($items));
$this->assertEquals('summer', $items[0]->getTitle());
$this->assertEquals('winter', $items[1]->getTitle());
$_GET['filterValue'] = '0';
$view = $p->paginate($query, 1, 10);
$items = $view->getItems();
$this->assertEquals(2, count($items));
$this->assertEquals('autumn', $items[0]->getTitle());
$this->assertEquals('spring', $items[1]->getTitle());
$_GET['filterValue'] = 'false';
$view = $p->paginate($query, 1, 10);
$items = $view->getItems();
$this->assertEquals(2, count($items));
$this->assertEquals('autumn', $items[0]->getTitle());
$this->assertEquals('spring', $items[1]->getTitle());
$this->assertEquals(8, $this->queryAnalyzer->getNumExecutedQueries());
$executed = $this->queryAnalyzer->getExecutedQueries();
// Different aliases separators according to Doctrine version
if (version_compare(\Doctrine\ORM\Version::VERSION, '2.5', '<')) {
$this->assertEquals('SELECT a0_.id AS id0, a0_.title AS title1, a0_.enabled AS enabled2 FROM Article a0_ WHERE a0_.enabled = 1 LIMIT 10 OFFSET 0', $executed[1]);
$this->assertEquals('SELECT a0_.id AS id0, a0_.title AS title1, a0_.enabled AS enabled2 FROM Article a0_ WHERE a0_.enabled = 1 LIMIT 10 OFFSET 0', $executed[3]);
$this->assertEquals('SELECT a0_.id AS id0, a0_.title AS title1, a0_.enabled AS enabled2 FROM Article a0_ WHERE a0_.enabled = 0 LIMIT 10 OFFSET 0', $executed[5]);
$this->assertEquals('SELECT a0_.id AS id0, a0_.title AS title1, a0_.enabled AS enabled2 FROM Article a0_ WHERE a0_.enabled = 0 LIMIT 10 OFFSET 0', $executed[7]);
} else {
$this->assertEquals('SELECT a0_.id AS id_0, a0_.title AS title_1, a0_.enabled AS enabled_2 FROM Article a0_ WHERE a0_.enabled = 1 LIMIT 10 OFFSET 0', $executed[1]);
$this->assertEquals('SELECT a0_.id AS id_0, a0_.title AS title_1, a0_.enabled AS enabled_2 FROM Article a0_ WHERE a0_.enabled = 1 LIMIT 10 OFFSET 0', $executed[3]);
$this->assertEquals('SELECT a0_.id AS id_0, a0_.title AS title_1, a0_.enabled AS enabled_2 FROM Article a0_ WHERE a0_.enabled = 0 LIMIT 10 OFFSET 0', $executed[5]);
$this->assertEquals('SELECT a0_.id AS id_0, a0_.title AS title_1, a0_.enabled AS enabled_2 FROM Article a0_ WHERE a0_.enabled = 0 LIMIT 10 OFFSET 0', $executed[7]);
}
}
/**
* @test
*/
public function shouldNotFilterInvalidBooleanFilterValues()
{
$em = $this->getMockSqliteEntityManager();
$this->populate($em);
$dispatcher = new EventDispatcher();
$dispatcher->addSubscriber(new PaginationSubscriber());
$dispatcher->addSubscriber(new Filtration());
$p = new Paginator($dispatcher);
$this->startQueryLog();
$_GET['filterParam'] = 'a.enabled';
$_GET['filterValue'] = 'invalid_boolean';
$query = $this->em->createQuery('SELECT a FROM Test\Fixture\Entity\Article a');
$query->setHint(UsesPaginator::HINT_FETCH_JOIN_COLLECTION, false);
$view = $p->paginate($query, 1, 10);
$items = $view->getItems();
$this->assertEquals(4, count($items));
$this->assertEquals(2, $this->queryAnalyzer->getNumExecutedQueries());
$executed = $this->queryAnalyzer->getExecutedQueries();
// Different aliases separators according to Doctrine version
if (version_compare(\Doctrine\ORM\Version::VERSION, '2.5', '<')) {
$this->assertEquals('SELECT a0_.id AS id0, a0_.title AS title1, a0_.enabled AS enabled2 FROM Article a0_ LIMIT 10 OFFSET 0', $executed[1]);
} else {
$this->assertEquals('SELECT a0_.id AS id_0, a0_.title AS title_1, a0_.enabled AS enabled_2 FROM Article a0_ LIMIT 10 OFFSET 0', $executed[1]);
}
}
/**
* @test
*/
public function shouldFilterNumericFilterValues()
{
$em = $this->getMockSqliteEntityManager();
$this->populateNumeric($em);
$dispatcher = new EventDispatcher();
$dispatcher->addSubscriber(new PaginationSubscriber());
$dispatcher->addSubscriber(new Filtration());
$p = new Paginator($dispatcher);
$this->startQueryLog();
$_GET['filterParam'] = 'a.title';
$_GET['filterValue'] = '0';
$query = $this->em->createQuery('SELECT a FROM Test\Fixture\Entity\Article a');
$query->setHint(UsesPaginator::HINT_FETCH_JOIN_COLLECTION, false);
$view = $p->paginate($query, 1, 10);
$items = $view->getItems();
$this->assertEquals(1, count($items));
$this->assertEquals('0', $items[0]->getTitle());
$_GET['filterValue'] = '1';
$query = $this->em->createQuery('SELECT a FROM Test\Fixture\Entity\Article a');
$query->setHint(UsesPaginator::HINT_FETCH_JOIN_COLLECTION, false);
$view = $p->paginate($query, 1, 10);
$items = $view->getItems();
$this->assertEquals(1, count($items));
$this->assertEquals('1', $items[0]->getTitle());
$this->assertEquals(4, $this->queryAnalyzer->getNumExecutedQueries());
$executed = $this->queryAnalyzer->getExecutedQueries();
// Different aliases separators according to Doctrine version
if (version_compare(\Doctrine\ORM\Version::VERSION, '2.5', '<')) {
$this->assertEquals('SELECT a0_.id AS id0, a0_.title AS title1, a0_.enabled AS enabled2 FROM Article a0_ WHERE a0_.title = 0 LIMIT 10 OFFSET 0', $executed[1]);
$this->assertEquals('SELECT a0_.id AS id0, a0_.title AS title1, a0_.enabled AS enabled2 FROM Article a0_ WHERE a0_.title = 1 LIMIT 10 OFFSET 0', $executed[3]);
} else {
$this->assertEquals('SELECT a0_.id AS id_0, a0_.title AS title_1, a0_.enabled AS enabled_2 FROM Article a0_ WHERE a0_.title = 0 LIMIT 10 OFFSET 0', $executed[1]);
$this->assertEquals('SELECT a0_.id AS id_0, a0_.title AS title_1, a0_.enabled AS enabled_2 FROM Article a0_ WHERE a0_.title = 1 LIMIT 10 OFFSET 0', $executed[3]);
}
}
/**
* @test
*/
public function shouldFilterComplexDoctrineQuery()
{
$em = $this->getMockSqliteEntityManager();
$this->populate($em);
$dispatcher = new EventDispatcher();
$dispatcher->addSubscriber(new PaginationSubscriber());
$dispatcher->addSubscriber(new Filtration());
$p = new Paginator($dispatcher);
$_GET['filterParam'] = 'a.title';
$_GET['filterValue'] = '*er';
$this->startQueryLog();
$query = $this->em->createQuery('SELECT a FROM Test\Fixture\Entity\Article a WHERE a.title <> \'\' AND (a.title LIKE \'summer\' OR a.title LIKE \'spring\')');
$query->setHint(UsesPaginator::HINT_FETCH_JOIN_COLLECTION, false);
$view = $p->paginate($query, 1, 10);
$items = $view->getItems();
$this->assertEquals(1, count($items));
$this->assertEquals('summer', $items[0]->getTitle());
$_GET['filterParam'] = 'a.id,a.title';
$view = $p->paginate($query, 1, 10);
$items = $view->getItems();
$this->assertEquals(1, count($items));
$this->assertEquals('summer', $items[0]->getTitle());
$executed = $this->queryAnalyzer->getExecutedQueries();
$query = $this->em->createQuery('SELECT a FROM Test\Fixture\Entity\Article a WHERE a.title <> \'\' OR (a.title LIKE \'summer\' OR a.title LIKE \'spring\')');
$query->setHint(UsesPaginator::HINT_FETCH_JOIN_COLLECTION, false);
$view = $p->paginate($query, 1, 10);
$items = $view->getItems();
$this->assertEquals(2, count($items));
$this->assertEquals('summer', $items[0]->getTitle());
$this->assertEquals('winter', $items[1]->getTitle());
$executed = $this->queryAnalyzer->getExecutedQueries();
$query = $this->em->createQuery('SELECT a FROM Test\Fixture\Entity\Article a WHERE a.title <> \'\'');
$query->setHint(UsesPaginator::HINT_FETCH_JOIN_COLLECTION, false);
$view = $p->paginate($query, 1, 10);
$items = $view->getItems();
$this->assertEquals(2, count($items));
$this->assertEquals('summer', $items[0]->getTitle());
$this->assertEquals('winter', $items[1]->getTitle());
$executed = $this->queryAnalyzer->getExecutedQueries();
$_GET['filterParam'] = 'a.title';
$view = $p->paginate($query, 1, 10);
$items = $view->getItems();
$this->assertEquals(2, count($items));
$this->assertEquals('summer', $items[0]->getTitle());
$this->assertEquals('winter', $items[1]->getTitle());
$executed = $this->queryAnalyzer->getExecutedQueries();
// Different aliases separators according to Doctrine version
if (version_compare(\Doctrine\ORM\Version::VERSION, '2.5', '<')) {
$this->assertEquals('SELECT a0_.id AS id0, a0_.title AS title1, a0_.enabled AS enabled2 FROM Article a0_ WHERE a0_.title LIKE \'%er\' AND a0_.title <> \'\' AND (a0_.title LIKE \'summer\' OR a0_.title LIKE \'spring\') LIMIT 10 OFFSET 0', $executed[1]);
$this->assertEquals('SELECT a0_.id AS id0, a0_.title AS title1, a0_.enabled AS enabled2 FROM Article a0_ WHERE (a0_.id LIKE \'%er\' OR a0_.title LIKE \'%er\') AND a0_.title <> \'\' AND (a0_.title LIKE \'summer\' OR a0_.title LIKE \'spring\') LIMIT 10 OFFSET 0', $executed[3]);
$this->assertEquals('SELECT a0_.id AS id0, a0_.title AS title1, a0_.enabled AS enabled2 FROM Article a0_ WHERE (a0_.id LIKE \'%er\' OR a0_.title LIKE \'%er\') AND (a0_.title <> \'\' OR (a0_.title LIKE \'summer\' OR a0_.title LIKE \'spring\')) LIMIT 10 OFFSET 0', $executed[5]);
$this->assertEquals('SELECT a0_.id AS id0, a0_.title AS title1, a0_.enabled AS enabled2 FROM Article a0_ WHERE (a0_.id LIKE \'%er\' OR a0_.title LIKE \'%er\') AND a0_.title <> \'\' LIMIT 10 OFFSET 0', $executed[7]);
$this->assertEquals('SELECT a0_.id AS id0, a0_.title AS title1, a0_.enabled AS enabled2 FROM Article a0_ WHERE a0_.title LIKE \'%er\' AND a0_.title <> \'\' LIMIT 10 OFFSET 0', $executed[9]);
} else {
$this->assertEquals('SELECT a0_.id AS id_0, a0_.title AS title_1, a0_.enabled AS enabled_2 FROM Article a0_ WHERE a0_.title LIKE \'%er\' AND a0_.title <> \'\' AND (a0_.title LIKE \'summer\' OR a0_.title LIKE \'spring\') LIMIT 10 OFFSET 0', $executed[1]);
$this->assertEquals('SELECT a0_.id AS id_0, a0_.title AS title_1, a0_.enabled AS enabled_2 FROM Article a0_ WHERE (a0_.id LIKE \'%er\' OR a0_.title LIKE \'%er\') AND a0_.title <> \'\' AND (a0_.title LIKE \'summer\' OR a0_.title LIKE \'spring\') LIMIT 10 OFFSET 0', $executed[3]);
$this->assertEquals('SELECT a0_.id AS id_0, a0_.title AS title_1, a0_.enabled AS enabled_2 FROM Article a0_ WHERE (a0_.id LIKE \'%er\' OR a0_.title LIKE \'%er\') AND (a0_.title <> \'\' OR (a0_.title LIKE \'summer\' OR a0_.title LIKE \'spring\')) LIMIT 10 OFFSET 0', $executed[5]);
$this->assertEquals('SELECT a0_.id AS id_0, a0_.title AS title_1, a0_.enabled AS enabled_2 FROM Article a0_ WHERE (a0_.id LIKE \'%er\' OR a0_.title LIKE \'%er\') AND a0_.title <> \'\' LIMIT 10 OFFSET 0', $executed[7]);
$this->assertEquals('SELECT a0_.id AS id_0, a0_.title AS title_1, a0_.enabled AS enabled_2 FROM Article a0_ WHERE a0_.title LIKE \'%er\' AND a0_.title <> \'\' LIMIT 10 OFFSET 0', $executed[9]);
}
}
/**
* @test
*/
public function shouldFilterSimpleDoctrineQueryWithMultipleProperties()
{
$em = $this->getMockSqliteEntityManager();
$this->populate($em);
$dispatcher = new EventDispatcher();
$dispatcher->addSubscriber(new PaginationSubscriber());
$dispatcher->addSubscriber(new Filtration());
$p = new Paginator($dispatcher);
$_GET['filterParam'] = 'a.id,a.title';
$_GET['filterValue'] = '*er';
$this->startQueryLog();
$query = $this->em->createQuery('SELECT a FROM Test\Fixture\Entity\Article a');
$query->setHint(UsesPaginator::HINT_FETCH_JOIN_COLLECTION, false);
$view = $p->paginate($query, 1, 10);
$items = $view->getItems();
$this->assertEquals(2, count($items));
$this->assertEquals('summer', $items[0]->getTitle());
$this->assertEquals('winter', $items[1]->getTitle());
$_GET['filterParam'] = array('a.id', 'a.title');
$view = $p->paginate($query, 1, 10);
$items = $view->getItems();
$this->assertEquals(2, count($items));
$this->assertEquals('summer', $items[0]->getTitle());
$this->assertEquals('winter', $items[1]->getTitle());
$executed = $this->queryAnalyzer->getExecutedQueries();
// Different aliases separators according to Doctrine version
if (version_compare(\Doctrine\ORM\Version::VERSION, '2.5', '<')) {
$this->assertEquals('SELECT a0_.id AS id0, a0_.title AS title1, a0_.enabled AS enabled2 FROM Article a0_ WHERE a0_.id LIKE \'%er\' OR a0_.title LIKE \'%er\' LIMIT 10 OFFSET 0', $executed[1]);
$this->assertEquals('SELECT a0_.id AS id0, a0_.title AS title1, a0_.enabled AS enabled2 FROM Article a0_ WHERE a0_.id LIKE \'%er\' OR a0_.title LIKE \'%er\' LIMIT 10 OFFSET 0', $executed[3]);
} else {
$this->assertEquals('SELECT a0_.id AS id_0, a0_.title AS title_1, a0_.enabled AS enabled_2 FROM Article a0_ WHERE a0_.id LIKE \'%er\' OR a0_.title LIKE \'%er\' LIMIT 10 OFFSET 0', $executed[1]);
$this->assertEquals('SELECT a0_.id AS id_0, a0_.title AS title_1, a0_.enabled AS enabled_2 FROM Article a0_ WHERE a0_.id LIKE \'%er\' OR a0_.title LIKE \'%er\' LIMIT 10 OFFSET 0', $executed[3]);
}
}
/**
* @test
*/
public function shouldFilterComplexDoctrineQueryWithMultipleProperties()
{
$em = $this->getMockSqliteEntityManager();
$this->populate($em);
$dispatcher = new EventDispatcher();
$dispatcher->addSubscriber(new PaginationSubscriber());
$dispatcher->addSubscriber(new Filtration());
$p = new Paginator($dispatcher);
$_GET['filterParam'] = 'a.id,a.title';
$_GET['filterValue'] = '*er';
$this->startQueryLog();
$query = $this->em->createQuery('SELECT a FROM Test\Fixture\Entity\Article a WHERE a.title <> \'\' AND (a.title LIKE \'summer\' OR a.title LIKE \'spring\')');
$query->setHint(UsesPaginator::HINT_FETCH_JOIN_COLLECTION, false);
$view = $p->paginate($query, 1, 10);
$items = $view->getItems();
$this->assertEquals(1, count($items));
$this->assertEquals('summer', $items[0]->getTitle());
$executed = $this->queryAnalyzer->getExecutedQueries();
// Different aliases separators according to Doctrine version
if (version_compare(\Doctrine\ORM\Version::VERSION, '2.5', '<')) {
$this->assertEquals('SELECT a0_.id AS id0, a0_.title AS title1, a0_.enabled AS enabled2 FROM Article a0_ WHERE (a0_.id LIKE \'%er\' OR a0_.title LIKE \'%er\') AND a0_.title <> \'\' AND (a0_.title LIKE \'summer\' OR a0_.title LIKE \'spring\') LIMIT 10 OFFSET 0', $executed[1]);
} else {
$this->assertEquals('SELECT a0_.id AS id_0, a0_.title AS title_1, a0_.enabled AS enabled_2 FROM Article a0_ WHERE (a0_.id LIKE \'%er\' OR a0_.title LIKE \'%er\') AND a0_.title <> \'\' AND (a0_.title LIKE \'summer\' OR a0_.title LIKE \'spring\') LIMIT 10 OFFSET 0', $executed[1]);
}
}
/**
* @test
* @expectedException UnexpectedValueException
*/
public function shouldValidateFiltrationParameter()
{
$_GET['filterParam'] = '"a.title\'';
$_GET['filterValue'] = 'summer';
$query = $this
->getMockSqliteEntityManager()
->createQuery('SELECT a FROM Test\Fixture\Entity\Article a')
;
$dispatcher = new EventDispatcher();
$dispatcher->addSubscriber(new PaginationSubscriber());
$dispatcher->addSubscriber(new Filtration());
$p = new Paginator($dispatcher);
$view = $p->paginate($query, 1, 10);
$items = $view->getItems();
}
/**
* @test
* @expectedException UnexpectedValueException
*/
public function shouldValidateFiltrationParameterWithoutAlias()
{
$_GET['filterParam'] = 'title';
$_GET['filterValue'] = 'summer';
$query = $this
->getMockSqliteEntityManager()
->createQuery('SELECT a FROM Test\Fixture\Entity\Article a')
;
$dispatcher = new EventDispatcher();
$dispatcher->addSubscriber(new PaginationSubscriber());
$dispatcher->addSubscriber(new Filtration());
$p = new Paginator($dispatcher);
$view = $p->paginate($query, 1, 10);
$items = $view->getItems();
}
/**
* @test
* @expectedException UnexpectedValueException
*/
public function shouldValidateFiltrationParameterExistance()
{
$_GET['filterParam'] = 'a.nonExistantField';
$_GET['filterValue'] = 'summer';
$query = $this
->getMockSqliteEntityManager()
->createQuery('SELECT a FROM Test\Fixture\Entity\Article a')
;
$dispatcher = new EventDispatcher();
$dispatcher->addSubscriber(new PaginationSubscriber());
$dispatcher->addSubscriber(new Filtration());
$p = new Paginator($dispatcher);
$view = $p->paginate($query, 1, 10);
$items = $view->getItems();
}
/**
* @test
*/
public function shouldFilterByAnyAvailableAlias()
{
$em = $this->getMockSqliteEntityManager();
$this->populate($em);
$_GET['filterParam'] = 'test_alias';
$_GET['filterValue'] = '*er';
$dql = <<<___SQL
SELECT a, a.title AS test_alias
FROM Test\Fixture\Entity\Article a
___SQL;
$query = $this->em->createQuery($dql);
$query->setHint(UsesPaginator::HINT_FETCH_JOIN_COLLECTION, false);
$dispatcher = new EventDispatcher();
$dispatcher->addSubscriber(new PaginationSubscriber());
$dispatcher->addSubscriber(new Filtration());
$p = new Paginator($dispatcher);
$this->startQueryLog();
$view = $p->paginate($query, 1, 10, array(PaginatorInterface::DISTINCT => false));
$items = $view->getItems();
$this->assertEquals(2, count($items));
$this->assertEquals('summer', $items[0][0]->getTitle());
$this->assertEquals('winter', $items[1][0]->getTitle());
$this->assertEquals(2, $this->queryAnalyzer->getNumExecutedQueries());
$executed = $this->queryAnalyzer->getExecutedQueries();
// Different aliases separators according to Doctrine version
if (version_compare(\Doctrine\ORM\Version::VERSION, '2.5', '<')) {
$this->assertEquals('SELECT a0_.id AS id0, a0_.title AS title1, a0_.enabled AS enabled2, a0_.title AS title3 FROM Article a0_ WHERE a0_.title LIKE \'%er\' LIMIT 10 OFFSET 0', $executed[1]);
} else {
$this->assertEquals('SELECT a0_.id AS id_0, a0_.title AS title_1, a0_.enabled AS enabled_2, a0_.title AS title_3 FROM Article a0_ WHERE a0_.title LIKE \'%er\' LIMIT 10 OFFSET 0', $executed[1]);
}
}
/**
* @test
*/
public function shouldNotWorkWithInitialPaginatorEventDispatcher()
{
$em = $this->getMockSqliteEntityManager();
$this->populate($em);
$_GET['filterParam'] = 'a.title';
$_GET['filterValue'] = 'summer';
$query = $this
->em
->createQuery('SELECT a FROM Test\Fixture\Entity\Article a')
;
$query->setHint(UsesPaginator::HINT_FETCH_JOIN_COLLECTION, false);
$p = new Paginator();
$this->startQueryLog();
$view = $p->paginate($query, 1, 10);
$this->assertTrue($view instanceof SlidingPagination);
$this->assertEquals(2, $this->queryAnalyzer->getNumExecutedQueries());
$executed = $this->queryAnalyzer->getExecutedQueries();
// Different aliases separators according to Doctrine version
if (version_compare(\Doctrine\ORM\Version::VERSION, '2.5', '<')) {
$this->assertEquals('SELECT a0_.id AS id0, a0_.title AS title1, a0_.enabled AS enabled2 FROM Article a0_ LIMIT 10 OFFSET 0', $executed[1]);
} else {
$this->assertEquals('SELECT a0_.id AS id_0, a0_.title AS title_1, a0_.enabled AS enabled_2 FROM Article a0_ LIMIT 10 OFFSET 0', $executed[1]);
}
}
/**
* @test
*/
public function shouldNotExecuteExtraQueriesWhenCountIsZero()
{
$_GET['filterParam'] = 'a.title';
$_GET['filterValue'] = 'asc';
$query = $this
->getMockSqliteEntityManager()
->createQuery('SELECT a FROM Test\Fixture\Entity\Article a')
;
$p = new Paginator();
$this->startQueryLog();
$view = $p->paginate($query, 1, 10);
$this->assertTrue($view instanceof SlidingPagination);
$this->assertEquals(2, $this->queryAnalyzer->getNumExecutedQueries());
}
/**
* @test
*/
public function shouldFilterWithEmptyParametersAndDefaults()
{
$em = $this->getMockSqliteEntityManager();
$this->populate($em);
$dispatcher = new EventDispatcher();
$dispatcher->addSubscriber(new PaginationSubscriber());
$dispatcher->addSubscriber(new Filtration());
$p = new Paginator($dispatcher);
$_GET['filterParam'] = '';
$_GET['filterValue'] = 'summer';
$this->startQueryLog();
$query = $this->em->createQuery('SELECT a FROM Test\Fixture\Entity\Article a');
$query->setHint(UsesPaginator::HINT_FETCH_JOIN_COLLECTION, false);
$defaultFilterFields = 'a.title';
$view = $p->paginate($query, 1, 10, compact(PaginatorInterface::DEFAULT_FILTER_FIELDS));
$items = $view->getItems();
$this->assertEquals(1, count($items));
$this->assertEquals('summer', $items[0]->getTitle());
$defaultFilterFields = 'a.id,a.title';
$view = $p->paginate($query, 1, 10, compact(PaginatorInterface::DEFAULT_FILTER_FIELDS));
$items = $view->getItems();
$this->assertEquals(1, count($items));
$this->assertEquals('summer', $items[0]->getTitle());
$defaultFilterFields = array('a.id', 'a.title');
$view = $p->paginate($query, 1, 10, compact(PaginatorInterface::DEFAULT_FILTER_FIELDS));
$items = $view->getItems();
$this->assertEquals(1, count($items));
$this->assertEquals('summer', $items[0]->getTitle());
$executed = $this->queryAnalyzer->getExecutedQueries();
// Different aliases separators according to Doctrine version
if (version_compare(\Doctrine\ORM\Version::VERSION, '2.5', '<')) {
$this->assertEquals('SELECT a0_.id AS id0, a0_.title AS title1, a0_.enabled AS enabled2 FROM Article a0_ WHERE a0_.title LIKE \'summer\' LIMIT 10 OFFSET 0', $executed[1]);
$this->assertEquals('SELECT a0_.id AS id0, a0_.title AS title1, a0_.enabled AS enabled2 FROM Article a0_ WHERE a0_.id LIKE \'summer\' OR a0_.title LIKE \'summer\' LIMIT 10 OFFSET 0', $executed[3]);
$this->assertEquals('SELECT a0_.id AS id0, a0_.title AS title1, a0_.enabled AS enabled2 FROM Article a0_ WHERE a0_.id LIKE \'summer\' OR a0_.title LIKE \'summer\' LIMIT 10 OFFSET 0', $executed[5]);
} else {
$this->assertEquals('SELECT a0_.id AS id_0, a0_.title AS title_1, a0_.enabled AS enabled_2 FROM Article a0_ WHERE a0_.title LIKE \'summer\' LIMIT 10 OFFSET 0', $executed[1]);
$this->assertEquals('SELECT a0_.id AS id_0, a0_.title AS title_1, a0_.enabled AS enabled_2 FROM Article a0_ WHERE a0_.id LIKE \'summer\' OR a0_.title LIKE \'summer\' LIMIT 10 OFFSET 0', $executed[3]);
$this->assertEquals('SELECT a0_.id AS id_0, a0_.title AS title_1, a0_.enabled AS enabled_2 FROM Article a0_ WHERE a0_.id LIKE \'summer\' OR a0_.title LIKE \'summer\' LIMIT 10 OFFSET 0', $executed[5]);
}
}
/**
* @test
*/
public function shouldNotFilterWithEmptyParametersAndDefaults()
{
$em = $this->getMockSqliteEntityManager();
$this->populate($em);
$dispatcher = new EventDispatcher();
$dispatcher->addSubscriber(new PaginationSubscriber());
$dispatcher->addSubscriber(new Filtration());
$p = new Paginator($dispatcher);
$_GET['filterParam'] = 'a.title';
$_GET['filterValue'] = '';
$this->startQueryLog();
$query = $this->em->createQuery('SELECT a FROM Test\Fixture\Entity\Article a');
$query->setHint(UsesPaginator::HINT_FETCH_JOIN_COLLECTION, false);
$view = $p->paginate($query, 1, 10);
$items = $view->getItems();
$this->assertEquals(4, count($items));
$_GET['filterParam'] = '';
$_GET['filterValue'] = 'summer';
$view = $p->paginate($query, 1, 10);
$items = $view->getItems();
$this->assertEquals(4, count($items));
$_GET['filterParam'] = '';
$_GET['filterValue'] = '';
$view = $p->paginate($query, 1, 10);
$items = $view->getItems();
$this->assertEquals(4, count($items));
$executed = $this->queryAnalyzer->getExecutedQueries();
// Different aliases separators according to Doctrine version
if (version_compare(\Doctrine\ORM\Version::VERSION, '2.5', '<')) {
$this->assertEquals('SELECT a0_.id AS id0, a0_.title AS title1, a0_.enabled AS enabled2 FROM Article a0_ LIMIT 10 OFFSET 0', $executed[1]);
$this->assertEquals('SELECT a0_.id AS id0, a0_.title AS title1, a0_.enabled AS enabled2 FROM Article a0_ LIMIT 10 OFFSET 0', $executed[3]);
$this->assertEquals('SELECT a0_.id AS id0, a0_.title AS title1, a0_.enabled AS enabled2 FROM Article a0_ LIMIT 10 OFFSET 0', $executed[5]);
} else {
$this->assertEquals('SELECT a0_.id AS id_0, a0_.title AS title_1, a0_.enabled AS enabled_2 FROM Article a0_ LIMIT 10 OFFSET 0', $executed[1]);
$this->assertEquals('SELECT a0_.id AS id_0, a0_.title AS title_1, a0_.enabled AS enabled_2 FROM Article a0_ LIMIT 10 OFFSET 0', $executed[3]);
$this->assertEquals('SELECT a0_.id AS id_0, a0_.title AS title_1, a0_.enabled AS enabled_2 FROM Article a0_ LIMIT 10 OFFSET 0', $executed[5]);
}
}
protected function getUsedEntityFixtures()
{
return array('Test\Fixture\Entity\Article');
}
private function populate($em)
{
$summer = new Article();
$summer->setTitle('summer');
$summer->setEnabled(true);
$winter = new Article();
$winter->setTitle('winter');
$winter->setEnabled(true);
$autumn = new Article();
$autumn->setTitle('autumn');
$autumn->setEnabled(false);
$spring = new Article();
$spring->setTitle('spring');
$spring->setEnabled(false);
$em->persist($summer);
$em->persist($winter);
$em->persist($autumn);
$em->persist($spring);
$em->flush();
}
private function populateNumeric($em)
{
$zero = new Article();
$zero->setTitle('0');
$zero->setEnabled(true);
$one = new Article();
$one->setTitle('1');
$one->setEnabled(true);
$lower = new Article();
$lower->setTitle('123');
$lower->setEnabled(false);
$upper = new Article();
$upper->setTitle('234');
$upper->setEnabled(false);
$em->persist($zero);
$em->persist($one);
$em->persist($lower);
$em->persist($upper);
$em->flush();
}
private function getApcEntityManager()
{
$config = new \Doctrine\ORM\Configuration();
$config->setMetadataCacheImpl(new \Doctrine\Common\Cache\ApcCache());
$config->setQueryCacheImpl(new \Doctrine\Common\Cache\ApcCache());
$config->setProxyDir(__DIR__);
$config->setProxyNamespace('Gedmo\Mapping\Proxy');
$config->setAutoGenerateProxyClasses(false);
$config->setMetadataDriverImpl($this->getMetadataDriverImplementation());
$conn = array(
'driver' => 'pdo_sqlite',
'memory' => true,
);
$em = \Doctrine\ORM\EntityManager::create($conn, $config);
return $em;
}
}

View File

@@ -0,0 +1,93 @@
<?php
namespace Test\Pager\Subscriber\Filtration\Doctrine\ORM;
use Knp\Component\Pager\PaginatorInterface;
use Test\Tool\BaseTestCaseORM;
use Knp\Component\Pager\Paginator;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Knp\Component\Pager\Event\Subscriber\Paginate\PaginationSubscriber;
use Knp\Component\Pager\Event\Subscriber\Filtration\FiltrationSubscriber as Filtration;
use Test\Fixture\Entity\Article;
class WhitelistTest extends BaseTestCaseORM
{
/**
* @test
* @expectedException \UnexpectedValueException
*/
public function shouldWhitelistFiltrationFields()
{
$this->populate();
$_GET['filterParam'] = 'a.title';
$_GET['filterValue'] = 'summer';
$query = $this->em->createQuery('SELECT a FROM Test\Fixture\Entity\Article a');
$dispatcher = new EventDispatcher();
$dispatcher->addSubscriber(new PaginationSubscriber());
$dispatcher->addSubscriber(new Filtration());
$p = new Paginator($dispatcher);
$filterFieldWhitelist = array('a.title');
$view = $p->paginate($query, 1, 10, compact(PaginatorInterface::FILTER_FIELD_WHITELIST));
$items = $view->getItems();
$this->assertEquals(1, count($items));
$this->assertEquals('summer', $items[0]->getTitle());
$_GET['filterParam'] = 'a.id';
$view = $p->paginate($query, 1, 10, compact(PaginatorInterface::FILTER_FIELD_WHITELIST));
}
/**
* @test
*/
public function shouldFilterWithoutSpecificWhitelist()
{
$this->populate();
$_GET['filterParam'] = 'a.title';
$_GET['filterValue'] = 'autumn';
$query = $this->em->createQuery('SELECT a FROM Test\Fixture\Entity\Article a');
$dispatcher = new EventDispatcher();
$dispatcher->addSubscriber(new PaginationSubscriber());
$dispatcher->addSubscriber(new Filtration());
$p = new Paginator($dispatcher);
$view = $p->paginate($query, 1, 10);
$items = $view->getItems();
$this->assertEquals('autumn', $items[0]->getTitle());
$_GET['filterParam'] = 'a.id';
$view = $p->paginate($query, 1, 10);
$items = $view->getItems();
$this->assertEquals(0, count($items));
}
protected function getUsedEntityFixtures()
{
return array('Test\Fixture\Entity\Article');
}
private function populate()
{
$em = $this->getMockSqliteEntityManager();
$summer = new Article();
$summer->setTitle('summer');
$winter = new Article();
$winter->setTitle('winter');
$autumn = new Article();
$autumn->setTitle('autumn');
$spring = new Article();
$spring->setTitle('spring');
$em->persist($summer);
$em->persist($winter);
$em->persist($autumn);
$em->persist($spring);
$em->flush();
}
}

View File

@@ -0,0 +1,25 @@
<?php
use Test\Tool\BaseTestCase;
use Knp\Component\Pager\Event\Subscriber\Filtration\FiltrationSubscriber;
use Knp\Component\Pager\Event\BeforeEvent;
class FiltrationSubscriberTest extends BaseTestCase
{
/**
* @test
*/
function shouldRegisterExpectedSubscribersOnlyOnce()
{
$dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')->getMock();
$dispatcher->expects($this->exactly(2))->method('addSubscriber');
$subscriber = new FiltrationSubscriber;
$beforeEvent = new BeforeEvent($dispatcher);
$subscriber->before($beforeEvent);
// Subsequent calls do not add more subscribers
$subscriber->before($beforeEvent);
}
}

View File

@@ -0,0 +1,75 @@
<?php
use Test\Tool\BaseTestCase;
use Knp\Component\Pager\Paginator;
use Knp\Component\Pager\Pagination\SlidingPagination;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Test\Mock\PaginationSubscriber as MockPaginationSubscriber;
use Knp\Component\Pager\Pagination\PaginationInterface;
use Knp\Component\Pager\Event\Subscriber\Paginate\ArraySubscriber;
use Knp\Component\Pager\Event\Subscriber\Paginate\PaginationSubscriber;
class ArrayTest extends BaseTestCase
{
/**
* @test
*/
function shouldPaginateAnArray()
{
$dispatcher = new EventDispatcher;
$dispatcher->addSubscriber(new ArraySubscriber);
$dispatcher->addSubscriber(new MockPaginationSubscriber); // pagination view
$p = new Paginator($dispatcher);
$items = array('first', 'second');
$view = $p->paginate($items, 1, 10);
$this->assertTrue($view instanceof PaginationInterface);
$this->assertEquals(1, $view->getCurrentPageNumber());
$this->assertEquals(10, $view->getItemNumberPerPage());
$this->assertEquals(2, count($view->getItems()));
$this->assertEquals(2, $view->getTotalItemCount());
}
/**
* @test
*/
function shouldSlicePaginateAnArray()
{
$dispatcher = new EventDispatcher;
$dispatcher->addSubscriber(new ArraySubscriber);
$dispatcher->addSubscriber(new MockPaginationSubscriber); // pagination view
$p = new Paginator($dispatcher);
$items = range('a', 'u');
$view = $p->paginate($items, 2, 10);
$this->assertEquals(2, $view->getCurrentPageNumber());
$this->assertEquals(10, $view->getItemNumberPerPage());
$this->assertEquals(10, count($view->getItems()));
$this->assertEquals(21, $view->getTotalItemCount());
}
/**
* @test
*/
function shouldSupportPaginateStrategySubscriber()
{
$items = array('first', 'second');
$p = new Paginator;
$view = $p->paginate($items, 1, 10);
$this->assertTrue($view instanceof PaginationInterface);
}
/**
* @test
*/
function shouldPaginateArrayObject()
{
$items = array('first', 'second');
$array = new \ArrayObject($items);
$p = new Paginator;
$view = $p->paginate($array, 1, 10);
$this->assertTrue($view instanceof PaginationInterface);
}
}

View File

@@ -0,0 +1,63 @@
<?php
use Test\Tool\BaseTestCase;
use Knp\Component\Pager\Paginator;
use Knp\Component\Pager\Pagination\SlidingPagination;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Test\Mock\PaginationSubscriber as MockPaginationSubscriber;
use Knp\Component\Pager\Pagination\PaginationInterface;
use Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine\CollectionSubscriber;
use Knp\Component\Pager\Event\Subscriber\Paginate\PaginationSubscriber;
use Doctrine\Common\Collections\ArrayCollection;
class CollectionTest extends BaseTestCase
{
/**
* @test
*/
function shouldPaginateCollection()
{
$dispatcher = new EventDispatcher;
$dispatcher->addSubscriber(new CollectionSubscriber);
$dispatcher->addSubscriber(new MockPaginationSubscriber); // pagination view
$p = new Paginator($dispatcher);
$items = new ArrayCollection(array('first', 'second'));
$view = $p->paginate($items, 1, 10);
$this->assertEquals(1, $view->getCurrentPageNumber());
$this->assertEquals(10, $view->getItemNumberPerPage());
$this->assertEquals(2, count($view->getItems()));
$this->assertEquals(2, $view->getTotalItemCount());
}
/**
* @test
*/
function shouldSlicePaginateAnArray()
{
$dispatcher = new EventDispatcher;
$dispatcher->addSubscriber(new CollectionSubscriber);
$dispatcher->addSubscriber(new MockPaginationSubscriber); // pagination view
$p = new Paginator($dispatcher);
$items = new ArrayCollection(range('a', 'u'));
$view = $p->paginate($items, 2, 10);
$this->assertEquals(2, $view->getCurrentPageNumber());
$this->assertEquals(10, $view->getItemNumberPerPage());
$this->assertEquals(10, count($view->getItems()));
$this->assertEquals(21, $view->getTotalItemCount());
}
/**
* @test
*/
function shouldSupportPaginateStrategySubscriber()
{
$items = new ArrayCollection(array('first', 'second'));
$p = new Paginator;
$view = $p->paginate($items, 1, 10);
$this->assertTrue($view instanceof PaginationInterface);
}
}

View File

@@ -0,0 +1,62 @@
<?php
namespace Test\Pager\Subscriber\Paginate\Doctrine;
use Test\Tool\BaseTestCaseORM;
use Doctrine\DBAL\Query\QueryBuilder;
use Knp\Component\Pager\Paginator;
use Test\Fixture\Entity\Article;
class DBALQueryBuilderTest extends BaseTestCaseORM
{
/**
* @test
*/
function shouldPaginateSimpleDoctrineQuery()
{
$this->populate();
$p = new Paginator;
$qb = new QueryBuilder($this->em->getConnection());
$qb->select('*')
->from('Article', 'a')
;
$view = $p->paginate($qb, 1, 2);
$this->assertEquals(1, $view->getCurrentPageNumber());
$this->assertEquals(2, $view->getItemNumberPerPage());
$this->assertEquals(4, $view->getTotalItemCount());
$items = $view->getItems();
$this->assertEquals(2, count($items));
$this->assertEquals('summer', $items[0]['title']);
$this->assertEquals('winter', $items[1]['title']);
}
protected function getUsedEntityFixtures()
{
return array('Test\Fixture\Entity\Article');
}
private function populate()
{
$em = $this->getMockSqliteEntityManager();
$summer = new Article;
$summer->setTitle('summer');
$winter = new Article;
$winter->setTitle('winter');
$autumn = new Article;
$autumn->setTitle('autumn');
$spring = new Article;
$spring->setTitle('spring');
$em->persist($summer);
$em->persist($winter);
$em->persist($autumn);
$em->persist($spring);
$em->flush();
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace Test\Pager\Subscriber\Sortable\Doctrine\ODM\MongoDB;
use Test\Tool\BaseTestCaseMongoODM;
use Knp\Component\Pager\Paginator;
use Test\Fixture\Document\Image;
class GridFsTest extends BaseTestCaseMongoODM
{
/**
* @test
*/
function shouldPaginate()
{
$this->populate();
$query = $this->dm
->createQueryBuilder('Test\Fixture\Document\Image')
->getQuery()
;
$p = new Paginator;
$view = $p->paginate($query, 1, 10);
$cursor = $query->execute();
$this->assertEquals(4, count($view->getItems()));
}
private function populate()
{
$mockFile = __DIR__.'/summer.gif';
$dm = $this->getMockDocumentManager();
$summer = new Image;
$summer->setTitle('summer');
$summer->setFile($mockFile);
$winter = new Image;
$winter->setTitle('winter');
$winter->setFile($mockFile);
$autumn = new Image;
$autumn->setTitle('autumn');
$autumn->setFile($mockFile);
$spring = new Image;
$spring->setTitle('spring');
$spring->setFile($mockFile);
$dm->persist($summer);
$dm->persist($winter);
$dm->persist($autumn);
$dm->persist($spring);
$dm->flush();
}
}

View File

@@ -0,0 +1,54 @@
<?php
namespace Test\Pager\Subscriber\Paginate\Doctrine\ODM\MongoDB;
use Test\Tool\BaseTestCaseMongoODM;
use Knp\Component\Pager\Paginator;
use Test\Fixture\Document\Article;
class QueryBuilderTest extends BaseTestCaseMongoODM
{
/**
* @test
*/
function shouldSupportPaginateStrategySubscriber()
{
$this->populate();
$qb = $this
->getMockDocumentManager()
->createQueryBuilder('Test\Fixture\Document\Article')
;
$p = new Paginator;
$pagination = $p->paginate($qb, 1, 2);
$this->assertEquals(1, $pagination->getCurrentPageNumber());
$this->assertEquals(2, $pagination->getItemNumberPerPage());
$this->assertEquals(4, $pagination->getTotalItemCount());
$items = array_values($pagination->getItems());
$this->assertEquals(2, count($items));
$this->assertEquals('summer', $items[0]->getTitle());
$this->assertEquals('winter', $items[1]->getTitle());
}
private function populate()
{
$em = $this->getMockDocumentManager();
$summer = new Article;
$summer->setTitle('summer');
$winter = new Article;
$winter->setTitle('winter');
$autumn = new Article;
$autumn->setTitle('autumn');
$spring = new Article;
$spring->setTitle('spring');
$em->persist($summer);
$em->persist($winter);
$em->persist($autumn);
$em->persist($spring);
$em->flush();
}
}

View File

@@ -0,0 +1,78 @@
<?php
namespace Test\Pager\Subscriber\Paginate\Doctrine\ODM\MongoDB;
use Test\Tool\BaseTestCaseMongoODM;
use Knp\Component\Pager\Paginator;
use Knp\Component\Pager\Pagination\SlidingPagination;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine\ODM\MongoDB\QuerySubscriber;
use Knp\Component\Pager\Event\Subscriber\Paginate\PaginationSubscriber;
use Test\Fixture\Document\Article;
class QueryTest extends BaseTestCaseMongoODM
{
/**
* @test
*/
function shouldPaginateSimpleDoctrineMongoDBQuery()
{
$this->populate();
$dispatcher = new EventDispatcher;
$dispatcher->addSubscriber(new QuerySubscriber);
$dispatcher->addSubscriber(new PaginationSubscriber); // pagination view
$p = new Paginator($dispatcher);
$qb = $this->dm->createQueryBuilder('Test\Fixture\Document\Article');
$query = $qb->getQuery();
$pagination = $p->paginate($query, 1, 2);
$this->assertTrue($pagination instanceof SlidingPagination);
$this->assertEquals(1, $pagination->getCurrentPageNumber());
$this->assertEquals(2, $pagination->getItemNumberPerPage());
$this->assertEquals(4, $pagination->getTotalItemCount());
$items = array_values($pagination->getItems());
$this->assertEquals(2, count($items));
$this->assertEquals('summer', $items[0]->getTitle());
$this->assertEquals('winter', $items[1]->getTitle());
}
/**
* @test
*/
function shouldSupportPaginateStrategySubscriber()
{
$query = $this
->getMockDocumentManager()
->createQueryBuilder('Test\Fixture\Document\Article')
->getQuery()
;
$p = new Paginator;
$pagination = $p->paginate($query, 1, 10);
$this->assertTrue($pagination instanceof SlidingPagination);
}
private function populate()
{
$em = $this->getMockDocumentManager();
$summer = new Article;
$summer->setTitle('summer');
$winter = new Article;
$winter->setTitle('winter');
$autumn = new Article;
$autumn->setTitle('autumn');
$spring = new Article;
$spring->setTitle('spring');
$em->persist($summer);
$em->persist($winter);
$em->persist($autumn);
$em->persist($spring);
$em->flush();
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@@ -0,0 +1,62 @@
<?php
namespace Test\Pager\Subscriber\Paginate\Doctrine\ODM\PHPCR;
use Knp\Component\Pager\Paginator;
use Test\Fixture\Document\PHPCR\Article;
use Test\Tool\BaseTestCasePHPCRODM;
class QueryBuilderSubscriberTest extends BaseTestCasePHPCRODM
{
/**
* @test
*/
public function shouldSupportPaginateStrategySubscriber()
{
$this->populate();
$qb = $this->dm->createQueryBuilder();
$qb->fromDocument('Test\Fixture\Document\PHPCR\Article', 'a');
$p = new Paginator();
$pagination = $p->paginate($qb, 1, 2);
$this->assertEquals(1, $pagination->getCurrentPageNumber());
$this->assertEquals(2, $pagination->getItemNumberPerPage());
$this->assertEquals(4, $pagination->getTotalItemCount());
$items = $pagination->getItems();
$this->assertEquals(2, count($items));
$this->assertEquals('summer', $items->first()->getTitle());
$this->assertEquals('winter', $items->last()->getTitle());
}
private function populate()
{
$dm = $this->getMockDocumentManager();
$root = $dm->find(null, '/');
$summer = new Article();
$summer->setTitle('summer');
$summer->setParent($root);
$winter = new Article();
$winter->setTitle('winter');
$winter->setParent($root);
$autumn = new Article();
$autumn->setTitle('autumn');
$autumn->setParent($root);
$spring = new Article();
$spring->setTitle('spring');
$spring->setParent($root);
$dm->persist($summer);
$dm->persist($winter);
$dm->persist($autumn);
$dm->persist($spring);
$dm->flush();
}
}

View File

@@ -0,0 +1,84 @@
<?php
namespace Test\Pager\Subscriber\Paginate\Doctrine\ODM\PHPCR;
use Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine\ODM\PHPCR\QuerySubscriber;
use Knp\Component\Pager\Pagination\SlidingPagination;
use Knp\Component\Pager\Paginator;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Test\Fixture\Document\PHPCR\Article;
use Test\Mock\PaginationSubscriber;
use Test\Tool\BaseTestCasePHPCRODM;
class QuerySubscriberTest extends BaseTestCasePHPCRODM
{
/**
* @test
*/
function shouldPaginateSimpleDoctrinePHPCRQuery()
{
$this->populate();
$dispatcher = new EventDispatcher();
$dispatcher->addSubscriber(new QuerySubscriber());
$dispatcher->addSubscriber(new PaginationSubscriber()); // pagination view
$p = new Paginator($dispatcher);
$query = $this->dm->createQueryBuilder()->fromDocument('Test\Fixture\Document\PHPCR\Article', 'a')->getQuery();
$pagination = $p->paginate($query, 1, 2);
$this->assertTrue($pagination instanceof SlidingPagination);
$this->assertEquals(1, $pagination->getCurrentPageNumber());
$this->assertEquals(2, $pagination->getItemNumberPerPage());
$this->assertEquals(4, $pagination->getTotalItemCount());
$items = $pagination->getItems();
$this->assertEquals(2, count($items));
$this->assertEquals('summer', $items->first()->getTitle());
$this->assertEquals('winter', $items->last()->getTitle());
}
/**
* @test
*/
function shouldSupportPaginateStrategySubscriber()
{
$this->getMockDocumentManager();
$query = $this->dm->createQueryBuilder()->fromDocument('Test\Fixture\Document\PHPCR\Article', 'a')->getQuery();
$p = new Paginator();
$pagination = $p->paginate($query, 1, 10);
$this->assertTrue($pagination instanceof SlidingPagination);
}
private function populate()
{
$dm = $this->getMockDocumentManager();
$root = $dm->find(null, '/');
$summer = new Article();
$summer->setTitle('summer');
$summer->setParent($root);
$winter = new Article();
$winter->setTitle('winter');
$winter->setParent($root);
$autumn = new Article();
$autumn->setTitle('autumn');
$autumn->setParent($root);
$spring = new Article();
$spring->setTitle('spring');
$spring->setParent($root);
$dm->persist($summer);
$dm->persist($winter);
$dm->persist($autumn);
$dm->persist($spring);
$dm->flush();
}
}

View File

@@ -0,0 +1,194 @@
<?php
namespace Test\Pager\Subscriber\Paginate\Doctrine\ORM;
use Test\Tool\BaseTestCaseORM;
use Knp\Component\Pager\Paginator;
use Knp\Component\Pager\Pagination\SlidingPagination;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Knp\Component\Pager\Pagination\PaginationInterface;
use Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine\ORM\QuerySubscriber;
use Knp\Component\Pager\Event\Subscriber\Paginate\PaginationSubscriber;
use Test\Fixture\Entity\Shop\Product;
use Test\Fixture\Entity\Shop\Tag;
use Doctrine\ORM\Query;
class AdvancedQueryTest extends BaseTestCaseORM
{
/**
* Its not possible to make distinction and predict
* count of such query
*
* @test
* @expectedException RuntimeException
*/
function shouldFailToPaginateMultiRootQuery()
{
$this->populate();
$dql = <<<___SQL
SELECT p FROM
Test\Fixture\Entity\Shop\Product p,
Test\Fixture\Entity\Shop\Tag t
___SQL;
$q = $this->em->createQuery($dql);
$p = new Paginator;
$this->startQueryLog();
$view = $p->paginate($q, 1, 2);
}
/**
* @test
*/
function shouldBeAbleToPaginateWithHavingClause()
{
$this->populate();
$dql = <<<___SQL
SELECT p, t
FROM Test\Fixture\Entity\Shop\Product p
INNER JOIN p.tags t
GROUP BY p.id
HAVING p.numTags = COUNT(t)
___SQL;
$q = $this->em->createQuery($dql);
$q->setHydrationMode(Query::HYDRATE_ARRAY);
$p = new Paginator;
$view = $p->paginate($q, 1, 10, array('wrap-queries' => true));
$this->assertEquals(3, count($view));
}
/**
* @test
*/
function shouldBeAbleToPaginateMixedKeyArray()
{
$this->populate();
$dql = <<<___SQL
SELECT p, t, p.title FROM
Test\Fixture\Entity\Shop\Product p
LEFT JOIN
p.tags t
___SQL;
$q = $this->em->createQuery($dql);
$p = new Paginator;
$view = $p->paginate($q, 1, 10);
$this->assertEquals(3, count($view));
$items = $view->getItems();
// and should be hydrated as array
$this->assertTrue(isset($items[0]['title']));
}
/**
* @test
*/
function shouldBeAbleToPaginateCaseBasedQuery()
{
if (version_compare(\Doctrine\ORM\Version::VERSION, '2.2.0-DEV', '<')) {
$this->markTestSkipped('Only recent orm version can test against this query.');
}
$this->populate();
$dql = <<<___SQL
SELECT p,
CASE
WHEN p.title LIKE :keyword
AND p.description LIKE :keyword
THEN 0
WHEN p.title LIKE :keyword
THEN 1
WHEN p.description LIKE :keyword
THEN 2
ELSE 3
END AS relevance
FROM Test\Fixture\Entity\Shop\Product p
WHERE (
p.title LIKE :keyword
OR p.description LIKE :keyword
)
GROUP BY p.id
ORDER BY relevance ASC, p.id DESC
___SQL;
$q = $this->em->createQuery($dql);
$q->setParameter('keyword', '%Star%');
$q->setHydrationMode(\Doctrine\ORM\Query::HYDRATE_ARRAY);
$p = new Paginator;
$view = $p->paginate($q, 1, 10);
$this->assertEquals(1, count($view));
$items = $view->getItems();
// and should be hydrated as array
$this->assertEquals('Starship', $items[0][0]['title']);
$this->assertEquals(1, $items[0]['relevance']);
}
/**
* @test
*/
function shouldUseOutputWalkersIfHinted()
{
$this->populate();
$dql = <<<___SQL
SELECT p, t
FROM Test\Fixture\Entity\Shop\Product p
INNER JOIN p.tags t
GROUP BY p.id
HAVING p.numTags = COUNT(t)
___SQL;
$q = $this->em->createQuery($dql);
$q->setHydrationMode(Query::HYDRATE_ARRAY);
$p = new Paginator;
$view = $p->paginate($q, 1, 10, array('wrap-queries' => true));
$this->assertEquals(3, count($view));
}
protected function getUsedEntityFixtures()
{
return array(
'Test\Fixture\Entity\Shop\Product',
'Test\Fixture\Entity\Shop\Tag'
);
}
private function populate()
{
$em = $this->getMockSqliteEntityManager();
$cheep = new Tag;
$cheep->setName('Cheep');
$new = new Tag;
$new->setName('New');
$special = new Tag;
$special->setName('Special');
$starship = new Product;
$starship->setTitle('Starship');
$starship->setPrice(277.66);
$starship->addTag($new);
$starship->addTag($special);
$cheese = new Product;
$cheese->setTitle('Cheese');
$cheese->setPrice(7.66);
$cheese->addTag($cheep);
$shoe = new Product;
$shoe->setTitle('Shoe');
$shoe->setPrice(2.66);
$shoe->addTag($special);
$em->persist($special);
$em->persist($cheep);
$em->persist($new);
$em->persist($starship);
$em->persist($cheese);
$em->persist($shoe);
$em->flush();
}
}

View File

@@ -0,0 +1,72 @@
<?php
namespace Test\Pager\Subscriber\Paginate\Doctrine\ORM;
use Test\Tool\BaseTestCaseORM;
use Knp\Component\Pager\Paginator;
use Test\Fixture\Entity\Composite;
use Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine\ORM\QuerySubscriber\UsesPaginator;
use Doctrine\ORM\Query;
class CompositeKeyTest extends BaseTestCaseORM
{
/**
* @test
*/
function shouldBeHandledByQueryHintByPassingCount()
{
$p = new Paginator;
$em = $this->getMockSqliteEntityManager();
$this->populate($em);
$count = $em
->createQuery('SELECT COUNT(c) FROM Test\Fixture\Entity\Composite c')
->getSingleScalarResult()
;
$query = $em
->createQuery('SELECT c FROM Test\Fixture\Entity\Composite c')
->setHint('knp_paginator.count', $count)
;
$query->setHint(UsesPaginator::HINT_FETCH_JOIN_COLLECTION, false);
$view = $p->paginate($query, 1, 10, array('wrap-queries' => true));
$items = $view->getItems();
$this->assertEquals(4, count($items));
}
protected function getUsedEntityFixtures()
{
return array('Test\Fixture\Entity\Composite');
}
private function populate($em)
{
$summer = new Composite;
$summer->setId(1);
$summer->setTitle('summer');
$summer->setUid(100);
$winter = new Composite;
$winter->setId(2);
$winter->setTitle('winter');
$winter->setUid(200);
$autumn = new Composite;
$autumn->setId(3);
$autumn->setTitle('autumn');
$autumn->setUid(300);
$spring = new Composite;
$spring->setId(4);
$spring->setTitle('spring');
$spring->setUid(400);
$em->persist($summer);
$em->persist($winter);
$em->persist($autumn);
$em->persist($spring);
$em->flush();
}
}

View File

@@ -0,0 +1,62 @@
<?php
namespace Test\Pager\Subscriber\Paginate\Doctrine\ORM;
use Test\Tool\BaseTestCaseORM;
use Knp\Component\Pager\Paginator;
use Test\Fixture\Entity\Article;
class QueryBuilderTest extends BaseTestCaseORM
{
/**
* @test
*/
function shouldPaginateSimpleDoctrineQuery()
{
$this->populate();
$p = new Paginator;
$qb = $this->em->createQueryBuilder();
$qb
->select('a')
->from('Test\Fixture\Entity\Article', 'a')
;
$view = $p->paginate($qb, 1, 2);
$this->assertEquals(1, $view->getCurrentPageNumber());
$this->assertEquals(2, $view->getItemNumberPerPage());
$this->assertEquals(4, $view->getTotalItemCount());
$items = $view->getItems();
$this->assertEquals(2, count($items));
$this->assertEquals('summer', $items[0]->getTitle());
$this->assertEquals('winter', $items[1]->getTitle());
}
protected function getUsedEntityFixtures()
{
return array('Test\Fixture\Entity\Article');
}
private function populate()
{
$em = $this->getMockSqliteEntityManager();
$summer = new Article;
$summer->setTitle('summer');
$winter = new Article;
$winter->setTitle('winter');
$autumn = new Article;
$autumn->setTitle('autumn');
$spring = new Article;
$spring->setTitle('spring');
$em->persist($summer);
$em->persist($winter);
$em->persist($autumn);
$em->persist($spring);
$em->flush();
}
}

View File

@@ -0,0 +1,82 @@
<?php
namespace Test\Pager\Subscriber\Paginate\Doctrine\ORM;
use Test\Tool\BaseTestCaseORM;
use Knp\Component\Pager\Paginator;
use Knp\Component\Pager\Pagination\SlidingPagination;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Knp\Component\Pager\Pagination\PaginationInterface;
use Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine\ORM\QuerySubscriber;
use Test\Fixture\Entity\Article;
use Test\Mock\PaginationSubscriber;
class QueryTest extends BaseTestCaseORM
{
/**
* @test
*/
function shouldPaginateSimpleDoctrineQuery()
{
$this->populate();
$dispatcher = new EventDispatcher;
$dispatcher->addSubscriber(new QuerySubscriber\UsesPaginator);
$dispatcher->addSubscriber(new PaginationSubscriber);
$p = new Paginator($dispatcher);
$query = $this->em->createQuery('SELECT a FROM Test\Fixture\Entity\Article a');
$view = $p->paginate($query, 1, 2);
$this->assertTrue($view instanceof PaginationInterface);
$this->assertEquals(1, $view->getCurrentPageNumber());
$this->assertEquals(2, $view->getItemNumberPerPage());
$this->assertEquals(4, $view->getTotalItemCount());
$items = $view->getItems();
$this->assertEquals(2, count($items));
$this->assertEquals('summer', $items[0]->getTitle());
$this->assertEquals('winter', $items[1]->getTitle());
}
/**
* @test
*/
function shouldSupportPaginateStrategySubscriber()
{
$query = $this
->getMockSqliteEntityManager()
->createQuery('SELECT a FROM Test\Fixture\Entity\Article a')
;
$p = new Paginator;
$view = $p->paginate($query, 1, 10);
$this->assertTrue($view instanceof PaginationInterface);
}
protected function getUsedEntityFixtures()
{
return array('Test\Fixture\Entity\Article');
}
private function populate()
{
$em = $this->getMockSqliteEntityManager();
$summer = new Article;
$summer->setTitle('summer');
$winter = new Article;
$winter->setTitle('winter');
$autumn = new Article;
$autumn->setTitle('autumn');
$spring = new Article;
$spring->setTitle('spring');
$em->persist($summer);
$em->persist($winter);
$em->persist($autumn);
$em->persist($spring);
$em->flush();
}
}

View File

@@ -0,0 +1,133 @@
<?php
namespace Test\Pager\Subscriber\Paginate\Doctrine\ORM\QueryTest;
use Test\Tool\BaseTestCaseORM;
use Knp\Component\Pager\Paginator;
use Knp\Component\Pager\Pagination\SlidingPagination;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Knp\Component\Pager\Pagination\PaginationInterface;
use Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine\ORM\QuerySubscriber;
use Knp\Component\Pager\Event\Subscriber\Paginate\PaginationSubscriber;
use Test\Fixture\Entity\Shop\Product;
use Test\Fixture\Entity\Shop\Tag;
use Doctrine\ORM\Query;
use Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine\ORM\QuerySubscriber\UsesPaginator;
class UsesPaginatorTest extends BaseTestCaseORM
{
/**
* @test
*/
function shouldUseOutputWalkersIfAskedTo()
{
$this->populate();
$dql = <<<___SQL
SELECT p, t
FROM Test\Fixture\Entity\Shop\Product p
INNER JOIN p.tags t
GROUP BY p.id
HAVING p.numTags = COUNT(t)
___SQL;
$q = $this->em->createQuery($dql);
$q->setHydrationMode(Query::HYDRATE_ARRAY);
$q->setHint(UsesPaginator::HINT_FETCH_JOIN_COLLECTION, true);
$this->startQueryLog();
$p = new Paginator;
$view = $p->paginate($q, 1, 10, array('wrap-queries' => true));
$this->assertEquals(3, $this->queryAnalyzer->getNumExecutedQueries());
$this->assertEquals(3, count($view));
}
/**
* @test
*/
function shouldNotUseOutputWalkersByDefault()
{
$this->populate();
$dql = <<<___SQL
SELECT p
FROM Test\Fixture\Entity\Shop\Product p
GROUP BY p.id
___SQL;
$q = $this->em->createQuery($dql);
$q->setHint(UsesPaginator::HINT_FETCH_JOIN_COLLECTION, false);
$q->setHydrationMode(Query::HYDRATE_ARRAY);
$this->startQueryLog();
$p = new Paginator;
$view = $p->paginate($q, 1, 10, array('wrap-queries' => false));
$this->assertEquals(2, $this->queryAnalyzer->getNumExecutedQueries());
$this->assertEquals(3, count($view));
}
/**
* @test
*/
function shouldFetchJoinCollectionsIfNeeded()
{
$this->populate();
$dql = <<<___SQL
SELECT p, t
FROM Test\Fixture\Entity\Shop\Product p
INNER JOIN p.tags t
GROUP BY p.id
HAVING p.numTags = COUNT(t)
___SQL;
$q = $this->em->createQuery($dql);
$q->setHydrationMode(Query::HYDRATE_ARRAY);
$q->setHint(UsesPaginator::HINT_FETCH_JOIN_COLLECTION, true);
$this->startQueryLog();
$p = new Paginator;
$view = $p->paginate($q, 1, 10, array('wrap-queries' => true));
$this->assertEquals(3, $this->queryAnalyzer->getNumExecutedQueries());
$this->assertEquals(3, count($view));
}
protected function getUsedEntityFixtures()
{
return array(
'Test\Fixture\Entity\Shop\Product',
'Test\Fixture\Entity\Shop\Tag'
);
}
private function populate()
{
$em = $this->getMockSqliteEntityManager();
$cheep = new Tag;
$cheep->setName('Cheep');
$new = new Tag;
$new->setName('New');
$special = new Tag;
$special->setName('Special');
$starship = new Product;
$starship->setTitle('Starship');
$starship->setPrice(277.66);
$starship->addTag($new);
$starship->addTag($special);
$cheese = new Product;
$cheese->setTitle('Cheese');
$cheese->setPrice(7.66);
$cheese->addTag($cheep);
$shoe = new Product;
$shoe->setTitle('Shoe');
$shoe->setPrice(2.66);
$shoe->addTag($special);
$em->persist($special);
$em->persist($cheep);
$em->persist($new);
$em->persist($starship);
$em->persist($cheese);
$em->persist($shoe);
$em->flush();
}
}

View File

@@ -0,0 +1,68 @@
<?php
use Elastica\Query;
use Elastica\Query\Term;
use Elastica\Result;
use Elastica\Type;
use Knp\Component\Pager\Paginator;
use Knp\Component\Pager\Event\Subscriber\Paginate\ElasticaQuerySubscriber;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Test\Mock\PaginationSubscriber as MockPaginationSubscriber;
use Test\Tool\BaseTestCase;
class ElasticaTest extends BaseTestCase
{
public function testElasticaSubscriber()
{
$dispatcher = new EventDispatcher;
$dispatcher->addSubscriber(new ElasticaQuerySubscriber());
$dispatcher->addSubscriber(new MockPaginationSubscriber); // pagination view
$p = new Paginator($dispatcher);
$query = Query::create(new Term(array(
'name' => 'Fred',
)));
$response = $this->getMockBuilder('Elastica\\ResultSet')->disableOriginalConstructor()->getMock();
$response->expects($this->once())
->method('getTotalHits')
->will($this->returnValue(2));
$response->expects($this->once())
->method('getResults')
->will($this->returnValue(array(new Result(array()), new Result(array()))));
$searchable = $this->getMockBuilder('Elastica\\SearchableInterface')->getMock();
$searchable->expects($this->once())
->method('search')
->with($query)
->will($this->returnValue($response));
$view = $p->paginate(array($searchable, $query), 1, 10);
$this->assertEquals(0, $query->getParam('from'), 'Query offset set correctly');
$this->assertEquals(10, $query->getParam('size'), 'Query limit set correctly');
$this->assertSame($response, $view->getCustomParameter('resultSet'), 'Elastica ResultSet available in Paginator');
$this->assertEquals(1, $view->getCurrentPageNumber());
$this->assertEquals(10, $view->getItemNumberPerPage());
$this->assertEquals(2, count($view->getItems()));
$this->assertEquals(2, $view->getTotalItemCount());
}
/**
* @test
*/
function shouldSlicePaginateAnArray()
{
/*$dispatcher = new EventDispatcher;
$dispatcher->addSubscriber(new ArraySubscriber);
$dispatcher->addSubscriber(new MockPaginationSubscriber); // pagination view
$p = new Paginator($dispatcher);
$items = range('a', 'u');
$view = $p->paginate($items, 2, 10);
$this->assertEquals(2, $view->getCurrentPageNumber());
$this->assertEquals(10, $view->getItemNumberPerPage());
$this->assertEquals(10, count($view->getItems()));
$this->assertEquals(21, $view->getTotalItemCount());*/
}
}

View File

@@ -0,0 +1,25 @@
<?php
use Test\Tool\BaseTestCase;
use Knp\Component\Pager\Event\Subscriber\Paginate\PaginationSubscriber;
use Knp\Component\Pager\Event\BeforeEvent;
class PaginationSubscriberTest extends BaseTestCase
{
/**
* @test
*/
function shouldRegisterExpectedSubscribersOnlyOnce()
{
$dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')->getMock();
$dispatcher->expects($this->exactly(13))->method('addSubscriber');
$subscriber = new PaginationSubscriber;
$beforeEvent = new BeforeEvent($dispatcher);
$subscriber->before($beforeEvent);
// Subsequent calls do not add more subscribers
$subscriber->before($beforeEvent);
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace Test\Pager\Subscriber\Paginate;
use Knp\Component\Pager\Paginator;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Knp\Component\Pager\Pagination\PaginationInterface;
use Knp\Component\Pager\Event\Subscriber\Paginate\ArraySubscriber;
use Knp\Component\Pager\Event\Subscriber\Paginate\SolariumQuerySubscriber;
use Test\Mock\PaginationSubscriber as MockPaginationSubscriber;
class SolariumQuerySubscriberTest extends \PHPUnit_Framework_TestCase
{
/**
* @test
* @expectedException RuntimeException
* @expectedExceptionMessage One of listeners must count and slice given target
*/
function testArrayShouldNotBeHandled()
{
$array = array(1 => 'foo', 2 => 'bar');
$dispatcher = new EventDispatcher;
$dispatcher->addSubscriber(new SolariumQuerySubscriber);
$dispatcher->addSubscriber(new MockPaginationSubscriber);
$p = new Paginator($dispatcher);
$p->paginate($array, 1, 10);
}
}

View File

@@ -0,0 +1,115 @@
<?php
namespace Test\Pager\Subscriber\Sortable;
use Knp\Component\Pager\Event\ItemsEvent;
use Knp\Component\Pager\Event\Subscriber\Sortable\ArraySubscriber;
use Test\Tool\BaseTestCase;
use Knp\Component\Pager\PaginatorInterface;
class ArraySubscriberTest extends BaseTestCase
{
/**
* @test
*/
public function shouldSort()
{
$array = array(
array('entry' => array('sortProperty' => 2)),
array('entry' => array('sortProperty' => 3)),
array('entry' => array('sortProperty' => 1)),
);
$itemsEvent = new ItemsEvent(0, 10);
$itemsEvent->target = &$array;
$itemsEvent->options = array(PaginatorInterface::SORT_FIELD_PARAMETER_NAME => 'sort', PaginatorInterface::SORT_DIRECTION_PARAMETER_NAME => 'ord');
$arraySubscriber = new ArraySubscriber();
// test asc sort
$_GET = array('sort' => '[entry][sortProperty]', 'ord' => 'asc');
$arraySubscriber->items($itemsEvent);
$this->assertEquals(1, $array[0]['entry']['sortProperty']);
$itemsEvent->unsetCustomPaginationParameter('sorted');
// test desc sort
$_GET ['ord'] = 'desc';
$arraySubscriber->items($itemsEvent);
$this->assertEquals(3, $array[0]['entry']['sortProperty']);
}
/**
* @test
*/
public function shouldSortWithCustomCallback()
{
$array = array(
array('name' => 'hot'),
array('name' => 'cold'),
array('name' => 'hot'),
);
$itemsEvent = new ItemsEvent(0, 10);
$itemsEvent->target = &$array;
$itemsEvent->options = array(
PaginatorInterface::SORT_FIELD_PARAMETER_NAME => 'sort',
PaginatorInterface::SORT_DIRECTION_PARAMETER_NAME => 'ord',
'sortFunction' => function (&$target, $sortField, $sortDirection) {
usort($target, function($object1, $object2) use ($sortField, $sortDirection) {
if ($object1[$sortField] === $object2[$sortField]) {
return 0;
}
return ($object1[$sortField] === 'hot' ? 1 : -1) * ($sortDirection === 'asc' ? 1 : -1);
});
},
);
$arraySubscriber = new ArraySubscriber();
// test asc sort
$_GET = array('sort' => '.name', 'ord' => 'asc');
$arraySubscriber->items($itemsEvent);
$this->assertEquals('cold', $array[0]['name']);
$itemsEvent->unsetCustomPaginationParameter('sorted');
// test desc sort
$_GET['ord'] = 'desc';
$arraySubscriber->items($itemsEvent);
$this->assertEquals('hot', $array[0]['name']);
}
/**
* @test
*/
public function shouldSortEvenWhenTheSortPropertyIsNotAccessible()
{
$array = array(
array('entry' => array('sortProperty' => 2)),
array('entry' => array()),
array('entry' => array('sortProperty' => 1)),
);
$itemsEvent = new ItemsEvent(0, 10);
$itemsEvent->target = &$array;
$itemsEvent->options = array(PaginatorInterface::SORT_FIELD_PARAMETER_NAME => 'sort', PaginatorInterface::SORT_DIRECTION_PARAMETER_NAME => 'ord');
$arraySubscriber = new ArraySubscriber();
// test asc sort
$_GET = array('sort' => '[entry][sortProperty]', 'ord' => 'asc');
$arraySubscriber->items($itemsEvent);
$this->assertEquals(false, isset($array[0]['entry']['sortProperty']));
$itemsEvent->unsetCustomPaginationParameter('sorted');
// test desc sort
$_GET ['ord'] = 'desc';
$arraySubscriber->items($itemsEvent);
$this->assertEquals(2, $array[0]['entry']['sortProperty']);
}
}

View File

@@ -0,0 +1,89 @@
<?php
namespace Test\Pager\Subscriber\Sortable\Doctrine\ODM\MongoDB;
use Test\Tool\BaseTestCaseMongoODM;
use Knp\Component\Pager\Paginator;
use Knp\Component\Pager\Pagination\SlidingPagination;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Knp\Component\Pager\Pagination\PaginationInterface;
use Knp\Component\Pager\Event\Subscriber\Paginate\PaginationSubscriber;
use Knp\Component\Pager\Event\Subscriber\Sortable\Doctrine\ODM\MongoDB\QuerySubscriber as Sortable;
use Test\Fixture\Document\Article;
class QueryTest extends BaseTestCaseMongoODM
{
/**
* @test
*/
function shouldSortSimpleDoctrineQuery()
{
$this->populate();
$dispatcher = new EventDispatcher;
$dispatcher->addSubscriber(new PaginationSubscriber);
$dispatcher->addSubscriber(new Sortable);
$p = new Paginator($dispatcher);
$_GET['sort'] = 'title';
$_GET['direction'] = 'asc';
$qb = $this->dm->createQueryBuilder('Test\Fixture\Document\Article');
$query = $qb->getQuery();
$view = $p->paginate($query, 1, 10);
$items = array_values($view->getItems());
$this->assertEquals(4, count($items));
$this->assertEquals('autumn', $items[0]->getTitle());
$this->assertEquals('spring', $items[1]->getTitle());
$this->assertEquals('summer', $items[2]->getTitle());
$this->assertEquals('winter', $items[3]->getTitle());
$_GET['direction'] = 'desc';
$view = $p->paginate($query, 1, 10);
$items = array_values($view->getItems());
$this->assertEquals(4, count($items));
$this->assertEquals('winter', $items[0]->getTitle());
$this->assertEquals('summer', $items[1]->getTitle());
$this->assertEquals('spring', $items[2]->getTitle());
$this->assertEquals('autumn', $items[3]->getTitle());
}
/**
* @test
*/
function shouldSortOnAnyField()
{
$_GET['sort'] = '"title\'';
$_GET['direction'] = 'asc';
$query = $this
->getMockDocumentManager()
->createQueryBuilder('Test\Fixture\Document\Article')
->getQuery()
;
$p = new Paginator;
$view = $p->paginate($query, 1, 10);
}
private function populate()
{
$em = $this->getMockDocumentManager();
$summer = new Article;
$summer->setTitle('summer');
$winter = new Article;
$winter->setTitle('winter');
$autumn = new Article;
$autumn->setTitle('autumn');
$spring = new Article;
$spring->setTitle('spring');
$em->persist($summer);
$em->persist($winter);
$em->persist($autumn);
$em->persist($spring);
$em->flush();
}
}

View File

@@ -0,0 +1,90 @@
<?php
namespace Test\Pager\Subscriber\Sortable\Doctrine\ODM\MongoDB;
use Knp\Component\Pager\PaginatorInterface;
use Test\Tool\BaseTestCaseMongoODM;
use Knp\Component\Pager\Paginator;
use Knp\Component\Pager\Pagination\SlidingPagination;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Knp\Component\Pager\Pagination\PaginationInterface;
use Knp\Component\Pager\Event\Subscriber\Paginate\PaginationSubscriber;
use Knp\Component\Pager\Event\Subscriber\Sortable\Doctrine\ODM\MongoDB\QuerySubscriber as Sortable;
use Test\Fixture\Document\Article;
class WhitelistTest extends BaseTestCaseMongoODM
{
/**
* @test
* @expectedException UnexpectedValueException
*/
function shouldWhitelistSortableFields()
{
$this->populate();
$_GET['sort'] = 'title';
$_GET['direction'] = 'asc';
$query = $this->dm
->createQueryBuilder('Test\Fixture\Document\Article')
->getQuery()
;
$p = new Paginator;
$sortFieldWhitelist = array('title');
$view = $p->paginate($query, 1, 10, compact(PaginatorInterface::SORT_FIELD_WHITELIST));
$items = array_values($view->getItems());
$this->assertEquals(4, count($items));
$this->assertEquals('autumn', $items[0]->getTitle());
$_GET['sort'] = 'id';
$view = $p->paginate($query, 1, 10, compact(PaginatorInterface::SORT_FIELD_WHITELIST));
}
/**
* @test
*/
function shouldSortWithoutSpecificWhitelist()
{
$this->populate();
$_GET['sort'] = 'title';
$_GET['direction'] = 'asc';
$query = $this->dm
->createQueryBuilder('Test\Fixture\Document\Article')
->getQuery()
;
$p = new Paginator;
$view = $p->paginate($query, 1, 10);
$items = array_values($view->getItems());
$this->assertEquals('autumn', $items[0]->getTitle());
$_GET['sort'] = 'id';
$view = $p->paginate($query, 1, 10);
$items = array_values($view->getItems());
$this->assertEquals('summer', $items[0]->getTitle());
}
private function populate()
{
$em = $this->getMockDocumentManager();
$summer = new Article;
$summer->setTitle('summer');
$winter = new Article;
$winter->setTitle('winter');
$autumn = new Article;
$autumn->setTitle('autumn');
$spring = new Article;
$spring->setTitle('spring');
$em->persist($summer);
$em->persist($winter);
$em->persist($autumn);
$em->persist($spring);
$em->flush();
}
}

View File

@@ -0,0 +1,254 @@
<?php
namespace Test\Pager\Subscriber\Sortable\Doctrine\ORM;
use Knp\Component\Pager\PaginatorInterface;
use Test\Tool\BaseTestCaseORM;
use Knp\Component\Pager\Paginator;
use Knp\Component\Pager\Pagination\SlidingPagination;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Knp\Component\Pager\Pagination\PaginationInterface;
use Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine\ORM\QuerySubscriber;
use Knp\Component\Pager\Event\Subscriber\Paginate\PaginationSubscriber;
use Knp\Component\Pager\Event\Subscriber\Sortable\Doctrine\ORM\QuerySubscriber as Sortable;
use Test\Fixture\Entity\Article;
use Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine\ORM\QuerySubscriber\UsesPaginator;
class QueryTest extends BaseTestCaseORM
{
/**
* @test
*/
function shouldHandleApcQueryCache()
{
if (!extension_loaded('apc') || !ini_get('apc.enable_cli')) {
$this->markTestSkipped('APC extension is not loaded.');
}
$config = new \Doctrine\ORM\Configuration();
$config->setMetadataCacheImpl(new \Doctrine\Common\Cache\ApcCache);
$config->setQueryCacheImpl(new \Doctrine\Common\Cache\ApcCache);
$config->setProxyDir(__DIR__);
$config->setProxyNamespace('Gedmo\Mapping\Proxy');
$config->getAutoGenerateProxyClasses(false);
$config->setMetadataDriverImpl($this->getMetadataDriverImplementation());
$conn = array(
'driver' => 'pdo_sqlite',
'memory' => true,
);
$em = \Doctrine\ORM\EntityManager::create($conn, $config);
$schema = array_map(function($class) use ($em) {
return $em->getClassMetadata($class);
}, (array)$this->getUsedEntityFixtures());
$schemaTool = new \Doctrine\ORM\Tools\SchemaTool($em);
$schemaTool->dropSchema(array());
$schemaTool->createSchema($schema);
$this->populate($em);
$_GET['sort'] = 'a.title';
$_GET['direction'] = 'asc';
$query = $em->createQuery('SELECT a FROM Test\Fixture\Entity\Article a');
$p = new Paginator;
$view = $p->paginate($query, 1, 10);
$query = $em->createQuery('SELECT a FROM Test\Fixture\Entity\Article a');
$view = $p->paginate($query, 1, 10);
}
/**
* @test
*/
function shouldSortSimpleDoctrineQuery()
{
$em = $this->getMockSqliteEntityManager();
$this->populate($em);
$dispatcher = new EventDispatcher;
$dispatcher->addSubscriber(new PaginationSubscriber);
$dispatcher->addSubscriber(new Sortable);
$p = new Paginator($dispatcher);
$_GET['sort'] = 'a.title';
$_GET['direction'] = 'asc';
$this->startQueryLog();
$query = $this->em->createQuery('SELECT a FROM Test\Fixture\Entity\Article a');
$query->setHint(UsesPaginator::HINT_FETCH_JOIN_COLLECTION, false);
$view = $p->paginate($query, 1, 10);
$items = $view->getItems();
$this->assertEquals(4, count($items));
$this->assertEquals('autumn', $items[0]->getTitle());
$this->assertEquals('spring', $items[1]->getTitle());
$this->assertEquals('summer', $items[2]->getTitle());
$this->assertEquals('winter', $items[3]->getTitle());
$_GET['direction'] = 'desc';
$view = $p->paginate($query, 1, 10);
$items = $view->getItems();
$this->assertEquals(4, count($items));
$this->assertEquals('winter', $items[0]->getTitle());
$this->assertEquals('summer', $items[1]->getTitle());
$this->assertEquals('spring', $items[2]->getTitle());
$this->assertEquals('autumn', $items[3]->getTitle());
$this->assertEquals(4, $this->queryAnalyzer->getNumExecutedQueries());
$executed = $this->queryAnalyzer->getExecutedQueries();
// Different aliases separators according to Doctrine version
if (version_compare(\Doctrine\ORM\Version::VERSION, '2.5', '<')) {
$this->assertEquals('SELECT a0_.id AS id0, a0_.title AS title1, a0_.enabled AS enabled2 FROM Article a0_ ORDER BY a0_.title ASC LIMIT 10 OFFSET 0', $executed[1]);
$this->assertEquals('SELECT a0_.id AS id0, a0_.title AS title1, a0_.enabled AS enabled2 FROM Article a0_ ORDER BY a0_.title DESC LIMIT 10 OFFSET 0', $executed[3]);
} else {
$this->assertEquals('SELECT a0_.id AS id_0, a0_.title AS title_1, a0_.enabled AS enabled_2 FROM Article a0_ ORDER BY a0_.title ASC LIMIT 10 OFFSET 0', $executed[1]);
$this->assertEquals('SELECT a0_.id AS id_0, a0_.title AS title_1, a0_.enabled AS enabled_2 FROM Article a0_ ORDER BY a0_.title DESC LIMIT 10 OFFSET 0', $executed[3]);
}
}
/**
* @test
* @expectedException UnexpectedValueException
*/
function shouldValidateSortableParameters()
{
$_GET['sort'] = '"a.title\'';
$_GET['direction'] = 'asc';
$query = $this
->getMockSqliteEntityManager()
->createQuery('SELECT a FROM Test\Fixture\Entity\Article a')
;
$p = new Paginator;
$view = $p->paginate($query, 1, 10);
}
/**
* @test
*/
function shouldSortByAnyAvailableAlias()
{
$em = $this->getMockSqliteEntityManager();
$this->populate($em);
$_GET['sort'] = 'counter';
$_GET['direction'] = 'asc';
$dql = <<<___SQL
SELECT a, COUNT(a) AS counter
FROM Test\Fixture\Entity\Article a
___SQL;
$query = $this->em->createQuery($dql);
$query->setHint(UsesPaginator::HINT_FETCH_JOIN_COLLECTION, false);
$p = new Paginator;
$this->startQueryLog();
$view = $p->paginate($query, 1, 10, array(PaginatorInterface::DISTINCT => false));
$this->assertEquals(2, $this->queryAnalyzer->getNumExecutedQueries());
$executed = $this->queryAnalyzer->getExecutedQueries();
// Different aliases separators according to Doctrine version
if (version_compare(\Doctrine\ORM\Version::VERSION, '2.5', '<')) {
$this->assertEquals('SELECT a0_.id AS id0, a0_.title AS title1, a0_.enabled AS enabled2, COUNT(a0_.id) AS sclr3 FROM Article a0_ ORDER BY sclr3 ASC LIMIT 10 OFFSET 0', $executed[1]);
} else {
$this->assertEquals('SELECT a0_.id AS id_0, a0_.title AS title_1, a0_.enabled AS enabled_2, COUNT(a0_.id) AS sclr_3 FROM Article a0_ ORDER BY sclr_3 ASC LIMIT 10 OFFSET 0', $executed[1]);
}
}
/**
* @test
*/
function shouldWorkWithInitialPaginatorEventDispatcher()
{
$em = $this->getMockSqliteEntityManager();
$this->populate($em);
$_GET['sort'] = 'a.title';
$_GET['direction'] = 'asc';
$query = $this
->em
->createQuery('SELECT a FROM Test\Fixture\Entity\Article a')
;
$query->setHint(UsesPaginator::HINT_FETCH_JOIN_COLLECTION, false);
$p = new Paginator;
$this->startQueryLog();
$view = $p->paginate($query, 1, 10);
$this->assertTrue($view instanceof SlidingPagination);
$this->assertEquals(2, $this->queryAnalyzer->getNumExecutedQueries());
$executed = $this->queryAnalyzer->getExecutedQueries();
// Different aliases separators according to Doctrine version
if (version_compare(\Doctrine\ORM\Version::VERSION, '2.5', '<')) {
$this->assertEquals('SELECT a0_.id AS id0, a0_.title AS title1, a0_.enabled AS enabled2 FROM Article a0_ ORDER BY a0_.title ASC LIMIT 10 OFFSET 0', $executed[1]);
} else {
$this->assertEquals('SELECT a0_.id AS id_0, a0_.title AS title_1, a0_.enabled AS enabled_2 FROM Article a0_ ORDER BY a0_.title ASC LIMIT 10 OFFSET 0', $executed[1]);
}
}
/**
* @test
*/
function shouldNotExecuteExtraQueriesWhenCountIsZero()
{
$_GET['sort'] = 'a.title';
$_GET['direction'] = 'asc';
$query = $this
->getMockSqliteEntityManager()
->createQuery('SELECT a FROM Test\Fixture\Entity\Article a')
;
$p = new Paginator;
$this->startQueryLog();
$view = $p->paginate($query, 1, 10);
$this->assertTrue($view instanceof SlidingPagination);
$this->assertEquals(2, $this->queryAnalyzer->getNumExecutedQueries());
}
protected function getUsedEntityFixtures()
{
return array('Test\Fixture\Entity\Article');
}
private function populate($em)
{
$summer = new Article;
$summer->setTitle('summer');
$winter = new Article;
$winter->setTitle('winter');
$autumn = new Article;
$autumn->setTitle('autumn');
$spring = new Article;
$spring->setTitle('spring');
$em->persist($summer);
$em->persist($winter);
$em->persist($autumn);
$em->persist($spring);
$em->flush();
}
private function getApcEntityManager()
{
$config = new \Doctrine\ORM\Configuration();
$config->setMetadataCacheImpl(new \Doctrine\Common\Cache\ApcCache);
$config->setQueryCacheImpl(new \Doctrine\Common\Cache\ApcCache);
$config->setProxyDir(__DIR__);
$config->setProxyNamespace('Gedmo\Mapping\Proxy');
$config->setAutoGenerateProxyClasses(false);
$config->setMetadataDriverImpl($this->getMetadataDriverImplementation());
$conn = array(
'driver' => 'pdo_sqlite',
'memory' => true,
);
$em = \Doctrine\ORM\EntityManager::create($conn, $config);
return $em;
}
}

View File

@@ -0,0 +1,90 @@
<?php
namespace Test\Pager\Subscriber\Sortable\Doctrine\ORM;
use Knp\Component\Pager\PaginatorInterface;
use Test\Tool\BaseTestCaseORM;
use Knp\Component\Pager\Paginator;
use Knp\Component\Pager\Pagination\SlidingPagination;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Knp\Component\Pager\Pagination\PaginationInterface;
use Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine\ORM\QuerySubscriber;
use Knp\Component\Pager\Event\Subscriber\Paginate\PaginationSubscriber;
use Knp\Component\Pager\Event\Subscriber\Sortable\Doctrine\ORM\QuerySubscriber as Sortable;
use Test\Fixture\Entity\Article;
class WhitelistTest extends BaseTestCaseORM
{
/**
* @test
* @expectedException UnexpectedValueException
*/
function shouldWhitelistSortableFields()
{
$this->populate();
$_GET['sort'] = 'a.title';
$_GET['direction'] = 'asc';
$query = $this->em->createQuery('SELECT a FROM Test\Fixture\Entity\Article a');
$p = new Paginator;
$sortFieldWhitelist = array('a.title');
$view = $p->paginate($query, 1, 10, compact(PaginatorInterface::SORT_FIELD_WHITELIST));
$items = $view->getItems();
$this->assertEquals(4, count($items));
$this->assertEquals('autumn', $items[0]->getTitle());
$_GET['sort'] = 'a.id';
$view = $p->paginate($query, 1, 10, compact(PaginatorInterface::SORT_FIELD_WHITELIST));
}
/**
* @test
*/
function shouldSortWithoutSpecificWhitelist()
{
$this->populate();
$_GET['sort'] = 'a.title';
$_GET['direction'] = 'asc';
$query = $this->em->createQuery('SELECT a FROM Test\Fixture\Entity\Article a');
$p = new Paginator;
$view = $p->paginate($query, 1, 10);
$items = $view->getItems();
$this->assertEquals('autumn', $items[0]->getTitle());
$_GET['sort'] = 'a.id';
$view = $p->paginate($query, 1, 10);
$items = $view->getItems();
$this->assertEquals('summer', $items[0]->getTitle());
}
protected function getUsedEntityFixtures()
{
return array('Test\Fixture\Entity\Article');
}
private function populate()
{
$em = $this->getMockSqliteEntityManager();
$summer = new Article;
$summer->setTitle('summer');
$winter = new Article;
$winter->setTitle('winter');
$autumn = new Article;
$autumn->setTitle('autumn');
$spring = new Article;
$spring->setTitle('spring');
$em->persist($summer);
$em->persist($winter);
$em->persist($autumn);
$em->persist($spring);
$em->flush();
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace Test\Pager\Subscriber\Sortable;
use Knp\Component\Pager\Paginator;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Knp\Component\Pager\Pagination\PaginationInterface;
use Knp\Component\Pager\Event\Subscriber\Sortable\SolariumQuerySubscriber;
use Test\Mock\PaginationSubscriber as MockPaginationSubscriber;
class SolariumQuerySubscriberTest extends \PHPUnit_Framework_TestCase
{
/**
* @test
* @expectedException RuntimeException
* @expectedExceptionMessage One of listeners must count and slice given target
*/
function testArrayShouldNotBeHandled()
{
$array = array(
'results' => array(
0 => array(
'city' => 'Lyon',
'market' => 'E'
),
1 => array(
'city' => 'Paris',
'market' => 'G'
),
),
'nbTotalResults' => 2
);
$dispatcher = new EventDispatcher;
$dispatcher->addSubscriber(new SolariumQuerySubscriber);
$dispatcher->addSubscriber(new MockPaginationSubscriber);
$p = new Paginator($dispatcher);
$p->paginate($array, 1, 10);
}
}

View File

@@ -0,0 +1,25 @@
<?php
use Test\Tool\BaseTestCase;
use Knp\Component\Pager\Event\Subscriber\Sortable\SortableSubscriber;
use Knp\Component\Pager\Event\BeforeEvent;
class SortableSubscriberTest extends BaseTestCase
{
/**
* @test
*/
function shouldRegisterExpectedSubscribersOnlyOnce()
{
$dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')->getMock();
$dispatcher->expects($this->exactly(6))->method('addSubscriber');
$subscriber = new SortableSubscriber;
$beforeEvent = new BeforeEvent($dispatcher);
$subscriber->before($beforeEvent);
// Subsequent calls do not add more subscribers
$subscriber->before($beforeEvent);
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace Test\Tool;
use PHPUnit_Framework_TestCase;
/**
* Base test case
*/
abstract class BaseTestCase extends PHPUnit_Framework_TestCase
{
/**
* {@inheritdoc}
*/
protected function setUp()
{
}
}

View File

@@ -0,0 +1,162 @@
<?php
namespace Test\Tool;
use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\ODM\MongoDB\Mapping\Driver\AnnotationDriver;
use Doctrine\ODM\MongoDB\DocumentManager;
use Doctrine\Common\EventManager;
use Doctrine\MongoDB\Connection;
/**
* Base test case contains common mock objects
*/
abstract class BaseTestCaseMongoODM extends \PHPUnit_Framework_TestCase
{
/**
* @var DocumentManager
*/
protected $dm;
/**
* {@inheritdoc}
*/
protected function setUp()
{
if (!class_exists('MongoClient')) {
$this->markTestSkipped('Missing Mongo extension.');
}
}
/**
* {@inheritdoc}
*/
protected function tearDown()
{
if ($this->dm) {
foreach ($this->dm->getDocumentDatabases() as $db) {
foreach ($db->listCollections() as $collection) {
$collection->drop();
}
}
$this->dm->getConnection()->close();
$this->dm = null;
}
}
/**
* DocumentManager mock object together with
* annotation mapping driver and database
*
* @param EventManager $evm
* @return DocumentManager
*/
protected function getMockDocumentManager(EventManager $evm = null)
{
$conn = new Connection;
$config = $this->getMockAnnotatedConfig();
try {
$this->dm = DocumentManager::create($conn, $config, $evm ?: $this->getEventManager());
$this->dm->getConnection()->connect();
} catch (\MongoException $e) {
$this->markTestSkipped('Doctrine MongoDB ODM failed to connect');
}
return $this->dm;
}
/**
* DocumentManager mock object with
* annotation mapping driver
*
* @param EventManager $evm
* @return DocumentManager
*/
protected function getMockMappedDocumentManager(EventManager $evm = null)
{
$conn = $this->getMock('Doctrine\\MongoDB\\Connection');
$config = $this->getMockAnnotatedConfig();
$this->dm = DocumentManager::create($conn, $config, $evm ?: $this->getEventManager());
return $this->dm;
}
/**
* Creates default mapping driver
*
* @return \Doctrine\ORM\Mapping\Driver\Driver
*/
protected function getMetadataDriverImplementation()
{
return new AnnotationDriver($_ENV['annotation_reader']);
}
/**
* Build event manager
*
* @return EventManager
*/
private function getEventManager()
{
$evm = new EventManager;
return $evm;
}
/**
* Get annotation mapping configuration
*
* @return Doctrine\ORM\Configuration
*/
private function getMockAnnotatedConfig()
{
$config = $this->getMock('Doctrine\\ODM\\MongoDB\\Configuration');
$config->expects($this->once())
->method('getProxyDir')
->will($this->returnValue(__DIR__.'/../../temp'));
$config->expects($this->once())
->method('getProxyNamespace')
->will($this->returnValue('Proxy'));
$config->expects($this->once())
->method('getHydratorDir')
->will($this->returnValue(__DIR__.'/../../temp'));
$config->expects($this->once())
->method('getHydratorNamespace')
->will($this->returnValue('Hydrator'));
$config->expects($this->any())
->method('getDefaultDB')
->will($this->returnValue('knp_pager_tests'));
$config->expects($this->once())
->method('getAutoGenerateProxyClasses')
->will($this->returnValue(true));
$config->expects($this->once())
->method('getAutoGenerateHydratorClasses')
->will($this->returnValue(true));
$config->expects($this->once())
->method('getClassMetadataFactoryName')
->will($this->returnValue('Doctrine\\ODM\\MongoDB\\Mapping\\ClassMetadataFactory'));
$config->expects($this->any())
->method('getMongoCmd')
->will($this->returnValue('$'));
$config
->expects($this->any())
->method('getDefaultCommitOptions')
->will($this->returnValue(array('safe' => true)))
;
$mappingDriver = $this->getMetadataDriverImplementation();
$config->expects($this->any())
->method('getMetadataDriverImpl')
->will($this->returnValue($mappingDriver));
return $config;
}
}

View File

@@ -0,0 +1,264 @@
<?php
namespace Test\Tool;
use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\ORM\Mapping\DefaultNamingStrategy;
use Doctrine\ORM\Mapping\DefaultQuoteStrategy;
use Doctrine\ORM\Mapping\Driver\AnnotationDriver;
use Doctrine\ORM\EntityManager;
use Doctrine\Common\EventManager;
use Doctrine\ORM\Tools\SchemaTool;
/**
* Base test case contains common mock objects
* and functionality among all extensions using
* ORM object manager
*
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
* @package Gedmo
* @subpackage BaseTestCase
* @link http://www.gediminasm.org
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
abstract class BaseTestCaseORM extends \PHPUnit_Framework_TestCase
{
/**
* @var EntityManager
*/
protected $em;
/**
* @var QueryAnalyzer
*/
protected $queryAnalyzer;
/**
* {@inheritdoc}
*/
protected function setUp()
{
}
/**
* EntityManager mock object together with
* annotation mapping driver and pdo_sqlite
* database in memory
*
* @param EventManager $evm
* @return EntityManager
*/
protected function getMockSqliteEntityManager(EventManager $evm = null)
{
$conn = array(
'driver' => 'pdo_sqlite',
'memory' => true,
);
$config = $this->getMockAnnotatedConfig();
$em = EntityManager::create($conn, $config, $evm ?: $this->getEventManager());
$schema = array_map(function($class) use ($em) {
return $em->getClassMetadata($class);
}, (array)$this->getUsedEntityFixtures());
$schemaTool = new SchemaTool($em);
$schemaTool->dropSchema(array());
$schemaTool->createSchema($schema);
return $this->em = $em;
}
/**
* EntityManager mock object together with
* annotation mapping driver and custom
* connection
*
* @param array $conn
* @param EventManager $evm
* @return EntityManager
*/
protected function getMockCustomEntityManager(array $conn, EventManager $evm = null)
{
$config = $this->getMockAnnotatedConfig();
$em = EntityManager::create($conn, $config, $evm ?: $this->getEventManager());
$schema = array_map(function($class) use ($em) {
return $em->getClassMetadata($class);
}, (array)$this->getUsedEntityFixtures());
$schemaTool = new SchemaTool($em);
$schemaTool->dropSchema(array());
$schemaTool->createSchema($schema);
return $this->em = $em;
}
/**
* EntityManager mock object with
* annotation mapping driver
*
* @param EventManager $evm
* @return EntityManager
*/
protected function getMockMappedEntityManager(EventManager $evm = null)
{
$driver = $this->getMock('Doctrine\DBAL\Driver');
$driver->expects($this->once())
->method('getDatabasePlatform')
->will($this->returnValue($this->getMock('Doctrine\DBAL\Platforms\MySqlPlatform')));
$conn = $this->getMock('Doctrine\DBAL\Connection', array(), array(array(), $driver));
$conn->expects($this->once())
->method('getEventManager')
->will($this->returnValue($evm ?: $this->getEventManager()));
$config = $this->getMockAnnotatedConfig();
$this->em = EntityManager::create($conn, $config);
return $this->em;
}
/**
* Starts query statistic log
*
* @throws \RuntimeException
*/
protected function startQueryLog()
{
if (!$this->em || !$this->em->getConnection()->getDatabasePlatform()) {
throw new \RuntimeException('EntityManager and database platform must be initialized');
}
$this->queryAnalyzer = new QueryAnalyzer($this->em->getConnection()->getDatabasePlatform());
$this->em
->getConfiguration()
->expects($this->any())
->method('getSQLLogger')
->will($this->returnValue($this->queryAnalyzer));
}
/**
* Stops query statistic log and outputs
* the data to screen or file
*
* @param boolean $dumpOnlySql
* @param boolean $writeToLog
* @throws \RuntimeException
*/
protected function stopQueryLog($dumpOnlySql = false, $writeToLog = false)
{
if ($this->queryAnalyzer) {
$output = $this->queryAnalyzer->getOutput($dumpOnlySql);
if ($writeToLog) {
$fileName = __DIR__.'/../../temp/query_debug_'.time().'.log';
if (($file = fopen($fileName, 'w+')) !== false) {
fwrite($file, $output);
fclose($file);
} else {
throw new \RuntimeException('Unable to write to the log file');
}
} else {
echo $output;
}
}
}
/**
* Creates default mapping driver
*
* @return \Doctrine\ORM\Mapping\Driver\Driver
*/
protected function getMetadataDriverImplementation()
{
return new AnnotationDriver($_ENV['annotation_reader']);
}
/**
* Get a list of used fixture classes
*
* @return array
*/
abstract protected function getUsedEntityFixtures();
/**
* Build event manager
*
* @return EventManager
*/
private function getEventManager()
{
$evm = new EventManager;
return $evm;
}
/**
* Get annotation mapping configuration
*
* @return Doctrine\ORM\Configuration
*/
private function getMockAnnotatedConfig()
{
$config = $this->getMock('Doctrine\ORM\Configuration');
$config
->expects($this->once())
->method('getProxyDir')
->will($this->returnValue(__DIR__.'/../../temp'))
;
$config
->expects($this->once())
->method('getProxyNamespace')
->will($this->returnValue('Proxy'))
;
$config
->expects($this->once())
->method('getAutoGenerateProxyClasses')
->will($this->returnValue(true))
;
$config
->expects($this->once())
->method('getClassMetadataFactoryName')
->will($this->returnValue('Doctrine\\ORM\\Mapping\\ClassMetadataFactory'))
;
$mappingDriver = $this->getMetadataDriverImplementation();
$config
->expects($this->any())
->method('getMetadataDriverImpl')
->will($this->returnValue($mappingDriver))
;
$config
->expects($this->any())
->method('getDefaultRepositoryClassName')
->will($this->returnValue('Doctrine\\ORM\\EntityRepository'))
;
$config
->expects($this->any())
->method('getQuoteStrategy')
->will($this->returnValue(new DefaultQuoteStrategy()))
;
$config
->expects($this->any())
->method('getNamingStrategy')
->will($this->returnValue(new DefaultNamingStrategy()))
;
$config
->expects($this->any())
->method('getCustomHydrationMode')
->will($this->returnValue('Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine\ORM\Query\AsIsHydrator'))
;
$config
->expects($this->any())
->method('getDefaultQueryHints')
->will($this->returnValue(array()))
;
return $config;
}
}

View File

@@ -0,0 +1,88 @@
<?php
namespace Test\Tool;
use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\DBAL\DriverManager;
use Doctrine\ODM\PHPCR\Mapping\Driver\AnnotationDriver;
use Doctrine\ODM\PHPCR\DocumentManager;
use Doctrine\Common\EventManager;
use Doctrine\MongoDB\Connection;
use Jackalope\RepositoryFactoryDoctrineDBAL;
use Jackalope\Session;
use Jackalope\Transport\DoctrineDBAL\RepositorySchema;
/**
* Base test case contains common mock objects
*/
abstract class BaseTestCasePHPCRODM extends \PHPUnit_Framework_TestCase
{
/**
* @var DocumentManager
*/
protected $dm;
protected function setUp()
{
if (!class_exists('Doctrine\ODM\PHPCR\Query\Query')) {
$this->markTestSkipped('Doctrine PHPCR-ODM is not available');
}
}
protected function tearDown()
{
if ($this->dm) {
$this->dm = null;
}
}
protected function getMockDocumentManager(EventManager $evm = null)
{
$config = new \Doctrine\ODM\PHPCR\Configuration();
$config->setMetadataDriverImpl($this->getMetadataDriverImplementation());
$this->dm = DocumentManager::create($this->getSession(), $config, $evm ?: $this->getEventManager());
return $this->dm;
}
protected function getMetadataDriverImplementation()
{
return new AnnotationDriver($_ENV['annotation_reader']);
}
private function getSession()
{
$connection = DriverManager::getConnection(array(
'driver' => 'pdo_sqlite',
'path' => ':memory:',
));
$factory = new RepositoryFactoryDoctrineDBAL();
$repository = $factory->getRepository(array(
'jackalope.doctrine_dbal_connection' => $connection,
));
$schema = new RepositorySchema(array('disable_fks' => true), $connection);
$schema->reset();
$session = $repository->login(new \PHPCR\SimpleCredentials('', ''));
$cnd = <<<CND
<phpcr='http://www.doctrine-project.org/projects/phpcr_odm'>
[phpcr:managed]
mixin
- phpcr:class (STRING)
- phpcr:classparents (STRING) multiple
CND;
$session->getWorkspace()->getNodeTypeManager()->registerNodeTypesCnd($cnd, true);
return $session;
}
private function getEventManager()
{
$evm = new EventManager();
return $evm;
}
}

View File

@@ -0,0 +1,242 @@
<?php
namespace Test\Tool;
use Doctrine\DBAL\Logging\SQLLogger;
use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Platforms\AbstractPlatform;
/**
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
* @package Gedmo.Tool.Logging.DBAL
* @subpackage QueryAnalyzer
* @link http://www.gediminasm.org
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
class QueryAnalyzer implements SQLLogger
{
/**
* Used database platform
*
* @var AbstractPlatform
*/
protected $platform;
/**
* Start time of currently executed query
*
* @var integer
*/
private $queryStartTime = null;
/**
* Total execution time of all queries
*
* @var integer
*/
private $totalExecutionTime = 0;
/**
* List of queries executed
*
* @var array
*/
private $queries = array();
/**
* Query execution times indexed
* in same order as queries
*
* @var array
*/
private $queryExecutionTimes = array();
/**
* Initialize log listener with database
* platform, which is needed for parameter
* conversion
*
* @param AbstractPlatform $platform
*/
public function __construct(AbstractPlatform $platform)
{
$this->platform = $platform;
}
/**
* {@inheritdoc}
*/
public function startQuery($sql, array $params = null, array $types = null)
{
$this->queryStartTime = microtime(true);
$this->queries[] = $this->generateSql($sql, $params, $types);
}
/**
* {@inheritdoc}
*/
public function stopQuery()
{
$ms = round(microtime(true) - $this->queryStartTime, 4) * 1000;
$this->queryExecutionTimes[] = $ms;
$this->totalExecutionTime += $ms;
}
/**
* Clean all collected data
*
* @return QueryAnalyzer
*/
public function cleanUp()
{
$this->queries = array();
$this->queryExecutionTimes = array();
$this->totalExecutionTime = 0;
return $this;
}
/**
* Dump the statistics of executed queries
*
* @param boolean $dumpOnlySql
* @return void
*/
public function getOutput($dumpOnlySql = false)
{
$output = '';
if (!$dumpOnlySql) {
$output .= 'Platform: ' . $this->platform->getName() . PHP_EOL;
$output .= 'Executed queries: ' . count($this->queries) . ', total time: ' . $this->totalExecutionTime . ' ms' . PHP_EOL;
}
foreach ($this->queries as $index => $sql) {
if (!$dumpOnlySql) {
$output .= 'Query(' . ($index+1) . ') - ' . $this->queryExecutionTimes[$index] . ' ms' . PHP_EOL;
}
$output .= $sql . ';' . PHP_EOL;
}
$output .= PHP_EOL;
return $output;
}
/**
* Index of the slowest query executed
*
* @return integer
*/
public function getSlowestQueryIndex()
{
$index = 0;
$slowest = 0;
foreach ($this->queryExecutionTimes as $i => $time) {
if ($time > $slowest) {
$slowest = $time;
$index = $i;
}
}
return $index;
}
/**
* Get total execution time of queries
*
* @return integer
*/
public function getTotalExecutionTime()
{
return $this->totalExecutionTime;
}
/**
* Get all queries
*
* @return array
*/
public function getExecutedQueries()
{
return $this->queries;
}
/**
* Get number of executed queries
*
* @return integer
*/
public function getNumExecutedQueries()
{
return count($this->queries);
}
/**
* Get all query execution times
*
* @return array
*/
public function getExecutionTimes()
{
return $this->queryExecutionTimes;
}
/**
* Create the SQL with mapped parameters
*
* @param string $sql
* @param array $params
* @param array $types
* @return sql
*/
private function generateSql($sql, $params, $types)
{
if (!count($params)) {
return $sql;
}
$converted = $this->getConvertedParams($params, $types);
if (is_int(key($params))) {
$index = key($converted);
$sql = preg_replace_callback('@\?@sm', function($match) use (&$index, $converted) {
return implode(' ', $converted[$index++]);
}, $sql);
} else {
foreach ($converted as $key => $value) {
$sql = str_replace(':' . $key, $value, $sql);
}
}
return $sql;
}
/**
* Get the converted parameter list
*
* @param array $params
* @param array $types
* @return array
*/
private function getConvertedParams($params, $types)
{
$result = array();
foreach ($params as $position => $value) {
if (isset($types[$position])) {
$type = $types[$position];
if (is_string($type)) {
$type = Type::getType($type);
}
if ($type instanceof Type) {
$value = $type->convertToDatabaseValue($value, $this->platform);
}
} else {
if (is_object($value) && $value instanceof \DateTime) {
$value = $value->format($this->platform->getDateTimeFormatString());
} elseif (!is_null($value)) {
$type = Type::getType(gettype($value));
$value = $type->convertToDatabaseValue($value, $this->platform);
}
}
if (is_string($value)) {
$value = "'{$value}'";
} elseif (is_null($value)) {
$value = 'NULL';
}
$result[$position] = $value;
}
return $result;
}
}

View File

@@ -0,0 +1,9 @@
<?php
require __DIR__.'/../vendor/autoload.php';
\Doctrine\Common\Annotations\AnnotationRegistry::registerLoader('class_exists');
$reader = new \Doctrine\Common\Annotations\AnnotationReader();
$reader = new \Doctrine\Common\Annotations\CachedReader($reader, new \Doctrine\Common\Cache\ArrayCache());
$_ENV['annotation_reader'] = $reader;

View File