Skip to content

Commit

Permalink
Merge branch 'master' into sensorario-naming-convention
Browse files Browse the repository at this point in the history
  • Loading branch information
sensorario committed May 31, 2018
2 parents 5212cee + 0b25d21 commit 6cbb821
Show file tree
Hide file tree
Showing 19 changed files with 1,024 additions and 299 deletions.
5 changes: 3 additions & 2 deletions .github/CONTRIBUTING.md
Expand Up @@ -40,12 +40,13 @@ work.

* if it contains new features must be opened against master branch;


Coding Standards
----------------

* every time new class or method is added, `@since` annotation should be
present. Just mark the minor version. `/** @since version x.y */`;

* always use yoda condition;
* always use `yoda` condition;

* respect PSR-2 coding standards;
* respect `PSR-2` coding standards;
15 changes: 9 additions & 6 deletions CHANGELOG-2.2.md
Expand Up @@ -4,9 +4,12 @@ CHANGELOG for 2.2
This changelog references the relevant changes (bug and security fixes) done
in 2.2 minor versions.

- resolve #79 - add new [FilterObject] component
- fix #77 - undefined index list
- fix #82 - anonymous method `setFilters` to more explicit `setAndFilters`
- fix #95 - remove `Mado\QueryBundle\Queries\Objects\Operator` statement
- fix #96 - remove variable assignment 'cause its unused
- fix #114 - convert negative limit to PHP_INT_MAX
- add new [FilterObject] component
- undefined index list
- anonymous method `setFilters` to more explicit `setAndFilters`
- remove `Mado\QueryBundle\Queries\Objects\Operator` statement
- remove variable assignment 'cause its unused
- convert negative limit to PHP_INT_MAX
- add [Services\FilterExtractor] extract additional filters from AdditionalFilterable
- add [Objects\Filter] to read filter value
- add [AdditionalFilterable] interface
131 changes: 131 additions & 0 deletions doc/additional-filters.md
@@ -0,0 +1,131 @@
# Additional Filters

Aims to limit information in response, based on predefined rules. Those rule
must be stored inside your application's user.

## User

User must respect `\Mado\QueryBundle\Interfaces\AdditionalFilterable` interface
and implement methods `getAdditionalFilters()`.

## Custom Filter

To work with additional filters, is necessary a custom filter. This should
respect the interface `Mado\QueryBundle\Interfaces\CustomFilter`.

### Example

You can create your custom filter using this syntax.

```php
<?php

namespace Mt\AdditionalFilters;

class YourCustomFilter implements CustomFilter
{
private static $filterMap = [
'filter_name' => \Bundle\To\EntityClass::class,
];

public function __construct(
EntityManagerInterface $manager,
GraphWalker $dijkstraWalker,
RequestStack $requestStack,
LoggerInterface $logger
) {
$this->manager = $manager;
$this->dijkstraWalker = $dijkstraWalker;
$this->requestStack = $requestStack;
$this->logger = $logger;
}

public function setUser(AdditionalFilterable $user)
{
$this->additionalFilters = AdditionalFilterExtractor::fromUser($user);
return $this;
}

public function allItemsTo(string $entity)
{
$this->entity = $entity;
$filters = [];
$translations = [
'filter_name' => [
'from' => '_embedded.shops.id',
'to' => 'id',
],
];
foreach ($translations as $filterName => $filterTranslation) {
if ($this->additionalFilters->getFilters($filterName) != '') {
$path = $this->getPathTo($filterName);
$genericAdditionalFilter = Filter::box([
'ids' => $this->additionalFilters->getFilters($filterName),
'path' => $path,
]);
$filterKey = $genericAdditionalFilter->getFieldAndOperator();
if ([] != $filterTranslation) {
if ($filterKey == $filterTranslation['from'] . '|' . $genericAdditionalFilter->getOperator()) {
$filterKey = $filterTranslation['to'] . '|' . $genericAdditionalFilter->getOperator();
$genericAdditionalFilter = $genericAdditionalFilter->withFullPath($filterKey);
}
}
$filtering = $this->requestStack->getCurrentRequest()->query->get('filtering', []);
$haveCheckedAdditionalFilters = false;
$field = $genericAdditionalFilter->getField();
foreach( $filtering as $filterKey => $value) {
$genericQueryStringFilter = Filter::fromQueryStringFilter([
$filterKey => $value
]);
if ($genericAdditionalFilter->getField() == $genericQueryStringFilter->getField()) {
if (
$genericAdditionalFilter->getOperator() == 'list'
&& $genericAdditionalFilter->getOperator() == $genericQueryStringFilter->getOperator()
) {
$haveCheckedAdditionalFilters = true;
$additionalFiltersIds = explode(',', $genericAdditionalFilter->getIds());
$querystringIds = explode(',', $genericQueryStringFilter->getIds());
$intersection = array_intersect($querystringIds, $additionalFiltersIds);
$ids = join(',', $intersection);
if ($intersection == []) {
throw new ForbiddenContentException(
'Oops! Forbidden requested id ' . $value
. ' is not available. Available are '
. $genericAdditionalFilter->getIds()
);
}
$filters[$genericAdditionalFilter->getFieldAndOperator()] = $ids;
}
}
}
if (!$haveCheckedAdditionalFilters) {
$ids = $genericAdditionalFilter->getIds();
$filters[$genericAdditionalFilter->getFieldAndOperator()] = $ids;
}
}
}

return $filters;
}

public function getPathTo(string $filter)
{
$this->dijkstraWalker->buildPathBetween(
$this->entity,
self::getEntityFromFilter($filter)
);
return $this->dijkstraWalker->getPath();
}

public static function getEntityFromFilter(string $filterName)
{
return self::$filterMap[$filterName];
}

public function setEntity(string $entity)
{
$this->entity = $entity;
return $this;
}
}
```
1 change: 1 addition & 0 deletions phpunit.xml
Expand Up @@ -13,6 +13,7 @@
<directory suffix=".php">src/Mado/QueryBundle/Component</directory>
<directory suffix=".php">src/Mado/QueryBundle/Dictionary</directory>
<directory suffix=".php">src/Mado/QueryBundle/Queries</directory>
<directory suffix=".php">src/Mado/QueryBundle/Objects</directory>
<directory suffix=".php">src/Mado/QueryBundle/Repositories</directory>
<directory suffix=".php">src/Mado/QueryBundle/Resources</directory>
<directory suffix=".php">src/Mado/QueryBundle/Services</directory>
Expand Down
7 changes: 7 additions & 0 deletions src/Mado/QueryBundle/Exceptions/ForbiddenContentException.php
@@ -0,0 +1,7 @@
<?php

namespace Mado\QueryBundle\Exceptions;

final class ForbiddenContentException extends \Exception
{
}
9 changes: 9 additions & 0 deletions src/Mado/QueryBundle/Exceptions/InvalidFiltersException.php
@@ -0,0 +1,9 @@
<?php

namespace Mado\QueryBundle\Exceptions;

use Exception;

final class InvalidFiltersException extends Exception
{
}
8 changes: 8 additions & 0 deletions src/Mado/QueryBundle/Interfaces/AdditionalFilterable.php
@@ -0,0 +1,8 @@
<?php

namespace Mado\QueryBundle\Interfaces;

interface AdditionalFilterable
{
public function getAdditionalFilters();
}
28 changes: 28 additions & 0 deletions src/Mado/QueryBundle/Interfaces/CustomFilter.php
@@ -0,0 +1,28 @@
<?php

namespace Mado\QueryBundle\Interfaces;

use Doctrine\ORM\EntityManagerInterface;
use Mado\QueryBundle\Component\Meta\GraphWalker;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\RequestStack;

interface CustomFilter
{
public function __construct(
EntityManagerInterface $manager,
GraphWalker $walter,
RequestStack $requestStack,
LoggerInterface $logger
);

public function setUser(AdditionalFilterable $user);

public function allItemsTo(string $entity);

public function getPathTo(string $fullyQualifiedClassName);

public static function getEntityFromFilter(string $filterName);

public function setEntity(string $entity);
}
116 changes: 116 additions & 0 deletions src/Mado/QueryBundle/Objects/Filter.php
@@ -0,0 +1,116 @@
<?php

namespace Mado\QueryBundle\Objects;

class Filter
{
private $rawFilter;

private $ids;

private $operator;

public static function box(array $params)
{
$rawIds = $params['ids'];
$path = $params['path'];

$operator = key($rawIds);
$ids = join(',', current($rawIds));

return new self([
'raw_filter' => self::buildRawFilter($path, $operator),
'ids' => $ids,
'operator' => $operator,
'path' => $path,
]);
}

private static function buildRawFilter($path, $operator)
{
return $path . '.id|' . $operator;
}

private function __construct(array $params)
{
$this->rawFilter = $params['raw_filter'];
$this->ids = $params['ids'];
$this->operator = $params['operator'];
$this->path = $params['path'];
}

public function getFieldAndOperator()
{
return $this->rawFilter;
}

public function getIds()
{
return $this->ids;
}

public function getOperator()
{
return $this->operator;
}

public function getPath()
{
return $this->path;
}

public function withPath($path)
{
$rawFilter = self::buildRawFilter($path, $this->operator);

if ($path == '') {
$rawFilter = str_replace('.', '', $rawFilter);
}

return new self([
'raw_filter' => $rawFilter,
'ids' => $this->ids,
'operator' => $this->operator,
'path' => $path,
]);
}

public function withFullPath($path)
{
$explodedPath = explode('|', $path);

$path = $explodedPath[0];
$operator = $explodedPath[1];

return new self([
'raw_filter' => join('|', $explodedPath),
'ids' => $this->ids,
'operator' => $operator,
'path' => $path,
]);
}

public function getField()
{
$explodedPath = explode('|', $this->getFieldAndOperator());

$field = $explodedPath[0];

return $field;
}

public static function fromQueryStringFilter(array $params)
{
return new self([
'raw_filter' => key($params),
'ids' => current($params),
'operator' => explode('|', key($params))[1],
'path' => null,
]);
}

public function getValue()
{
return $this->ids;
}
}
6 changes: 6 additions & 0 deletions src/Mado/QueryBundle/Queries/AbstractQuery.php
Expand Up @@ -19,6 +19,8 @@ abstract class AbstractQuery

protected $qBuilder;

protected $joinFactory;

public function __construct(EntityManager $manager)
{
$this->manager = $manager;
Expand All @@ -36,6 +38,8 @@ public function createSelectAndGroupBy($entityName, $alias, $groupByField)
$this->qBuilder = $this->manager->createQueryBuilder()
->select($select)
->groupBy($groupBy);

$this->joinFactory = new Join($this->getEntityName(), $this->entityAlias, $this->manager);
}

public function createQueryBuilder($entityName, $alias)
Expand All @@ -46,6 +50,8 @@ public function createQueryBuilder($entityName, $alias)
$this->qBuilder = $this->manager->createQueryBuilder()
->select($alias)
->from($this->entityName, $alias);

$this->joinFactory = new Join($this->getEntityName(), $this->entityAlias, $this->manager);
}

public function getEntityName()
Expand Down

0 comments on commit 6cbb821

Please sign in to comment.