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

Support sorting by multiple columns #49

Merged
merged 6 commits into from
Feb 24, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ Filebase is simple by design, but has enough features for the more advanced.
* File locking (on save)
* Intuitive Method Naming


## Installation

Use [Composer](http://getcomposer.org/) to install package.
Expand Down Expand Up @@ -368,6 +367,12 @@ $usersWithGmail = $db->where('email','LIKE','@gmail.com')
->orderBy('email', 'ASC')
->results();

// OrderBy can be applied multiple times to perform a multi-sort
$usersWithGmail = $db->query()
->where('email','LIKE','@gmail.com')
->orderBy('last_name', 'ASC')
->orderBy('email', 'ASC')
->results();

// this will return the first user in the list based on ascending order of user name.
$user = $db->orderBy('name', 'ASC')->first();
Expand Down
16 changes: 11 additions & 5 deletions src/Query.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ class Query extends QueryLogic
protected $fields = [];
protected $limit = 0;
protected $offset = 0;
protected $sortBy = 'ASC';
protected $orderBy = '';
protected $sortBy = ['ASC'];
protected $orderBy = [''];


/**
Expand Down Expand Up @@ -114,10 +114,16 @@ public function limit($limit, $offset = 0)
* ->orderBy()
*
*/
public function orderBy($field, $sort)
public function orderBy($field, $sort = 'ASC')
{
$this->orderBy = $field;
$this->sortBy = $sort;
if (count($this->orderBy) == 1 && $this->orderBy[0] == '') {
// Just set the initial index
$this->orderBy[0] = $field;
$this->sortBy[0] = strtoupper($sort);
} else {
$this->orderBy[] = $field;
$this->sortBy[] = strtoupper($sort);
}

return $this;
}
Expand Down
29 changes: 4 additions & 25 deletions src/QueryLogic.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?php namespace Filebase;

use Filebase\SortLogic;

class QueryLogic
{
Expand Down Expand Up @@ -192,35 +193,13 @@ protected function offsetLimit()
*/
protected function sort()
{
$orderBy = $this->orderBy;
$sortBy = $this->sortBy;

if ($orderBy=='')
if ($this->orderBy[0] == '')
{
return false;
}

usort($this->documents, function($a, $b) use ($orderBy, $sortBy) {

$propA = $a->field($orderBy);
$propB = $b->field($orderBy);


if (strnatcasecmp($propB, $propA) == strnatcasecmp($propA, $propB)) {
return 0;
}

if ($sortBy == 'DESC')
{
return (strnatcasecmp($propB, $propA) < strnatcasecmp($propA, $propB)) ? -1 : 1;
}
else
{
return (strnatcasecmp($propA, $propB) < strnatcasecmp($propB, $propA)) ? -1 : 1;
}

});

$sortlogic = new SortLogic($this->orderBy, $this->sortBy, 0);
usort($this->documents, [$sortlogic, 'sort']);
}


Expand Down
79 changes: 79 additions & 0 deletions src/SortLogic.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php namespace Filebase;

/**
* Recursive sort logic class
*
* @package Filebase
*/
class SortLogic
{
/**
* Field names to sort by
*
* @var array
*/
public $orderBy = [];

/**
* Sort direction (ASC, DESC)
*
* @var array
*/
public $sortDirection = [];

/**
* Index of the current sort data
*
* @var int
*/
public $index = 0;

/**
* Constructor
*
* @param array $orderBy
* @param array $sortDirection
* @param int $index
* @return void
*/
public function __construct($orderBy, $sortDirection, $index = 0)
{
$this->orderBy = $orderBy;
$this->sortDirection = $sortDirection;
$this->index = $index;
}

/**
* Sorting callback
*
* @param Document $docA
* @param Document $docB
* @return return int (-1, 0, 1)
*/
public function sort($docA, $docB)
{
$propA = $docA->field($this->orderBy[$this->index]);
$propB = $docB->field($this->orderBy[$this->index]);

if (strnatcasecmp($propA, $propB) == 0)
{
if (!isset($this->orderBy[$this->index + 1]))
{
return 0;
}

// If they match and there are multiple orderBys, go deeper (recurse)
$sortlogic = new self($this->orderBy, $this->sortDirection, $this->index + 1);
return $sortlogic->sort($docA, $docB);
}

if ($this->sortDirection[$this->index] == 'DESC')
{
return strnatcasecmp($propB, $propA);
}
else
{
return strnatcasecmp($propA, $propB);
}
}
}
92 changes: 92 additions & 0 deletions tests/QueryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,98 @@ public function testSorting()

}

public function prepareMultiOrderTestData()
{
$db = new \Filebase\Database([
'dir' => __DIR__ . '/databases/users_orderbymult',
'cache' => false
]);

$db->flush(true);

$companies = ['Google'=>['CA', 150], 'Apple'=>['CA', 180], 'Microsoft'=>['WA', 120], 'Amex'=>['DC', 20], 'Hooli'=>['CA', 150], 'Amazon'=>['PA', 140]];

foreach ($companies as $company => $data) {
$doc = $db->get(uniqid());
$doc->name = $company;
$doc->location = $data[0];
$doc->rank['reviews'] = $data[1];
$doc->status = 'enabled';
$doc->save();
}

return $db;
}

public function testMultiOrderbyOneOnly()
{
$db = $this->prepareMultiOrderTestData();

$test1 = $db->query()->orderBy('name', 'asc')->results();
$actual = array_map(function($doc) {
return $doc['name'];
}, $test1);
$expected = ['Amazon', 'Amex', 'Apple', 'Google', 'Hooli', 'Microsoft'];
$this->assertEquals($expected, $actual);

$db->flush(true);
}

public function testMultiOrderbyTwoAscAsc()
{
$db = $this->prepareMultiOrderTestData();

$test1 = $db->query()->orderBy('location', 'ASC')->orderBy('name', 'ASC')->results();
$actual = array_map(function($doc) {
return $doc['name'];
}, $test1);
$expected = ['Apple', 'Google', 'Hooli', 'Amex', 'Amazon', 'Microsoft'];
$this->assertEquals($expected, $actual);

$db->flush(true);
}

public function testMultiOrderbyTwoDescAsc()
{
$db = $this->prepareMultiOrderTestData();

$test2 = $db->query()->orderBy('location', 'desc')->orderBy('name', 'ASC')->results();
$actual = array_map(function($doc) {
return $doc['name'];
}, $test2);
$expected = ['Microsoft', 'Amazon', 'Amex', 'Apple', 'Google', 'Hooli'];
$this->assertEquals($expected, $actual);

$db->flush(true);
}

public function testMultiOrderbyTwoAscDesc()
{
$db = $this->prepareMultiOrderTestData();

$test3 = $db->query()->orderBy('location', 'ASC')->orderBy('name', 'DESC')->results();
$actual = array_map(function($doc) {
return $doc['name'];
}, $test3);
$expected = ['Hooli', 'Google', 'Apple', 'Amex', 'Amazon', 'Microsoft'];
$this->assertEquals($expected, $actual);

$db->flush(true);
}

public function testMultiOrderbyThree()
{
$db = $this->prepareMultiOrderTestData();

$test4 = $db->query()->orderBy('location', 'ASC')->orderBy('rank.reviews', 'ASC')->orderBy('name')->results();
$actual = array_map(function($doc) {
return $doc['name'];
}, $test4);
$expected = ['Google', 'Hooli', 'Apple', 'Amex', 'Amazon', 'Microsoft'];
$this->assertEquals($expected, $actual);

$db->flush(true);
}

//--------------------------------------------------------------------

Expand Down