Skip to content

Commit

Permalink
Merge 95545e9 into 7cc694f
Browse files Browse the repository at this point in the history
  • Loading branch information
brendt committed Jul 15, 2019
2 parents 7cc694f + 95545e9 commit 204f45b
Show file tree
Hide file tree
Showing 4 changed files with 270 additions and 4 deletions.
26 changes: 26 additions & 0 deletions README.md
Expand Up @@ -77,6 +77,32 @@ By using the `$casts` property you can cast your `status` attribute to `int` or
- [ ] Validation [#5](https://github.com/spatie/laravel-enum/issues/5)
- [ ] Request Transformation [#7](https://github.com/spatie/laravel-enum/pull/7)

### Scopes

The `HasEnums` trait also provides some useful scopes to query your database.
These scopes will also take the optional mapping you provided into account.

```php
Post::whereEnum('status', StatusEnum::DRAFT());

Post::whereNotEnum('status', StatusEnum::PUBLISHED());
```

You may provide multiple enums as an array:

```php
Post::whereEnum('status', [StatusEnum::DRAFT(), StatusEnum::ARCHIVED()]);

Post::whereNotEnum('status', [StatusEnum::PUBLISHED()]);
```

You may also provide textual input:

```php
Post::whereEnum('status', 'archived');
Post::whereEnum('status', 'legacy archived value');
```

### Testing

``` bash
Expand Down
13 changes: 13 additions & 0 deletions src/Exceptions/NoSuchEnumField.php
@@ -0,0 +1,13 @@
<?php

namespace Spatie\Enum\Laravel\Exceptions;

use InvalidArgumentException;

final class NoSuchEnumField extends InvalidArgumentException
{
public static function make(string $field, string $model): NoSuchEnumField
{
return new self("No enum field {$field} registered on model {$model}");
}
}
131 changes: 127 additions & 4 deletions src/HasEnums.php
Expand Up @@ -2,10 +2,12 @@

namespace Spatie\Enum\Laravel;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use InvalidArgumentException;
use Spatie\Enum\Enumerable;
use Spatie\Enum\Laravel\Exceptions\InvalidEnumError;
use Spatie\Enum\Laravel\Exceptions\NoSuchEnumField;

/**
* @mixin Model
Expand All @@ -28,6 +30,86 @@ public function getAttribute($key)
: $value;
}

/**
* @param Builder $builder
* @param string $key
* @param int|string|Enumerable|int[]|string[]|Enumerable[] $enumerables
*
* @see Builder::whereIn()
*/
public function scopeWhereEnum(
Builder $builder,
string $key,
$enumerables
): void {
$this->buildEnumScope(
$builder,
'whereIn',
$key,
$enumerables
);
}

/**
* @param Builder $builder
* @param string $key
* @param int|string|Enumerable|int[]|string[]|Enumerable[] $enumerables
*
* @see Builder::orWhereIn()
*/
public function scopeOrWhereEnum(
Builder $builder,
string $key,
$enumerables
): void {
$this->buildEnumScope(
$builder,
'orWhereIn',
$key,
$enumerables
);
}

/**
* @param Builder $builder
* @param string $key
* @param int|string|Enumerable|int[]|string[]|Enumerable[] $enumerables
*
* @see Builder::whereNotIn()
*/
public function scopeWhereNotEnum(
Builder $builder,
string $key,
$enumerables
): void {
$this->buildEnumScope(
$builder,
'whereNotIn',
$key,
$enumerables
);
}

/**
* @param Builder $builder
* @param string $key
* @param int|string|Enumerable|int[]|string[]|Enumerable[] $enumerables
*
* @see Builder::orWhereNotIn()
*/
public function scopeOrWhereNotEnum(
Builder $builder,
string $key,
$enumerables
): void {
$this->buildEnumScope(
$builder,
'orWhereNotIn',
$key,
$enumerables
);
}

/**
* @param string $key
* @param int|string|Enumerable $value
Expand All @@ -46,13 +128,24 @@ protected function setEnumAttribute(string $key, $value)
throw InvalidEnumError::make(static::class, $key, $enumClass, get_class($value));
}

$this->attributes[$key] = $this->hasCast($key, ['int', 'integer'])
? $value->getIndex()
: $value->getValue();
$this->attributes[$key] = $this->getStoredValue($key, $value);

return $this;
}

/**
* @param string $key
* @param Enumerable $enum
*
* @return int|string
*/
protected function getStoredValue(string $key, Enumerable $enum)
{
return $this->hasCast($key, ['int', 'integer'])
? $enum->getIndex()
: $enum->getValue();
}

/**
* @param string $key
* @param int|string $value
Expand Down Expand Up @@ -89,9 +182,39 @@ protected function getEnumClass(string $key): string
*/
protected function asEnum(string $class, $value): Enumerable
{
if ($value instanceof Enumerable) {
return $value;
}

return forward_static_call(
$class.'::make',
$class . '::make',
$value
);
}

/**
* @param Builder $builder
* @param string $method
* @param string $key
* @param int|string|Enumerable|int[]|string[]|Enumerable[] $enumerables
*/
protected function buildEnumScope(
Builder $builder,
string $method,
string $key,
$enumerables
): void {
if (! $this->isEnumAttribute($key)) {
throw NoSuchEnumField::make($key, get_class($this));
}

$enumerables = is_array($enumerables) ? $enumerables : [$enumerables];

$builder->$method(
$key,
array_map(function ($enumerable) use ($key) {
$this->getStoredValue($key, $this->asEnum($key, $enumerable));
}, $enumerables)
);
}
}
104 changes: 104 additions & 0 deletions tests/EnumScopeTest.php
@@ -0,0 +1,104 @@
<?php

namespace Spatie\Enum\Laravel\Tests;

use Spatie\Enum\Laravel\Exceptions\NoSuchEnumField;
use Spatie\Enum\Laravel\Tests\Extra\Post;
use Spatie\Enum\Laravel\Tests\Extra\StatusEnum;

final class EnumScopeTest extends TestCase
{
/** @test */
public function scope_where_enum_invalid_enum_field_throws_exception()
{
$this->expectException(NoSuchEnumField::class);

Post::whereEnum('unknown', StatusEnum::draft())->count();
}

/** @test */
public function scope_where_not_enum_invalid_enum_field_throws_exception()
{
$this->expectException(NoSuchEnumField::class);

Post::whereNotEnum('unknown', StatusEnum::draft())->count();
}

/** @test */
public function test_scope_where_enum()
{
Post::create([
'status' => StatusEnum::draft(),
]);

$this->assertEquals(1, Post::whereEnum('status', StatusEnum::draft())->count());
$this->assertEquals(0, Post::whereEnum('status', StatusEnum::published())->count());
}

/** @test */
public function test_scope_where_enum_with_array()
{
Post::create([
'status' => StatusEnum::draft(),
]);

Post::create([
'status' => StatusEnum::published(),
]);

$this->assertEquals(2, Post::whereEnum('status', [StatusEnum::draft(), StatusEnum::published()])->count());
}

/** @test */
public function test_scope_where_not_enum()
{
Post::create([
'status' => StatusEnum::draft(),
]);

$this->assertEquals(1, Post::whereNotEnum('status', StatusEnum::published())->count());
$this->assertEquals(0, Post::whereNotEnum('status', StatusEnum::draft())->count());
}

/** @test */
public function test_scope_where_not_enum_with_array()
{
Post::create([
'status' => StatusEnum::draft(),
]);

$this->assertEquals(1, Post::whereNotEnum('status', [StatusEnum::published(), StatusEnum::archived()])->count());
$this->assertEquals(0, Post::whereNotEnum('status', [StatusEnum::published(), StatusEnum::draft()])->count());
}

/** @test */
public function scope_with_textual_input()
{
Post::create([
'status' => StatusEnum::archived(),
]);

$this->assertEquals(1, Post::whereEnum('status', 'archived')->count());
}

/** @test */
public function scope_with_index_input()
{
Post::create([
'status' => StatusEnum::archived(),
]);

$this->assertEquals(1, Post::whereEnum('status', 2)->count());
}

/** @test */
public function scope_with_mapped_input()
{
Post::create([
'status' => StatusEnum::archived(),
]);

$this->assertEquals(1, Post::whereEnum('status', 'stored archive')->count());
$this->assertEquals(1, Post::whereEnum('status', StatusEnum::archived())->count());
}
}

0 comments on commit 204f45b

Please sign in to comment.