Skip to content

Commit

Permalink
v4 (#314)
Browse files Browse the repository at this point in the history
  • Loading branch information
garak authored Sep 17, 2022
1 parent 72669ea commit 0b19aa0
Show file tree
Hide file tree
Showing 46 changed files with 400 additions and 286 deletions.
4 changes: 1 addition & 3 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,8 @@ jobs:
matrix:
include:
- description: 'Lowest'
php: '7.4'
composer_option: '--prefer-lowest'
- description: '8.0'
php: '8.0'
composer_option: '--prefer-lowest'
- description: '8.1'
php: '8.1'
name: PHP ${{ matrix.php }} tests (${{ matrix.description }})
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## 4.0.0

*Unreleased*

* Paginator constructor now accepts as second argument an instance of ArgumentAccessInterface, instead of
a RequestStack. So you can now paginate outside the web context

## 3.6.0

*Released at 2022-08-18*
Expand Down
21 changes: 11 additions & 10 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,22 @@
}
],
"require": {
"php": "^7.4 || ^8.0",
"symfony/event-dispatcher-contracts": "^2.0 || ^3.0",
"symfony/http-foundation": "^4.4 || ^5.4 || ^6.0"
"php": "^8.0",
"symfony/event-dispatcher-contracts": "^3.0"
},
"require-dev": {
"ext-pdo_sqlite": "*",
"doctrine/mongodb-odm": "^2.4",
"doctrine/orm": "^2.11",
"doctrine/phpcr-odm": "^1.2",
"jackalope/jackalope-doctrine-dbal": "^1.2",
"doctrine/orm": "^2.12",
"doctrine/phpcr-odm": "^1.6",
"jackalope/jackalope-doctrine-dbal": "^1.8",
"phpunit/phpunit": "^9.5",
"propel/propel1": "^1.7",
"ruflin/elastica": "^7.0",
"solarium/solarium": "^6.0",
"symfony/http-kernel": "^4.4 || ^5.4 || ^6.0",
"symfony/property-access": "^4.4 || ^5.4 || ^6.0"
"symfony/http-foundation": "^5.4 || ^6.0",
"symfony/http-kernel": "^5.4 || ^6.0",
"symfony/property-access": "^5.4 || ^6.0"
},
"suggest": {
"doctrine/common": "to allow usage pagination with Doctrine ArrayCollection",
Expand All @@ -47,14 +47,15 @@
"propel/propel1": "to allow usage pagination with Propel ORM",
"ruflin/elastica": "to allow usage pagination with ElasticSearch Client",
"solarium/solarium": "to allow usage pagination with Solarium Client",
"symfony/property-access": "To allow sorting arrays"
"symfony/http-foundation": "to retrieve arguments from Request",
"symfony/property-access": "to allow sorting arrays"
},
"conflict": {
"doctrine/dbal": "<2.10"
},
"extra": {
"branch-alias": {
"dev-master": "3.x-dev"
"dev-master": "4.x-dev"
}
},
"autoload": {
Expand Down
14 changes: 4 additions & 10 deletions docs/pager/config.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
## 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 |
Expand All @@ -32,7 +30,6 @@ The list of existing options are:

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.
Expand All @@ -42,22 +39,19 @@ If you want to order your results by a column from a fetch joined t-many associa
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*.

> **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.


### `pageOutOfRange`

Defines behavior if page number is out of range (i.g. exceeds the last page number):
* 'ignore' - do nothing;
* 'fix' - replace page number with max page;
* 'throwException' - throw PageNumberOutOfRangeException (if you want to handle this case in the app).
* 'ignore' - do nothing;
* 'fix' - replace page number with max page;
* 'throwException' - throw PageNumberOutOfRangeException (if you want to handle this case in the app).
14 changes: 8 additions & 6 deletions docs/pager/intro.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
# Intro to Knp Pager Component

This is a PHP 7 paginator with a totally different core concept.

**Note:** it is still experimental, any ideas on structural design are very welcome
This is a PHP 8 paginator with a totally different core concept.

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
Expand All @@ -17,6 +15,7 @@ Why reinvent the wheel? Can someone tell me what's the definition of **wheel** i
## Requirements:

- Symfony EventDispatcher component
- Symfony HttpFoundation component (optional, if you want to retrieve data from Symfony Request)
- Namespace based autoloader for this library

## Features:
Expand All @@ -36,7 +35,10 @@ pagination view - for representation purposes.
### Controller

```php
$paginator = new Knp\Component\Pager\Paginator;
// see usage.md for full vars
$dispatcher = '[..]';
$accessor = '[..]';
$paginator = new Knp\Component\Pager\Paginator($dispatcher, $accessor);
$target = range('a', 'u');
// uses event subscribers to paginate $target
$pagination = $paginator->paginate($target, 2/*page*/, 10/*limit*/);
Expand All @@ -49,7 +51,7 @@ echo $pagination; // renders pagination

// overriding view rendering

$pagination->renderer = function($data) use ($template) {
$pagination->renderer = function ($data) use ($template) {
return $twig->render($template, $data);
};

Expand All @@ -68,7 +70,7 @@ $pagination = $paginator->paginate($em->createQuery('SELECT a FROM Entity\Articl
### Custom data repository pagination

For applications having custom data repositories (like DDD repositories, CQRS read models) you can provide custom
data rerieval inside callbacks.
data retrieval inside callbacks.

```php
$repository = ...;
Expand Down
15 changes: 13 additions & 2 deletions docs/pager/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,24 @@ require 'vendor/autoload.php';

// usage examples will continue here

use Knp\Component\Pager\Paginator; // used class name
use Knp\Component\Pager\Paginator;
use Knp\Component\Pager\ArgumentAccess\RequestArgumentAccess;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\HttpFoundation\RequestStack;

// 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
$dispatcher = new EventDispatcher();
$dispatcher->addSubscriber(new PaginationSubscriber());
$dispatcher->addSubscriber(new SortableSubscriber());

// here we're using the the default access to arguments, based on Symfony Request
// you can use your preferred way to retrieve them, by implementing RequestArgumentAccessInterface
$accessor = new RequestArgumentAccess(RequestStack::createFromGlobals());

$paginator = new Paginator($dispatcher, $accessor);
$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*/);
Expand Down
12 changes: 12 additions & 0 deletions src/Knp/Component/Pager/ArgumentAccess/ArgumentAccessInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

namespace Knp\Component\Pager\ArgumentAccess;

interface ArgumentAccessInterface
{
public function has(string $name): bool;

public function get(string $name): string|int|float|bool|null;

public function set(string $name, string|int|float|bool|null $value): void;
}
40 changes: 40 additions & 0 deletions src/Knp/Component/Pager/ArgumentAccess/RequestArgumentAccess.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

namespace Knp\Component\Pager\ArgumentAccess;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;

final class RequestArgumentAccess implements ArgumentAccessInterface
{
private RequestStack $requestStack;

public function __construct(RequestStack $requestStack)
{
$this->requestStack = $requestStack;
}

public function has(string $name): bool
{
return $this->getRequest()->query->has($name);
}

public function get(string $name): string|int|float|bool|null
{
return $this->getRequest()->query->get($name);
}

public function set(string $name, string|int|float|bool|null $value): void
{
$this->getRequest()->query->set($name, $value);
}

private function getRequest(): Request
{
if (null === $request = $this->requestStack->getCurrentRequest()) {
$request = Request::createFromGlobals();
}

return $request;
}
}
12 changes: 6 additions & 6 deletions src/Knp/Component/Pager/Event/BeforeEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Knp\Component\Pager\Event;

use Symfony\Component\HttpFoundation\Request;
use Knp\Component\Pager\ArgumentAccess\ArgumentAccessInterface;
use Symfony\Contracts\EventDispatcher\Event;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;

Expand All @@ -13,21 +13,21 @@ final class BeforeEvent extends Event
{
private EventDispatcherInterface $eventDispatcher;

private ?Request $request;
private ArgumentAccessInterface $argumentAccess;

public function __construct(EventDispatcherInterface $eventDispatcher, ?Request $request)
public function __construct(EventDispatcherInterface $eventDispatcher, ArgumentAccessInterface $argumentAccess)
{
$this->eventDispatcher = $eventDispatcher;
$this->request = $request;
$this->argumentAccess = $argumentAccess;
}

public function getEventDispatcher(): EventDispatcherInterface
{
return $this->eventDispatcher;
}

public function getRequest(): Request
public function getArgumentAccess(): ArgumentAccessInterface
{
return $this->request ?? Request::createFromGlobals();
return $this->argumentAccess;
}
}
12 changes: 4 additions & 8 deletions src/Knp/Component/Pager/Event/ItemsEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,8 @@ final class ItemsEvent extends Event
{
/**
* A target being paginated
*
* @var mixed
*/
public $target;
public mixed $target = null;

/**
* List of options
Expand All @@ -23,10 +21,8 @@ final class ItemsEvent extends Event

/**
* Items result
*
* @var mixed
*/
public $items;
public mixed $items = null;

/**
* Count result
Expand All @@ -43,7 +39,7 @@ public function __construct(int $offset, int $limit)
$this->limit = $limit;
}

public function setCustomPaginationParameter($name, $value): void
public function setCustomPaginationParameter(string $name, mixed $value): void
{
$this->customPaginationParams[$name] = $value;
}
Expand All @@ -53,7 +49,7 @@ public function getCustomPaginationParameters(): array
return $this->customPaginationParams;
}

public function unsetCustomPaginationParameter($name): void
public function unsetCustomPaginationParameter(string $name): void
{
if (isset($this->customPaginationParams[$name])) {
unset($this->customPaginationParams[$name]);
Expand Down
4 changes: 1 addition & 3 deletions src/Knp/Component/Pager/Event/PaginationEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,8 @@ final class PaginationEvent extends Event
{
/**
* A target being paginated
*
* @var mixed
*/
public $target;
public mixed $target = null;

/**
* List of options
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
namespace Knp\Component\Pager\Event\Subscriber\Filtration\Doctrine\ORM\Query;

use Doctrine\DBAL\Types\Types as Type;
use Doctrine\ORM\Query\AST\Functions\LowerFunction;
use Doctrine\ORM\Query\AST\ComparisonExpression;
use Doctrine\ORM\Query\AST\ConditionalExpression;
use Doctrine\ORM\Query\AST\ConditionalFactor;
use Doctrine\ORM\Query\AST\ConditionalPrimary;
use Doctrine\ORM\Query\AST\ConditionalTerm;
use Doctrine\ORM\Query\AST\Functions\LowerFunction;
use Doctrine\ORM\Query\AST\LikeExpression;
use Doctrine\ORM\Query\AST\Literal;
use Doctrine\ORM\Query\AST\Node;
Expand All @@ -34,18 +34,15 @@ class WhereWalker extends TreeWalkerAdapter
public const HINT_PAGINATOR_FILTER_VALUE = 'knp_paginator.filter.value';

/**
* Filter strings in a case insensitive way
* Filter strings in a case-insensitive way
*/
const HINT_PAGINATOR_FILTER_CASE_INSENSITIVE = 'knp_paginator.filter.case_insensitive';
public const HINT_PAGINATOR_FILTER_CASE_INSENSITIVE = 'knp_paginator.filter.case_insensitive';

/**
* Walks down a SelectStatement AST node, modifying it to
* filter the query like requested by url
*
* @param SelectStatement $AST
* @return void|string
*/
public function walkSelectStatement(SelectStatement $AST)
public function walkSelectStatement(SelectStatement $AST): void
{
$query = $this->_getQuery();
$queriedValue = $query->getHint(self::HINT_PAGINATOR_FILTER_VALUE);
Expand Down Expand Up @@ -96,7 +93,8 @@ public function walkSelectStatement(SelectStatement $AST)
Type::BIGINT,
Type::FLOAT,
Type::DECIMAL,
]
],
true
)
)
) {
Expand Down
Loading

0 comments on commit 0b19aa0

Please sign in to comment.