Skip to content

Commit

Permalink
Collection improvements (#94)
Browse files Browse the repository at this point in the history
* Added common AbstractCollection

* CR Fixes

* Migrating ResultCollection

* Improvements in chain searching

* CellCollection introduced

* Added missing PHPDocs

* Added PHPDoc and documentation

* Composer improvements

* Documentation typo fix
  • Loading branch information
krzysztof-gzocha authored Sep 8, 2016
1 parent 6fb88f4 commit 71618b9
Show file tree
Hide file tree
Showing 45 changed files with 573 additions and 337 deletions.
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"name": "krzysztof-gzocha/searcher",
"description": "Searcher is a framework-agnostic search query builder. Search queries are written using Criterias and can be run against MySQL, MongoDB or even files.",
"description": "Searcher is a framework-agnostic search query builder. Search queries are written using Criterias and can be run against MySQL, MongoDB, ElasticSearch or even files.",
"keywords": ["search", "searcher", "agnostic", "query", "builder", "elastic", "mongo", "doctrine", "criteria"],
"license": "MIT",
"type": "library",
"homepage": "http://searcher.rtfd.io",
Expand Down
18 changes: 12 additions & 6 deletions docs/chain-search.rst
Original file line number Diff line number Diff line change
Expand Up @@ -85,18 +85,19 @@ Now we are ready and we can create an instance of ``ChainSearch`` and populate i

.. code:: php
$cells = [
new Cell(
$cells = new CellCollection([
// Optionally you can specify a name for ease of fetching sub-results
'users' => new Cell(
$userSearcher,
new Transformer(),
'users' // Just an optional name
),
new Cell(
'statistics' => new Cell(
$statisticSearcher,
new EndTransformer(), // We don't want to go further
'statistics'
),
];
]);
$chainSearch = new ChainSearch($cells);
$results = $chainSearch->search($entryCriteria);
Expand All @@ -109,3 +110,8 @@ Now, the variable ``$results`` will hold a ``ResultCollection`` with two element
'users' => [/** results from $userSearcher **/],
'statistics' => [/** results from $statisticSearcher **/],
]
.. warning::

When trying to use CellCollection will less than 2 elements InvalidArgumentException will be thrown, because
there is no sense in using chain search with just 1 cell.
2 changes: 1 addition & 1 deletion docs/introduction.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Searcher
What?
-----------------
*Searcher* is a framework-agnostic search query builder.
Search queries are written using Criterias and can be run againstMySQL, MongoDB or even files.
Search queries are written using Criterias and can be run against MySQL, MongoDB or even files.
Supported PHP versions: >=5.4, 7 and HHVM.
You can find searcher in two most important places:

Expand Down
136 changes: 136 additions & 0 deletions src/KGzocha/Searcher/AbstractCollection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
<?php

namespace KGzocha\Searcher;

/**
* @author Krzysztof Gzocha <[email protected]>
*/
abstract class AbstractCollection implements \Countable, \IteratorAggregate
{
/**
* @var array
*/
private $items;

/**
* @param \Traversable|array $items
*/
public function __construct($items = [])
{
$this->items = [];
$this->isTraversable($items);
$this->checkItems($items);

$this->items = $items;
}

/**
* @param object $item
*
* @return bool
*/
abstract protected function isItemValid($item);

/**
* {@inheritdoc}
*/
public function getIterator()
{
return new \ArrayIterator($this->items);
}

/**
* {@inheritdoc}
*/
public function count()
{
return count($this->items);
}

/**
* @param mixed $item
*
* @return $this
*/
protected function addItem($item)
{
$this->items[] = $item;

return $this;
}

/**
* @param string $name
* @param mixed $item
*
* @return $this
*/
protected function addNamedItem($name, $item)
{
$this->items[$name] = $item;

return $this;
}

/**
* @return array
*/
protected function getItems()
{
return $this->items;
}

/**
* @param string $name
*
* @return mixed|null
*/
protected function getNamedItem($name)
{
if (array_key_exists($name, $this->items)) {
return $this->items[$name];
}

return null;
}

/**
* @param mixed $items
*
* @throws \InvalidArgumentException
*/
private function isTraversable($items)
{
if (is_array($items)) {
return;
}

if ($items instanceof \Traversable) {
return;
}

throw new \InvalidArgumentException(sprintf(
'Argument passed to collection %s needs to be an array or traversable object',
get_class($this)
));
}

/**
* @param array|\Traversable $items
*
* @throws \InvalidArgumentException
*/
private function checkItems($items)
{
foreach ($items as $item) {
if ($this->isItemValid($item)) {
continue;
}

throw new \InvalidArgumentException(sprintf(
'At least one item in collection "%s" is invalid.',
get_class($this)
));
}
}
}
18 changes: 1 addition & 17 deletions src/KGzocha/Searcher/Chain/Cell.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,16 @@ class Cell implements CellInterface
*/
private $transformer;

/**
* @var string|null
*/
private $name;

/**
* @param SearcherInterface $searcher
* @param TransformerInterface $transformer
* @param string $name
*/
public function __construct(
SearcherInterface $searcher,
TransformerInterface $transformer,
$name = null
TransformerInterface $transformer
) {
$this->searcher = $searcher;
$this->transformer = $transformer;
$this->name = $name;
}

/**
Expand All @@ -59,14 +51,6 @@ public function getTransformer()
return $this->transformer;
}

/**
* @return null|string
*/
public function getName()
{
return $this->name;
}

/**
* @return bool
*/
Expand Down
5 changes: 0 additions & 5 deletions src/KGzocha/Searcher/Chain/CellInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,6 @@ public function getSearcher();
*/
public function getTransformer();

/**
* @return null|string
*/
public function getName();

/**
* @return bool
*/
Expand Down
79 changes: 35 additions & 44 deletions src/KGzocha/Searcher/Chain/ChainSearch.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace KGzocha\Searcher\Chain;

use KGzocha\Searcher\Chain\Collection\CellCollectionInterface;
use KGzocha\Searcher\Criteria\Collection\CriteriaCollectionInterface;
use KGzocha\Searcher\Result\ResultCollection;
use KGzocha\Searcher\SearcherInterface;
Expand All @@ -16,20 +17,17 @@
*/
class ChainSearch implements SearcherInterface
{
const MINIMUM_CELLS = 2;

/**
* @var CellInterface[]
* @var CellCollectionInterface
*/
private $cells;
private $cellCollection;

/**
* @param CellInterface[] $cells
* @param CellCollectionInterface $cellCollection
*/
public function __construct(array $cells)
public function __construct(CellCollectionInterface $cellCollection)
{
$this->validateCells($cells);
$this->cells = $cells;
$this->cellCollection = $cellCollection;
}

/**
Expand All @@ -45,57 +43,50 @@ public function __construct(array $cells)
public function search(
CriteriaCollectionInterface $criteriaCollection
) {
$previousCriteria = null;
$previousCriteria = $criteriaCollection;
$previousResults = null;
$resultsArray = [];
$result = new ResultCollection();

foreach ($this->cells as $cell) {
/** @var CellInterface $cell */
foreach ($this->cellCollection as $name => $cell) {
if ($cell->getTransformer()->skip($previousResults)) {
continue;
}

// Assumed only for first iteration
if (!$previousCriteria) {
$previousCriteria = $criteriaCollection;
}

$previousResults = $cell->getSearcher()->search($previousCriteria);
if ($cell->hasTransformer()) {
$previousCriteria = $cell->getTransformer()->transform($previousResults, $previousCriteria);
}

if ($cell->getName()) {
$resultsArray[$cell->getName()] = $previousResults;
continue;
}
$previousCriteria = $this->getNewCriteria(
$cell,
$previousCriteria,
$previousResults
);

array_push($resultsArray, $previousResults);
$result->addNamedItem($name, $previousResults);
}

return new ResultCollection($resultsArray);
return $result;
}

/**
* @param CellInterface[] $cells
* @throws \InvalidArgumentException
* If $cell has transformer then it will be used to return new criteria.
* If not old criteria will be returned.
*
* @param CellInterface $cell
* @param CriteriaCollectionInterface $criteria
* @param mixed $results
*
* @return CriteriaCollectionInterface
*/
private function validateCells(array $cells)
{
if (self::MINIMUM_CELLS > count($cells)) {
throw new \InvalidArgumentException(
'At least two searchers are required to create a chain'
);
private function getNewCriteria(
CellInterface $cell,
CriteriaCollectionInterface $criteria,
$results
) {
if (!$cell->hasTransformer()) {
return $criteria;
}

foreach ($cells as $cell) {
if (is_object($cell) && $cell instanceof CellInterface) {
continue;
}

throw new \InvalidArgumentException(sprintf(
'All cells passed to %s should be object and must implement CellInterface',
__CLASS__
));
}
return $cell
->getTransformer()
->transform($results, $criteria);
}
}
Loading

0 comments on commit 71618b9

Please sign in to comment.