Skip to content

Commit

Permalink
Merge pull request #232 from mvantwillert/master
Browse files Browse the repository at this point in the history
Add SortOrder class to allow missing to be set for sorting
  • Loading branch information
blackshadev committed Feb 14, 2024
2 parents f725403 + 7ad0364 commit cafc469
Show file tree
Hide file tree
Showing 6 changed files with 189 additions and 17 deletions.
20 changes: 12 additions & 8 deletions src/Domain/Syntax/Sort.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,31 @@

namespace JeroenG\Explorer\Domain\Syntax;

use Webmozart\Assert\Assert;

class Sort
{
/** @deprecated Use SortOrder::ASCENDING instead */
public const ASCENDING = 'asc';


/** @deprecated Use SortOrder::DESCENDING instead */
public const DESCENDING = 'desc';

private string $field;

private string $order;
private SortOrder $order;

public function __construct(string $field, string $order = self::ASCENDING)
public function __construct(string $field, string|SortOrder $order = SortOrder::ASCENDING)
{
$this->field = $field;
$this->order = $order;
Assert::inArray($order, [self::ASCENDING, self::DESCENDING]);

if (is_string($order)) {
$this->order = SortOrder::fromString($order);
} else {
$this->order = $order;
}
}

public function build(): array
{
return [$this->field => $this->order];
return [$this->field => $this->order->build()];
}
}
52 changes: 52 additions & 0 deletions src/Domain/Syntax/SortOrder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

declare(strict_types=1);

namespace JeroenG\Explorer\Domain\Syntax;

use Webmozart\Assert\Assert;

class SortOrder
{
public const ASCENDING = 'asc';

public const DESCENDING = 'desc';

public const MISSING_FIRST = '_first';

public const MISSING_LAST = '_last';

private string $order;

private ?string $missing;

private function __construct(string $order, ?string $missing)
{
$this->order = $order;
$this->missing = $missing;
Assert::inArray($order, [self::ASCENDING, self::DESCENDING]);
Assert::nullOrInArray($missing, [self::MISSING_FIRST, self::MISSING_LAST]);
}

public static function fromString(string $order): self
{
return new self($order, null);
}

public static function for(string $order = self::ASCENDING, string $missing = self::MISSING_LAST): self
{
return new self($order, $missing);
}

public function build(): array|string
{
if (is_null($this->missing)) {
return $this->order;
}

return [
'missing' => $this->missing,
'order' => $this->order
];
}
}
32 changes: 24 additions & 8 deletions tests/Unit/Domain/Query/QueryProperties/SortingTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,49 @@

use JeroenG\Explorer\Domain\Query\QueryProperties\Sorting;
use JeroenG\Explorer\Domain\Syntax\Sort;
use JeroenG\Explorer\Domain\Syntax\SortOrder;
use PHPUnit\Framework\TestCase;

final class SortingTest extends TestCase
{
public function test_it_builds_sorting(): void
{
$sort = Sorting::for(
new Sort(':fld:', Sort::DESCENDING),
new Sort(':fld:', SortOrder::DESCENDING),
);

self::assertSame([ 'sort' => [ [ ':fld:' => 'desc' ]]],$sort->build());
}

public function test_it_builds_sorting_from_sort_order(): void
{
$sort = Sorting::for(
new Sort(':fld:', SortOrder::for(SortOrder::DESCENDING, SortOrder::MISSING_FIRST)),
);

self::assertSame([ 'sort' => [ [ ':fld:' => ['missing' => '_first', 'order' => 'desc'] ]]],$sort->build());
}

public function test_it_combines(): void
{
$a = Sorting::for(
new Sort(':fld1:', Sort::DESCENDING),
new Sort(':fld2:', Sort::DESCENDING),
new Sort(':fld1:', SortOrder::DESCENDING),
new Sort(':fld2:', SortOrder::DESCENDING),
);
$b = Sorting::for(
new Sort(':fld3:', Sort::DESCENDING),
new Sort(':fld4:', Sort::DESCENDING),
new Sort(':fld3:', SortOrder::DESCENDING),
new Sort(':fld4:', SortOrder::DESCENDING),
);
$c = Sorting::for(
new Sort(':fld5:', Sort::DESCENDING),
new Sort(':fld5:', SortOrder::DESCENDING),
new Sort(':fld6:', SortOrder::for(SortOrder::DESCENDING)),
);
$d = Sorting::for(
new Sort(':fld7:', SortOrder::for(SortOrder::DESCENDING, SortOrder::MISSING_FIRST)),
);
$d = Sorting::for();
$e = Sorting::for();

$result = $a->combine($b, $c, $d);
$result = $a->combine($b, $c, $d, $e);

self::assertNotSame($a->build(), $result->build());
self::assertSame([
Expand All @@ -44,6 +58,8 @@ public function test_it_combines(): void
[ ':fld3:' => 'desc' ],
[ ':fld4:' => 'desc' ],
[ ':fld5:' => 'desc' ],
[ ':fld6:' => ['missing' => '_last', 'order' => 'desc']],
[ ':fld7:' => ['missing' => '_first', 'order' => 'desc']]
],
], $result->build());
}
Expand Down
52 changes: 52 additions & 0 deletions tests/Unit/Domain/Syntax/SortOrderTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php declare(strict_types=1);

namespace JeroenG\Explorer\Tests\Unit\Domain\Syntax;

use Illuminate\Testing\Assert;
use JeroenG\Explorer\Domain\Syntax\SortOrder;
use PHPUnit\Framework\TestCase;

final class SortOrderTest extends TestCase
{
public function test_it_uses_default_missing_when_creating_sort_order(): void
{
$sort = SortOrder::for(SortOrder::DESCENDING);

Assert::assertSame([
'missing' => SortOrder::MISSING_LAST,
'order' => SortOrder::DESCENDING
], $sort->build());
}

/**
* @dataProvider provideSortOrderStrings
*/
public function test_sort_order_can_be_created_from_sort_string(string $expectedResult, string $sortString): void
{
$subject = SortOrder::fromString($sortString);
Assert::assertSame($expectedResult, $subject->build());
}

/**
* @dataProvider provideMissingSortOrderStrings
*/
public function test_sort_order_can_be_created_from_sort_string_and_missing(array $expectedResult, string $sortString, string $missing): void
{
$subject = SortOrder::for($sortString, $missing);
Assert::assertSame($expectedResult, $subject->build());
}

public function provideSortOrderStrings(): iterable
{
yield 'asc' => ['asc', 'asc'];
yield 'desc' => ['desc', 'desc'];
}

public function provideMissingSortOrderStrings(): iterable
{
yield 'asc order with _last missing' => [['missing' => '_last', 'order' => 'asc'], 'asc', '_last'];
yield 'desc order with _last missing' => [['missing' => '_last', 'order' => 'desc'], 'desc', '_last'];
yield 'asc order with _first missing' => [['missing' => '_first', 'order' => 'asc'], 'asc', '_first'];
yield 'desc order with _first missing' => [['missing' => '_first', 'order' => 'desc'], 'desc', '_first'];
}
}
10 changes: 10 additions & 0 deletions tests/Unit/Query/QueryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use JeroenG\Explorer\Domain\Query\QueryProperties\TrackTotalHits;
use JeroenG\Explorer\Domain\Syntax\MatchAll;
use JeroenG\Explorer\Domain\Syntax\Sort;
use JeroenG\Explorer\Domain\Syntax\SortOrder;
use JeroenG\Explorer\Domain\Syntax\Term;
use PHPUnit\Framework\TestCase;
use TypeError;
Expand Down Expand Up @@ -44,6 +45,15 @@ public function test_it_builds_query_with_sort(): void
$result = $this->query->build();
self::assertEquals([$sort->build()], $result['sort'] ?? null);
}

public function test_it_builds_query_with_sort_order(): void
{
$sort = new Sort('field', SortOrder::for(SortOrder::DESCENDING, SortOrder::MISSING_FIRST));
$this->query->setSort([$sort]);

$result = $this->query->build();
self::assertEquals([$sort->build()], $result['sort'] ?? null);
}

public function test_it_throws_on_invalid_sort_argument(): void
{
Expand Down
40 changes: 39 additions & 1 deletion tests/Unit/ScoutSearchCommandBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use JeroenG\Explorer\Domain\Syntax\Compound\QueryType;
use JeroenG\Explorer\Domain\Syntax\MultiMatch;
use JeroenG\Explorer\Domain\Syntax\Sort;
use JeroenG\Explorer\Domain\Syntax\SortOrder;
use JeroenG\Explorer\Domain\Syntax\SyntaxInterface;
use JeroenG\Explorer\Domain\Syntax\Term;
use JeroenG\Explorer\Infrastructure\Scout\ScoutSearchCommandBuilder;
Expand Down Expand Up @@ -139,7 +140,44 @@ public function test_it_can_set_the_sort_order(): void

$command->setSort([new Sort('id', 'invalid')]);
}


public function test_it_can_set_the_sort_order_as_array(): void
{
$command = new ScoutSearchCommandBuilder();

self::assertFalse($command->hasSort());

$command->setSort([new Sort('id')]);

self::assertTrue($command->hasSort());
self::assertSame([['id' => 'asc']], $command->getSort());

$command->setSort([]);

self::assertFalse($command->hasSort());
self::assertSame([], $command->getSort());

$command->setSort([new Sort('id', SortOrder::for('desc'))]);

self::assertTrue($command->hasSort());
self::assertSame([['id' => ['missing' => '_last', 'order' => 'desc']]], $command->getSort());

$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Expected one of: "asc", "desc". Got: "invalid"');

$command->setSort([new Sort('id', SortOrder::for('invalid'))]);
}

public function test_it_throws_exception_when_missing_is_invalid(): void
{
$command = new ScoutSearchCommandBuilder();

$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Expected one of: "_first", "_last". Got: "invalid"');

$command->setSort([new Sort('id', SortOrder::for('desc', 'invalid'))]);
}

public function test_it_only_accepts_sort_classes(): void
{
$command = new ScoutSearchCommandBuilder();
Expand Down

0 comments on commit cafc469

Please sign in to comment.