Skip to content
This repository has been archived by the owner on Jun 11, 2019. It is now read-only.

Commit

Permalink
Control files for GIT and composer plus removal of run-tests which do…
Browse files Browse the repository at this point in the history
…esn't belong to the package
  • Loading branch information
crazyone-crazycoders committed Jul 27, 2013
1 parent c707de4 commit 544de00
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 164 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
.htaccess
error_log
sftp-config*.json
vendor
*.phar
*.phar
run-documentation
run-tests
212 changes: 58 additions & 154 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,190 +1,94 @@
CrazyCodr/Data/Filter
=====================

This package contains facilities to easily filter data from any enumerable source.
This package contains facilities to easily filter data from any enumerable source.

This class features, for now, a single iterator that you can use to filter out data as you iterate it. It offers slightly more complex features that the ones of a SPL FilterIterator in a sense that you can manage multiple filters at once, clear the lot, dynamically add new conditions to it as you go, etc.
This class features a single filter iterator accompagnied by filter group and a closure filter adapter that you can use to filter out data as you iterate it. It offers slightly more complex features that the ones of a SPL FilterIterator in a sense that you can manage multiple filters at once, clear the lot, dynamically add new conditions to it as you go, etc.

Quick documentation
-------------------
Why should i use it over SPL FilterIterator
-------------------------------------------

1. The filter class is an iterator and filters out data live as you iterate.
2. You can change the filter conditions live since it doesn't pre-process the inner iterator
3. Calling `where()` allows you to add conditions to the filter class, you can chain `where()` as long as you want
4. You can clear all filters by calling `clearFilters()` whenever you want
5. The main filter object has a type : ANY or ALL stating if all filter conditions must be met or just any
6. You can create groups of filters and assign ANY or ALL
1. Every class is designed to be extended to create concrete filtering classes which makes for better TDD
2. The iterator can be changed live (add/remove conditions/groups) to change the behavior of the iterator
3. You can use your filters outside the scope of an iteration using $filter->shouldKeep();

Whats next
How to use
----------

1. Rework the code for version 2, will probably not be entirely compatible with version 1. Support for object oriented filterConditions and filterConditionGroups
2. Add turnOn/turnOff possibilities on each filter and filterGroup

Examples
--------
1. Create a datasource that can be iterated
2. Create ClosureFilter objects that will execute what you want to filter out
3. Create a FilterGroup and add all filters to it
4. Create a FilterIterator and add the datasource and filtercontainer to it
5. Iterate, rinse, repeat...

**Using prepared conditions just in time**
Whats next for you?
-------------------

The fun aspect of this class is that you can prepare filtering closures in advance, they could be simple closures in another file that you test using unit tests. In this case, we prepare those closures in advance and add the right closures to the filtering process.
1. Download the package through composer "crazycodr/data-filter" and then look in the documentation directory of the package to know more about it, it's actually quite simple but so powerful
2. Look at the example
3. Try and build some tests using real life data such as CSV files
4. Use in production

```PHP
A few quick examples
--------------------

//Setup the filters
$filterOutToyota = function($data, $key){ return $data->make != 'Toyota'; }
$filterOutFord = function($data, $key){ return $data->make != 'Ford'; }
$filterOutSUV = function($data, $key){ return $data->make != 'suv'; }
$filterOutSubCompacts = function($data, $key){ return $data->type != 'subcompact'; }

//Setup the data
$data = <...>

//Setup the filtering
$filteredData = new \CrazyCodr\Data\Filter($data);
if($_GET['filters']['make']['toyota'] == 1){ $filteredData->where($filterOutToyota); }
if($_GET['filters']['make']['ford'] == 1){ $filteredData->where($filterOutToyota); }

//Loop
foreach($filteredData as $make)
{
<...>
}

```
**Basic setup for all examples below**

**Chaining support**

CrazyCodr Data Filter supports chaining of filter functions so you can skip repeating over and over again the original object that you called when creating a filter.
This code here will be used in all examples, paste it in front of each example, it will save use some display space.

```PHP
//You can chain the data filtering to create many conditions
$data = <...>;
$filteredData = new \CrazyCodr\Data\Filter($data);
$filteredData
->where(function($data, $key){ return $key != 'world'; })
->where(function($data, $key){ return $key != 'canada'; })
->where(function($data, $key){ return $data->user != 'crazycone'; });
include('../vendor/autoload.php');
use \CrazyCodr\Data\Filter as cdf;

//Setup sample data to work with
$data = array(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20);

//Create your filters, alternatively, it is better to create concrete classes, it becomes much more testable
$oddNumbers = new cdf\ClosureFilter(function($data){ return ($data % 2) == 1; });
$evenNumbers = new cdf\ClosureFilter(function($data){ return ($data % 2) == 0; });
$moreThan10 = new cdf\ClosureFilter(function($data){ return $data > 10; });
$moreThan15 = new cdf\ClosureFilter(function($data){ return $data > 15; });
$lessThan10 = new cdf\ClosureFilter(function($data){ return $data < 10; });
$lessThan5 = new cdf\ClosureFilter(function($data){ return $data < 5; });
```

**Dynamically adding constraints**
**Basic usage**

You can add constraint as you loop. The CrazyCodr Data Filter is not a preprocessor filter, it filters as you iterate. Thus, you can add filters to it as we go. Although not the best method this code below allows you to create a dynamic distinct filter base on the model of the vehicule.
The fun aspect of this class is that you can prepare filtering closures in advance, they could be simple closures in another file that you test using unit tests. In this case, we prepare those closures in advance and add the right closures to the filtering process.

```PHP
class CrazyCodr_Data_Filter_Example1_Mock {
public $year;
public $make;
public $model;
public $type;
public function __construct($year, $make, $model, $type){
$this->year = $year;
$this->make = $make;
$this->model = $model;
$this->type = $type;
}
}
//Basic filter setup
$filteredData = new cdf\FilterIterator(new cdf\FilterGroup(), $data);

$data = array(
new CrazyCodr_Data_Filter_Example1_Mock(2010, 'Hyundai', 'Tucson', 'SUV'),
new CrazyCodr_Data_Filter_Example1_Mock(2011, 'Hyundai', 'Tucson', 'SUV'),
new CrazyCodr_Data_Filter_Example1_Mock(2012, 'Hyundai', 'Tucson', 'SUV'),
new CrazyCodr_Data_Filter_Example1_Mock(2012, 'Hyundai', 'Elantra', 'Compact'),
new CrazyCodr_Data_Filter_Example1_Mock(2013, 'Hyundai', 'Accent', 'SubCompact'),
new CrazyCodr_Data_Filter_Example1_Mock(2009, 'Hyundai', 'SantaFee', 'SUV'),
new CrazyCodr_Data_Filter_Example1_Mock(2011, 'Hyundai', 'Genesis', 'Intermediate'),
);
$filteredData = new \CrazyCodr\Data\Filter($data);
//Display data
$filteredData->addFilter($oddNumbers);
foreach($filteredData as $data)
{
$filterOutThisName = $data->model;
$filteredData->where(
function($data)use($filterOutThisName){
echo 'Filtering on '.$filterOutThisName.'<br>';
return $data->model != $filterOutThisName;
}
);
var_dump($data);
echo '<br>';
echo $data.',';
}
```

**Using OR / ANY constraints instead of AND / ALL**

When creating the filter, you can use the second parameter to set the filter to ANY mode and use multiple conditions to add possible values to the result.

```PHP
class CrazyCodr_Data_Filter_Example1_Mock {
public $year;
public $make;
public $model;
public $type;
public function __construct($year, $make, $model, $type){
$this->year = $year;
$this->make = $make;
$this->model = $model;
$this->type = $type;
}
}

$data = array(
new CrazyCodr_Data_Filter_Example1_Mock(2010, 'Hyundai', 'Tucson', 'SUV'),
new CrazyCodr_Data_Filter_Example1_Mock(2011, 'Hyundai', 'Tucson', 'SUV'),
new CrazyCodr_Data_Filter_Example1_Mock(2012, 'Hyundai', 'Tucson', 'SUV'),
new CrazyCodr_Data_Filter_Example1_Mock(2012, 'Hyundai', 'Elantra', 'Compact'),
new CrazyCodr_Data_Filter_Example1_Mock(2013, 'Hyundai', 'Accent', 'SubCompact'),
new CrazyCodr_Data_Filter_Example1_Mock(2009, 'Hyundai', 'SantaFee', 'SUV'),
new CrazyCodr_Data_Filter_Example1_Mock(2011, 'Hyundai', 'Genesis', 'Intermediate'),
);
$filteredData = new \CrazyCodr\Data\Filter($data, \CrazyCodr\Data\Filter::FILTER_TYPE_ANY);
$filteredData->where(function($a){ return $a->make == 'Hyundai'; });
$filteredData->where(function($a){ return $a->make == 'Toyota'; });

//Will return all items that are from Toyota or Hyundai, change the type to ALL (default) and it
//fails on all tests because no car can be of Toyota AND Hyundai
foreach($filteredData as $data)
{
var_dump($data);
echo '<br>';
}
Results in
```
1,3,5,7,9,11,13,15,17,19,
```

**Creating groups of constraints**
**Support for grouped constraints**

When calling `where` you can specify a name for the group of constraint to add this new filter closure under. When you do, i'll regroup all constraint of the same name under a same operator. Passing the type of constraint when calling where sets that group's constraint type
By default, the FilterIterator uses a simple FilterGroup for better extension. The FilterGroup can be set to ANY mode instead of AND mode (default) and then the conditions complement each other.

```PHP
class CrazyCodr_Data_Filter_Example1_Mock {
public $year;
public $make;
public $model;
public $type;
public function __construct($year, $make, $model, $type){
$this->year = $year;
$this->make = $make;
$this->model = $model;
$this->type = $type;
}
}
//You can set the filter group to use a ANY condition, by default, it uses a ALL condition
$filteredData = new cdf\FilterIterator(new cdf\FilterGroup(cdf\FilterGroup::CONTAINER_TYPE_ANY), $data);

$data = array(
new CrazyCodr_Data_Filter_Example1_Mock(2010, 'Hyundai', 'Tucson', 'SUV'),
new CrazyCodr_Data_Filter_Example1_Mock(2011, 'Hyundai', 'Tucson', 'SUV'),
new CrazyCodr_Data_Filter_Example1_Mock(2012, 'Hyundai', 'Tucson', 'SUV'),
new CrazyCodr_Data_Filter_Example1_Mock(2012, 'Hyundai', 'Elantra', 'Compact'),
new CrazyCodr_Data_Filter_Example1_Mock(2013, 'Hyundai', 'Accent', 'SubCompact'),
new CrazyCodr_Data_Filter_Example1_Mock(2009, 'Hyundai', 'SantaFee', 'SUV'),
new CrazyCodr_Data_Filter_Example1_Mock(2011, 'Hyundai', 'Genesis', 'Intermediate'),
);
$filteredData = new \CrazyCodr\Data\Filter($data);
$filteredData->where(function($a){ return $a->year == 2012; });
$filteredData->where(function($a){ return $a->type == 'SUV'; }, 'typeGroup', \CrazyCodr\Data\Filter::FILTER_TYPE_ANY);
$filteredData->where(function($a){ return $a->type == 'Compact'; }, 'typeGroup', \CrazyCodr\Data\Filter::FILTER_TYPE_ANY);

//Will return all items that are from SUV or Compact type since the group is on ANY
//but will return only 2012 models because the whole filter is set on ALL
//Display data
$filteredData->addFilter($oddNumbers);
$filteredData->addFilter($moreThan15);
foreach($filteredData as $data)
{
var_dump($data);
echo '<br>';
echo $data.',';
}
```
Results in
```
1,3,5,7,9,11,13,15,16,17,18,19,20,
```
16 changes: 8 additions & 8 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion run-tests

This file was deleted.

0 comments on commit 544de00

Please sign in to comment.