diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml
index f064804..ca52647 100644
--- a/.github/workflows/run-tests.yml
+++ b/.github/workflows/run-tests.yml
@@ -9,12 +9,10 @@ jobs:
fail-fast: true
matrix:
os: [ ubuntu-latest ]
- php: [ 7.3, 7.4 ]
- laravel: [ 7.*, 8.* ]
+ php: [ 7.3, 7.4, 8.0 ]
+ laravel: [ 8.* ]
stability: [ prefer-lowest, prefer-stable ]
include:
- - laravel: 7.*
- testbench: 5.*
- laravel: 8.*
testbench: 6.*
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 85f9b3b..f96ef86 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,10 +2,29 @@
All notable changes to `nova-enum-field` will be documented in this file.
+## 2.3.0 - 2021-01-25
+
+- Drop support for Laravel `7.x`
+- Drop support for `laravel-enum < 3.1`.
+- Add a customisable select filter.
+- Add a customisable boolean filter.
+- Add a flagged enum field.
+- Refactor and simplify tests.
+
+## 2.2.0 - 2020-09-25
+
+- Add support for Laravel `8.x`.
+- Add support for PHP `8.0`.
+- Allow editing enums without casts.
+
+## 2.1.0 - 2020-09-01
+
+- Add support for `laravel-enum 2.2.0`.
+
## 2.0.0 - 2020-07-08
-- Update `larave-enum` to `2.x`
-- Drop support for laravel < `7.x`
+- Add support for `laravel-enum 2.x`.
+- Drop support for Laravel `< 7.x`.
## 1.1.0 - 2019-09-30
@@ -16,7 +35,7 @@ All notable changes to `nova-enum-field` will be documented in this file.
## 1.0.4 - 2019-09-27
- Add documentation.
-- Refactor field code.
+- Refactor `Enum` field code.
## 1.0.3 - 2019-09-27
diff --git a/README.md b/README.md
index 0df5c87..ae11f63 100644
--- a/README.md
+++ b/README.md
@@ -19,11 +19,11 @@ composer require simplesquid/nova-enum-field
## Setup
-This package requires that you use Attribute Casting in your models. From the docs at [BenSampo/laravel-enum](https://github.com/BenSampo/laravel-enum#attribute-casting), this can be done like so:
+It is strongly recommended that you use Attribute Casting in your models. From the docs at [BenSampo/laravel-enum](https://github.com/BenSampo/laravel-enum#attribute-casting), this can be done like this:
```php
+use App\Enums\UserType;
use BenSampo\Enum\Traits\CastsEnums;
-use BenSampo\Enum\Tests\Enums\UserType;
use Illuminate\Database\Eloquent\Model;
class Example extends Model
@@ -38,12 +38,12 @@ class Example extends Model
## Usage
-You can use the `Enum` field in your Nova resource like so:
+You can use the `Enum` field in your Nova resource like this:
```php
namespace App\Nova;
-use BenSampo\Enum\Tests\Enums\UserType;
+use App\Enums\UserType;
use SimpleSquid\Nova\Fields\Enum\Enum;
class Example extends Resource
@@ -55,7 +55,7 @@ class Example extends Resource
return [
// ...
- Enum::make('User Type')->attachEnum(UserType::class),
+ Enum::make('User Type')->attach(UserType::class),
// ...
];
@@ -63,6 +63,96 @@ class Example extends Resource
}
```
+### Flagged Enums
+
+You can use the `FlaggedEnum` field in your Nova resource like this (see [Flagged/Bitwise Enum](https://github.com/BenSampo/laravel-enum#flaggedbitwise-enum) setup):
+
+```php
+namespace App\Nova;
+
+use App\Enums\UserPermissions;
+use SimpleSquid\Nova\Fields\Enum\FlaggedEnum;
+
+class Example extends Resource
+{
+ // ...
+
+ public function fields(Request $request)
+ {
+ return [
+ // ...
+
+ FlaggedEnum::make('User Permissions')->attach(UserPermissions::class),
+
+ // ...
+ ];
+ }
+}
+```
+
+### Filters
+
+If you would like to use the provided Nova Select filter (which is compatible with both the `Enum` and `FlaggedEnum` fields), you can include it like this:
+
+```php
+namespace App\Nova;
+
+use App\Enums\UserPermissions;
+use App\Enums\UserType;
+use SimpleSquid\Nova\Fields\Enum\EnumFilter;
+
+class Example extends Resource
+{
+ // ...
+
+ public function filters(Request $request)
+ {
+ return [
+ new EnumFilter('user_type', UserType::class),
+
+ new EnumFilter('user_permissions', UserPermissions::class),
+
+ // Or with optional filter name:
+ (new EnumFilter('user_type', UserType::class))
+ ->name('Type of user'),
+ ];
+ }
+}
+```
+
+Alternatively, you may wish to use the provided Nova Boolean filter (which is also compatible with both the `Enum` and `FlaggedEnum` fields):
+
+```php
+namespace App\Nova;
+
+use App\Enums\UserPermissions;
+use App\Enums\UserType;
+use SimpleSquid\Nova\Fields\Enum\EnumBooleanFilter;
+
+class Example extends Resource
+{
+ // ...
+
+ public function filters(Request $request)
+ {
+ return [
+ new EnumBooleanFilter('user_type', UserType::class),
+
+ new EnumBooleanFilter('user_permissions', UserPermissions::class),
+
+ // Or with optional filter name:
+ (new EnumBooleanFilter('user_type', UserType::class))
+ ->name('Type of user'),
+
+ // When filtering a FlaggedEnum, it will default to filtering
+ // by ANY flags, however you may wish to filter by ALL flags:
+ (new EnumBooleanFilter('user_permissions', UserPermissions::class))
+ ->filterAllFlags(),
+ ];
+ }
+}
+```
+
## Testing
``` bash
diff --git a/composer.json b/composer.json
index e849199..1968b2f 100644
--- a/composer.json
+++ b/composer.json
@@ -27,14 +27,16 @@
],
"require": {
"php": "^7.3|^8.0",
- "bensampo/laravel-enum": "^2.0|^3.0",
- "cakephp/chronos": "^1.2.3|^2.0",
- "illuminate/support": "^7.0|^8.0",
+ "bensampo/laravel-enum": "^3.1",
+ "illuminate/support": "^8.0",
"laravel/nova": "^3.0"
},
"require-dev": {
- "orchestra/testbench": "^5.0|^6.0",
- "phpunit/phpunit": "^8.2|^9.0",
+ "joshgaber/novaunit": "^2.2",
+ "mockery/mockery": "^1.3.3",
+ "nunomaduro/collision": "^5.2",
+ "orchestra/testbench": "^6.0",
+ "phpunit/phpunit": "^9.3.3",
"symfony/var-dumper": "^5.0"
},
"autoload": {
@@ -47,6 +49,11 @@
"SimpleSquid\\Nova\\Fields\\Enum\\Tests\\": "tests"
}
},
+ "extra": {
+ "laravel": {
+ "providers": []
+ }
+ },
"scripts": {
"test": "vendor/bin/phpunit --colors=always"
},
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 1318ca3..ba6889a 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -10,6 +10,7 @@
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
+ printerClass="NunoMaduro\Collision\Adapters\Phpunit\Printer"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
@@ -18,7 +19,8 @@
- tests
+ tests/Fields
+ tests/Filters
diff --git a/src/Enum.php b/src/Enum.php
index b28f5a8..3c1ff9a 100644
--- a/src/Enum.php
+++ b/src/Enum.php
@@ -1,64 +1,49 @@
options($this->getEnumOptions($enumClass))
- ->rules('required', new EnumValue($enumClass, false))
- ->resolveUsing(
- function ($enum) {
- return $enum instanceof \BenSampo\Enum\Enum ? $enum->value : $enum;
- }
- )
- ->displayUsing(
- function ($enum) {
- return $enum instanceof \BenSampo\Enum\Enum ? $enum->description : $enum;
- }
- );
+ parent::__construct($name, $attribute, $resolveCallback);
+
+ $this->resolveUsing(
+ function ($value) {
+ return $value instanceof \BenSampo\Enum\Enum ? $value->value : $value;
+ }
+ );
+
+ $this->displayUsing(
+ function ($value) {
+ return $value instanceof \BenSampo\Enum\Enum ? $value->description : $value;
+ }
+ );
+
+ $this->fillUsing(
+ function (NovaRequest $request, $model, $attribute, $requestAttribute) {
+ if ($request->exists($requestAttribute)) {
+ $model->{$attribute} = $request[$requestAttribute];
+ }
+ }
+ );
}
- /**
- * Hydrate the given attribute on the model based on the incoming request.
- *
- * @param NovaRequest $request
- * @param string $requestAttribute
- * @param object $model
- * @param string $attribute
- *
- * @return void
- */
- protected function fillAttributeFromRequest(NovaRequest $request, $requestAttribute, $model, $attribute)
+ public function attach($class)
{
- if ($request->exists($requestAttribute)) {
- $model->{$attribute} = $request[$requestAttribute];
- }
+ return $this->options($class::asSelectArray())
+ ->rules('required', new EnumValue($class, false));
}
- protected function getEnumOptions(string $enumClass): array
+ /**
+ * @deprecated deprecated since version 2.3
+ */
+ public function attachEnum($class)
{
- // Since laravel-enum v2.2.0, the method has been named 'asSelectArray'
- if (in_array(Arrayable::class, class_implements($enumClass))) {
- return $enumClass::asSelectArray();
- }
-
- return $enumClass::toSelectArray();
+ return $this->attach($class);
}
}
diff --git a/src/EnumBooleanFilter.php b/src/EnumBooleanFilter.php
new file mode 100644
index 0000000..1d7f800
--- /dev/null
+++ b/src/EnumBooleanFilter.php
@@ -0,0 +1,93 @@
+column = $column;
+ $this->class = $class;
+
+ $this->flagged = is_subclass_of($this->class, \BenSampo\Enum\FlaggedEnum::class);
+ }
+
+ public function filterAnyFlags()
+ {
+ $this->scope = 'any';
+
+ return $this;
+ }
+
+ public function filterAllFlags()
+ {
+ $this->scope = 'all';
+
+ return $this;
+ }
+
+ public function name($name = null)
+ {
+ if (is_null($name)) {
+ return $this->name ?: Nova::humanize($this->column);
+ }
+
+ $this->name = $name;
+
+ return $this;
+ }
+
+ public function apply(Request $request, $query, $value)
+ {
+ $enums = array_keys(array_filter($value));
+
+ if (empty($enums)) {
+ return $query;
+ }
+
+ if ($this->flagged && $this->scope === 'all') {
+ return $this->scopeHasAllFlags($query, $this->column, $enums);
+ }
+
+ return $query->where(
+ function ($query) use ($enums) {
+ if ($this->flagged) {
+ $query = $this->scopeHasAnyFlags($query, $this->column, $enums);
+
+ $enums = in_array($this->class::None, $enums) ? [$this->class::None] : [];
+ } else {
+ $query->where($this->column, array_shift($enums));
+ }
+
+ foreach ($enums as $enum) {
+ $query->orWhere($this->column, $enum);
+ }
+ }
+ );
+ }
+
+ public function options(Request $request)
+ {
+ if ($this->flagged && $this->scope === 'all') {
+ return array_flip(Arr::except($this->class::asSelectArray(), $this->class::None));
+ }
+
+ return array_flip($this->class::asSelectArray());
+ }
+}
diff --git a/src/EnumFilter.php b/src/EnumFilter.php
new file mode 100644
index 0000000..d363104
--- /dev/null
+++ b/src/EnumFilter.php
@@ -0,0 +1,52 @@
+column = $column;
+ $this->class = $class;
+
+ $this->flagged = is_subclass_of($this->class, \BenSampo\Enum\FlaggedEnum::class);
+ }
+
+ public function name($name = null)
+ {
+ if (is_null($name)) {
+ return $this->name ?: Nova::humanize($this->column);
+ }
+
+ $this->name = $name;
+
+ return $this;
+ }
+
+ public function apply(Request $request, $query, $value)
+ {
+ if ($this->flagged && $value != $this->class::None) {
+ return $this->scopeHasFlag($query, $this->column, $value);
+ }
+
+ return $query->where($this->column, $value);
+ }
+
+ public function options(Request $request)
+ {
+ return array_flip($this->class::asSelectArray());
+ }
+}
diff --git a/src/FlaggedEnum.php b/src/FlaggedEnum.php
new file mode 100644
index 0000000..fb855a2
--- /dev/null
+++ b/src/FlaggedEnum.php
@@ -0,0 +1,44 @@
+noValueText('None');
+ }
+
+ public function attach($class)
+ {
+ $this->resolveUsing(
+ function ($value) use ($class) {
+ if (! $value instanceof \BenSampo\Enum\FlaggedEnum) {
+ return $value;
+ }
+
+ return collect($value->getValues())->mapWithKeys(function ($flag) use ($value) {
+ return [$flag => $value->hasFlag($flag)];
+ })->except($class::None)->all();
+ }
+ );
+
+ $this->fillUsing(
+ function (NovaRequest $request, $model, $attribute, $requestAttribute) use ($class) {
+ if ($request->exists($requestAttribute)) {
+ $value = json_decode($request[$requestAttribute], true);
+
+ $model->{$attribute} = $class::flags(array_keys(array_filter($value)));
+ }
+ }
+ );
+
+ return $this->options(Arr::except($class::asSelectArray(), $class::None));
+ }
+}
diff --git a/tests/Examples/FlaggedEnum.php b/tests/Examples/FlaggedEnum.php
new file mode 100644
index 0000000..8bec39e
--- /dev/null
+++ b/tests/Examples/FlaggedEnum.php
@@ -0,0 +1,20 @@
+ ExampleIntegerEnum::class,
+ 'enum' => FlaggedEnum::class,
];
}
diff --git a/tests/Examples/ExampleIntegerEnum.php b/tests/Examples/IntegerEnum.php
similarity index 87%
rename from tests/Examples/ExampleIntegerEnum.php
rename to tests/Examples/IntegerEnum.php
index beefdf1..4b25f67 100644
--- a/tests/Examples/ExampleIntegerEnum.php
+++ b/tests/Examples/IntegerEnum.php
@@ -9,7 +9,7 @@
* @method static Moderator()
* @method static Subscriber()
*/
-class ExampleIntegerEnum extends Enum
+class IntegerEnum extends Enum
{
const Administrator = 0;
diff --git a/tests/Examples/IntegerModel.php b/tests/Examples/IntegerModel.php
new file mode 100644
index 0000000..cc9f928
--- /dev/null
+++ b/tests/Examples/IntegerModel.php
@@ -0,0 +1,21 @@
+ IntegerEnum::class,
+ ];
+}
diff --git a/tests/Examples/NoCastsModel.php b/tests/Examples/NoCastsModel.php
new file mode 100644
index 0000000..dcd8100
--- /dev/null
+++ b/tests/Examples/NoCastsModel.php
@@ -0,0 +1,14 @@
+ StringEnum::class,
+ ];
+}
diff --git a/tests/Fields/FieldTest.php b/tests/Fields/FieldTest.php
new file mode 100644
index 0000000..8d180fd
--- /dev/null
+++ b/tests/Fields/FieldTest.php
@@ -0,0 +1,59 @@
+setUpDatabase($this->app);
+
+ $this->field = Enum::make('Enum')->attach(IntegerEnum::class);
+ }
+
+ /** @test */
+ public function it_starts_with_no_options_and_rules()
+ {
+ $field = Enum::make('Enum');
+
+ $this->assertArrayNotHasKey('options', $field->meta);
+
+ $this->assertEmpty($field->rules);
+ }
+
+ /** @test */
+ public function it_allows_an_enum_to_be_attached()
+ {
+ $this->assertArrayHasKey('options', $this->field->meta);
+ }
+
+ /** @test */
+ public function it_adds_correct_rules()
+ {
+ $this->assertContains('required', $this->field->rules);
+
+ $this->assertContainsEquals(new EnumValue(IntegerEnum::class, false), $this->field->rules);
+ }
+
+ /** @test */
+ public function it_displays_enum_options()
+ {
+ $this->assertCount(count(IntegerEnum::getValues()), $this->field->meta['options']);
+
+ foreach (IntegerEnum::getValues() as $enum) {
+ $this->assertContains([
+ 'label' => IntegerEnum::getDescription($enum),
+ 'value' => $enum,
+ ], $this->field->meta['options']);
+ }
+ }
+}
diff --git a/tests/Fields/FlaggedFieldTest.php b/tests/Fields/FlaggedFieldTest.php
new file mode 100644
index 0000000..61d2ade
--- /dev/null
+++ b/tests/Fields/FlaggedFieldTest.php
@@ -0,0 +1,111 @@
+ true,
+ FlaggedEnum::WriteComments => true,
+ FlaggedEnum::EditComments => false,
+ ];
+
+ protected function setUp(): void
+ {
+ parent::setUp();
+
+ $this->setUpDatabase($this->app);
+
+ $this->field = FlaggedEnumField::make('Enum')->attach(FlaggedEnum::class);
+
+ $this->model = FlaggedModel::create(['enum' => FlaggedEnum::None]);
+ }
+
+ /** @test */
+ public function it_starts_with_no_options()
+ {
+ $field = FlaggedEnumField::make('Enum');
+
+ $this->assertEmpty($field->options);
+ }
+
+ /** @test */
+ public function it_allows_an_enum_to_be_attached()
+ {
+ $this->assertNotEmpty($this->field->options);
+ }
+
+ /** @test */
+ public function it_has_no_value_text()
+ {
+ $this->assertSame('None', $this->field->noValueText);
+ }
+
+ /** @test */
+ public function it_displays_enum_options()
+ {
+ $this->assertCount(count(FlaggedEnum::getValues()) - 1, $this->field->options);
+
+ foreach (FlaggedEnum::getValues() as $enum) {
+ if ($enum === FlaggedEnum::None) {
+ continue;
+ }
+
+ $this->assertContains([
+ 'label' => FlaggedEnum::getDescription($enum),
+ 'name' => $enum,
+ ], $this->field->options);
+ }
+ }
+
+ /** @test */
+ public function it_resolves_enum_values()
+ {
+ $this->field->resolve($this->model);
+
+ $this->assertCount(count(FlaggedEnum::getValues()) - 1, $this->field->value);
+
+ foreach (array_keys($this->values) as $enum) {
+ $this->assertEquals(false, $this->field->value[$enum]);
+ }
+
+ $this->model->enum = array_keys(array_filter($this->values));
+
+ $this->field->resolve($this->model);
+
+ $this->assertCount(count(FlaggedEnum::getValues()) - 1, $this->field->value);
+
+ foreach ($this->values as $enum => $value) {
+ $this->assertEquals($value, $this->field->value[$enum]);
+ }
+ }
+
+ /** @test */
+ public function it_fills_database_with_flagged_enum_value()
+ {
+ $request = new NovaRequest();
+ $request->query->add(['enum' => json_encode($this->values)]);
+
+ $this->field->fill($request, $this->model);
+
+ $this->assertDatabaseHas('example_models', ['enum' => FlaggedEnum::None]);
+
+ $this->model->save();
+
+ $this->assertDatabaseHas('example_models', [
+ 'enum' => array_sum(array_keys(array_filter($this->values))),
+ ]);
+
+ $this->assertDatabaseMissing('example_models', ['enum' => FlaggedEnum::None]);
+ }
+}
diff --git a/tests/Fields/IntegerFieldTest.php b/tests/Fields/IntegerFieldTest.php
new file mode 100644
index 0000000..78010fb
--- /dev/null
+++ b/tests/Fields/IntegerFieldTest.php
@@ -0,0 +1,60 @@
+setUpDatabase($this->app);
+
+ $this->field = Enum::make('Enum')->attach(IntegerEnum::class);
+
+ $this->model = IntegerModel::create(['enum' => IntegerEnum::Moderator]);
+ }
+
+ /** @test */
+ public function it_resolves_enum_value()
+ {
+ $this->field->resolve($this->model);
+
+ $this->assertSame(IntegerEnum::Moderator, $this->field->value);
+ }
+
+ /** @test */
+ public function it_displays_enum_description()
+ {
+ $this->field->resolveForDisplay($this->model);
+
+ $this->assertSame(IntegerEnum::Moderator()->description, $this->field->value);
+ }
+
+ /** @test */
+ public function it_fills_database_with_enum_value()
+ {
+ $request = new NovaRequest();
+ $request->query->add(['enum' => IntegerEnum::Subscriber]);
+
+ $this->field->fill($request, $this->model);
+
+ $this->assertDatabaseHas('example_models', ['enum' => IntegerEnum::Moderator]);
+
+ $this->model->save();
+
+ $this->assertDatabaseHas('example_models', ['enum' => IntegerEnum::Subscriber]);
+
+ $this->assertDatabaseMissing('example_models', ['enum' => IntegerEnum::Moderator]);
+ }
+}
diff --git a/tests/Fields/NoCastsFieldTest.php b/tests/Fields/NoCastsFieldTest.php
new file mode 100644
index 0000000..80c3d0b
--- /dev/null
+++ b/tests/Fields/NoCastsFieldTest.php
@@ -0,0 +1,60 @@
+setUpDatabase($this->app, 'string');
+
+ $this->field = Enum::make('Enum')->attach(StringEnum::class);
+
+ $this->model = NoCastsModel::create(['enum' => StringEnum::Moderator]);
+ }
+
+ /** @test */
+ public function it_resolves_enum_value()
+ {
+ $this->field->resolve($this->model);
+
+ $this->assertSame(StringEnum::Moderator, $this->field->value);
+ }
+
+ /** @test */
+ public function it_displays_enum_description()
+ {
+ $this->field->resolveForDisplay($this->model);
+
+ $this->assertSame(StringEnum::Moderator, $this->field->value);
+ }
+
+ /** @test */
+ public function it_fills_database_with_enum_value()
+ {
+ $request = new NovaRequest();
+ $request->query->add(['enum' => StringEnum::Subscriber]);
+
+ $this->field->fill($request, $this->model);
+
+ $this->assertDatabaseHas('example_models', ['enum' => StringEnum::Moderator]);
+
+ $this->model->save();
+
+ $this->assertDatabaseHas('example_models', ['enum' => StringEnum::Subscriber]);
+
+ $this->assertDatabaseMissing('example_models', ['enum' => StringEnum::Moderator]);
+ }
+}
diff --git a/tests/Fields/StringFieldTest.php b/tests/Fields/StringFieldTest.php
new file mode 100644
index 0000000..4ade608
--- /dev/null
+++ b/tests/Fields/StringFieldTest.php
@@ -0,0 +1,60 @@
+setUpDatabase($this->app, 'string');
+
+ $this->field = Enum::make('Enum')->attach(StringEnum::class);
+
+ $this->model = StringModel::create(['enum' => StringEnum::Moderator]);
+ }
+
+ /** @test */
+ public function it_resolves_enum_value()
+ {
+ $this->field->resolve($this->model);
+
+ $this->assertSame(StringEnum::Moderator, $this->field->value);
+ }
+
+ /** @test */
+ public function it_displays_enum_description()
+ {
+ $this->field->resolveForDisplay($this->model);
+
+ $this->assertSame(StringEnum::Moderator()->description, $this->field->value);
+ }
+
+ /** @test */
+ public function it_fills_database_with_enum_value()
+ {
+ $request = new NovaRequest();
+ $request->query->add(['enum' => StringEnum::Subscriber]);
+
+ $this->field->fill($request, $this->model);
+
+ $this->assertDatabaseHas('example_models', ['enum' => StringEnum::Moderator]);
+
+ $this->model->save();
+
+ $this->assertDatabaseHas('example_models', ['enum' => StringEnum::Subscriber]);
+
+ $this->assertDatabaseMissing('example_models', ['enum' => StringEnum::Moderator]);
+ }
+}
diff --git a/tests/Filters/BooleanFilterTest.php b/tests/Filters/BooleanFilterTest.php
new file mode 100644
index 0000000..3bcc989
--- /dev/null
+++ b/tests/Filters/BooleanFilterTest.php
@@ -0,0 +1,42 @@
+filter = new EnumBooleanFilter('enum', IntegerEnum::class);
+
+ $this->mockFilter = new MockFilter($this->filter);
+ }
+
+ /** @test */
+ public function it_is_a_boolean_filter()
+ {
+ $this->mockFilter->assertBooleanFilter();
+ }
+
+ /** @test */
+ public function it_has_a_default_name()
+ {
+ $this->assertEquals('Enum', $this->filter->name());
+ }
+
+ /** @test */
+ public function it_can_have_a_different_name()
+ {
+ $this->assertInstanceOf(EnumBooleanFilter::class, $this->filter->name('Different name'));
+
+ $this->assertEquals('Different name', $this->filter->name());
+ }
+}
diff --git a/tests/Filters/FilterTest.php b/tests/Filters/FilterTest.php
new file mode 100644
index 0000000..1d16ad9
--- /dev/null
+++ b/tests/Filters/FilterTest.php
@@ -0,0 +1,42 @@
+filter = new EnumFilter('enum', IntegerEnum::class);
+
+ $this->mockFilter = new MockFilter($this->filter);
+ }
+
+ /** @test */
+ public function it_is_a_select_filter()
+ {
+ $this->mockFilter->assertSelectFilter();
+ }
+
+ /** @test */
+ public function it_has_a_default_name()
+ {
+ $this->assertEquals('Enum', $this->filter->name());
+ }
+
+ /** @test */
+ public function it_can_have_a_different_name()
+ {
+ $this->assertInstanceOf(EnumFilter::class, $this->filter->name('Different name'));
+
+ $this->assertEquals('Different name', $this->filter->name());
+ }
+}
diff --git a/tests/Filters/FlaggedBooleanFilterTest.php b/tests/Filters/FlaggedBooleanFilterTest.php
new file mode 100644
index 0000000..0b3ca7f
--- /dev/null
+++ b/tests/Filters/FlaggedBooleanFilterTest.php
@@ -0,0 +1,143 @@
+setUpDatabase($this->app);
+
+ $this->filter = new EnumBooleanFilter('enum', FlaggedEnum::class);
+
+ $this->mockFilter = new MockFilter($this->filter);
+
+ $this->models[0] = FlaggedModel::create(['enum' => FlaggedEnum::None]);
+
+ $this->models[1] = FlaggedModel::create(['enum' => FlaggedEnum::ReadComments]);
+
+ $this->models[2] = FlaggedModel::create([
+ 'enum' => array_sum([
+ FlaggedEnum::ReadComments,
+ FlaggedEnum::WriteComments,
+ ]),
+ ]);
+
+ $this->results = [
+ FlaggedEnum::None => [0],
+ FlaggedEnum::ReadComments => [1, 2],
+ FlaggedEnum::WriteComments => [2],
+ FlaggedEnum::EditComments => [],
+ ];
+ }
+
+ private function getOptions(array $keys, array $options = [[]]): array
+ {
+ if (empty($keys)) {
+ return $options;
+ }
+
+ $current = array_shift($keys);
+ $newOptions = [];
+
+ foreach ($options as $option) {
+ $newOptions[] = $option + [$current => true];
+ $newOptions[] = $option + [$current => false];
+ }
+
+ return $this->getOptions($keys, $newOptions);
+ }
+
+ /** @test */
+ public function it_contains_all_the_filter_values()
+ {
+ foreach (array_keys($this->results) as $enum) {
+ $this->mockFilter->assertHasOption($enum);
+ }
+ }
+
+ /** @test */
+ public function it_can_filter_by_all_selected_options()
+ {
+ $this->filter->filterAllFlags();
+
+ foreach (array_keys($this->results) as $enum) {
+ if ($enum === FlaggedEnum::None) {
+ $this->mockFilter->assertOptionMissing($enum);
+
+ continue;
+ }
+
+ $this->mockFilter->assertHasOption($enum);
+ }
+ }
+
+ /** @test */
+ public function it_returns_the_correct_results_when_filtering_by_any_flag()
+ {
+ foreach ($options = $this->getOptions(FlaggedEnum::getValues()) as $option) {
+ $response = $this->mockFilter->apply(IntegerModel::class, $option);
+
+ // None selected should show all models
+ if (count(array_filter($option)) === 0) {
+ $models = array_keys($this->models);
+ } else {
+ $models = array_unique(array_merge(...array_intersect_key($this->results, array_filter($option))));
+ }
+
+ $response->assertCount(count($models));
+
+ foreach ($models as $contain) {
+ $response->assertContains($this->models[$contain]);
+ }
+
+ foreach (array_diff(array_keys($this->models), $models) as $missing) {
+ $response->assertMissing($this->models[$missing]);
+ }
+ }
+ }
+
+ /** @test */
+ public function it_returns_the_correct_results_when_filtering_by_all_flags()
+ {
+ $this->filter->filterAllFlags();
+
+ foreach ($options = $this->getOptions(array_diff(FlaggedEnum::getValues(), [FlaggedEnum::None])) as $option) {
+ $response = $this->mockFilter->apply(IntegerModel::class, $option);
+
+ // None selected should show all models
+ if (count(array_filter($option)) === 0) {
+ $models = array_keys($this->models);
+ } else {
+ $models = array_intersect(array_keys($this->models), ...array_intersect_key($this->results, array_filter($option)));
+ }
+
+ $response->assertCount(count($models));
+
+ foreach ($models as $contain) {
+ $response->assertContains($this->models[$contain]);
+ }
+
+ foreach (array_diff(array_keys($this->models), $models) as $missing) {
+ $response->assertMissing($this->models[$missing]);
+ }
+ }
+ }
+}
diff --git a/tests/Filters/FlaggedFilterTest.php b/tests/Filters/FlaggedFilterTest.php
new file mode 100644
index 0000000..7d185eb
--- /dev/null
+++ b/tests/Filters/FlaggedFilterTest.php
@@ -0,0 +1,71 @@
+setUpDatabase($this->app);
+
+ $this->filter = new MockFilter(new EnumFilter('enum', FlaggedEnum::class));
+
+ $this->models[0] = FlaggedModel::create(['enum' => FlaggedEnum::None]);
+
+ $this->models[1] = FlaggedModel::create(['enum' => FlaggedEnum::ReadComments]);
+
+ $this->models[2] = FlaggedModel::create([
+ 'enum' => array_sum([
+ FlaggedEnum::ReadComments,
+ FlaggedEnum::WriteComments,
+ ]),
+ ]);
+
+ $this->results = [
+ FlaggedEnum::None => [0],
+ FlaggedEnum::ReadComments => [1, 2],
+ FlaggedEnum::WriteComments => [2],
+ FlaggedEnum::EditComments => [],
+ ];
+ }
+
+ /** @test */
+ public function it_contains_all_the_filter_values()
+ {
+ foreach (FlaggedEnum::getValues() as $enum) {
+ $this->filter->assertHasOption($enum);
+ }
+ }
+
+ /** @test */
+ public function it_returns_the_correct_results()
+ {
+ foreach ($this->results as $enum => $models) {
+ $response = $this->filter->apply(FlaggedModel::class, $enum);
+
+ $response->assertCount(count($models));
+
+ foreach ($models as $contain) {
+ $response->assertContains($this->models[$contain]);
+ }
+
+ foreach (array_diff(array_keys($this->models), $models) as $missing) {
+ $response->assertMissing($this->models[$missing]);
+ }
+ }
+ }
+}
diff --git a/tests/Filters/IntegerBooleanFilterTest.php b/tests/Filters/IntegerBooleanFilterTest.php
new file mode 100644
index 0000000..f87fa1a
--- /dev/null
+++ b/tests/Filters/IntegerBooleanFilterTest.php
@@ -0,0 +1,89 @@
+setUpDatabase($this->app);
+
+ $this->filter = new MockFilter(new EnumBooleanFilter('enum', IntegerEnum::class));
+
+ $this->models[0] = IntegerModel::create(['enum' => IntegerEnum::Moderator]);
+
+ $this->models[1] = IntegerModel::create(['enum' => IntegerEnum::Moderator]);
+
+ $this->models[2] = IntegerModel::create(['enum' => IntegerEnum::Administrator]);
+
+ $this->results = [
+ IntegerEnum::Moderator => [0, 1],
+ IntegerEnum::Administrator => [2],
+ IntegerEnum::Subscriber => [],
+ ];
+ }
+
+ private function getOptions(array $keys, array $options = [[]]): array
+ {
+ if (empty($keys)) {
+ return $options;
+ }
+
+ $current = array_shift($keys);
+ $newOptions = [];
+
+ foreach ($options as $option) {
+ $newOptions[] = $option + [$current => true];
+ $newOptions[] = $option + [$current => false];
+ }
+
+ return $this->getOptions($keys, $newOptions);
+ }
+
+ /** @test */
+ public function it_contains_all_the_filter_values()
+ {
+ foreach (IntegerEnum::getValues() as $enum) {
+ $this->filter->assertHasOption($enum);
+ }
+ }
+
+ /** @test */
+ public function it_returns_the_correct_results()
+ {
+ foreach ($options = $this->getOptions(IntegerEnum::getValues()) as $option) {
+ $response = $this->filter->apply(IntegerModel::class, $option);
+
+ // None selected should show all models
+ if (count(array_filter($option)) === 0) {
+ $models = array_keys($this->models);
+ } else {
+ $models = array_unique(array_merge(...array_intersect_key($this->results, array_filter($option))));
+ }
+
+ $response->assertCount(count($models));
+
+ foreach ($models as $contain) {
+ $response->assertContains($this->models[$contain]);
+ }
+
+ foreach (array_diff(array_keys($this->models), $models) as $missing) {
+ $response->assertMissing($this->models[$missing]);
+ }
+ }
+ }
+}
diff --git a/tests/Filters/IntegerFilterTest.php b/tests/Filters/IntegerFilterTest.php
new file mode 100644
index 0000000..510775a
--- /dev/null
+++ b/tests/Filters/IntegerFilterTest.php
@@ -0,0 +1,65 @@
+setUpDatabase($this->app);
+
+ $this->filter = new MockFilter(new EnumFilter('enum', IntegerEnum::class));
+
+ $this->models[0] = IntegerModel::create(['enum' => IntegerEnum::Moderator]);
+
+ $this->models[1] = IntegerModel::create(['enum' => IntegerEnum::Moderator]);
+
+ $this->models[2] = IntegerModel::create(['enum' => IntegerEnum::Administrator]);
+
+ $this->results = [
+ IntegerEnum::Moderator => [0, 1],
+ IntegerEnum::Administrator => [2],
+ IntegerEnum::Subscriber => [],
+ ];
+ }
+
+ /** @test */
+ public function it_contains_all_the_filter_values()
+ {
+ foreach (IntegerEnum::getValues() as $enum) {
+ $this->filter->assertHasOption($enum);
+ }
+ }
+
+ /** @test */
+ public function it_returns_the_correct_results()
+ {
+ foreach ($this->results as $enum => $models) {
+ $response = $this->filter->apply(IntegerModel::class, $enum);
+
+ $response->assertCount(count($models));
+
+ foreach ($models as $contain) {
+ $response->assertContains($this->models[$contain]);
+ }
+
+ foreach (array_diff(array_keys($this->models), $models) as $missing) {
+ $response->assertMissing($this->models[$missing]);
+ }
+ }
+ }
+}
diff --git a/tests/Filters/NoCastsBooleanFilterTest.php b/tests/Filters/NoCastsBooleanFilterTest.php
new file mode 100644
index 0000000..cbde178
--- /dev/null
+++ b/tests/Filters/NoCastsBooleanFilterTest.php
@@ -0,0 +1,89 @@
+setUpDatabase($this->app, 'string');
+
+ $this->filter = new MockFilter(new EnumBooleanFilter('enum', StringEnum::class));
+
+ $this->models[0] = NoCastsModel::create(['enum' => StringEnum::Moderator]);
+
+ $this->models[1] = NoCastsModel::create(['enum' => StringEnum::Moderator]);
+
+ $this->models[2] = NoCastsModel::create(['enum' => StringEnum::Administrator]);
+
+ $this->results = [
+ StringEnum::Moderator => [0, 1],
+ StringEnum::Administrator => [2],
+ StringEnum::Subscriber => [],
+ ];
+ }
+
+ private function getOptions(array $keys, array $options = [[]]): array
+ {
+ if (empty($keys)) {
+ return $options;
+ }
+
+ $current = array_shift($keys);
+ $newOptions = [];
+
+ foreach ($options as $option) {
+ $newOptions[] = $option + [$current => true];
+ $newOptions[] = $option + [$current => false];
+ }
+
+ return $this->getOptions($keys, $newOptions);
+ }
+
+ /** @test */
+ public function it_contains_all_the_filter_values()
+ {
+ foreach (StringEnum::getValues() as $enum) {
+ $this->filter->assertHasOption($enum);
+ }
+ }
+
+ /** @test */
+ public function it_returns_the_correct_results()
+ {
+ foreach ($options = $this->getOptions(StringEnum::getValues()) as $option) {
+ $response = $this->filter->apply(NoCastsModel::class, $option);
+
+ // None selected should show all models
+ if (count(array_filter($option)) === 0) {
+ $models = array_keys($this->models);
+ } else {
+ $models = array_unique(array_merge(...array_values(array_intersect_key($this->results, array_filter($option)))));
+ }
+
+ $response->assertCount(count($models));
+
+ foreach ($models as $contain) {
+ $response->assertContains($this->models[$contain]);
+ }
+
+ foreach (array_diff(array_keys($this->models), $models) as $missing) {
+ $response->assertMissing($this->models[$missing]);
+ }
+ }
+ }
+}
diff --git a/tests/Filters/NoCastsFilterTest.php b/tests/Filters/NoCastsFilterTest.php
new file mode 100644
index 0000000..e2b7db6
--- /dev/null
+++ b/tests/Filters/NoCastsFilterTest.php
@@ -0,0 +1,65 @@
+setUpDatabase($this->app, 'string');
+
+ $this->filter = new MockFilter(new EnumFilter('enum', StringEnum::class));
+
+ $this->models[0] = NoCastsModel::create(['enum' => StringEnum::Moderator]);
+
+ $this->models[1] = NoCastsModel::create(['enum' => StringEnum::Moderator]);
+
+ $this->models[2] = NoCastsModel::create(['enum' => StringEnum::Administrator]);
+
+ $this->results = [
+ StringEnum::Moderator => [0, 1],
+ StringEnum::Administrator => [2],
+ StringEnum::Subscriber => [],
+ ];
+ }
+
+ /** @test */
+ public function it_contains_all_the_filter_values()
+ {
+ foreach (StringEnum::getValues() as $enum) {
+ $this->filter->assertHasOption($enum);
+ }
+ }
+
+ /** @test */
+ public function it_returns_the_correct_results()
+ {
+ foreach ($this->results as $enum => $models) {
+ $response = $this->filter->apply(NoCastsModel::class, $enum);
+
+ $response->assertCount(count($models));
+
+ foreach ($models as $contain) {
+ $response->assertContains($this->models[$contain]);
+ }
+
+ foreach (array_diff(array_keys($this->models), $models) as $missing) {
+ $response->assertMissing($this->models[$missing]);
+ }
+ }
+ }
+}
diff --git a/tests/Filters/StringBooleanFilterTest.php b/tests/Filters/StringBooleanFilterTest.php
new file mode 100644
index 0000000..1a81819
--- /dev/null
+++ b/tests/Filters/StringBooleanFilterTest.php
@@ -0,0 +1,89 @@
+setUpDatabase($this->app, 'string');
+
+ $this->filter = new MockFilter(new EnumBooleanFilter('enum', StringEnum::class));
+
+ $this->models[0] = StringModel::create(['enum' => StringEnum::Moderator]);
+
+ $this->models[1] = StringModel::create(['enum' => StringEnum::Moderator]);
+
+ $this->models[2] = StringModel::create(['enum' => StringEnum::Administrator]);
+
+ $this->results = [
+ StringEnum::Moderator => [0, 1],
+ StringEnum::Administrator => [2],
+ StringEnum::Subscriber => [],
+ ];
+ }
+
+ private function getOptions(array $keys, array $options = [[]]): array
+ {
+ if (empty($keys)) {
+ return $options;
+ }
+
+ $current = array_shift($keys);
+ $newOptions = [];
+
+ foreach ($options as $option) {
+ $newOptions[] = $option + [$current => true];
+ $newOptions[] = $option + [$current => false];
+ }
+
+ return $this->getOptions($keys, $newOptions);
+ }
+
+ /** @test */
+ public function it_contains_all_the_filter_values()
+ {
+ foreach (StringEnum::getValues() as $enum) {
+ $this->filter->assertHasOption($enum);
+ }
+ }
+
+ /** @test */
+ public function it_returns_the_correct_results()
+ {
+ foreach ($options = $this->getOptions(StringEnum::getValues()) as $option) {
+ $response = $this->filter->apply(StringModel::class, $option);
+
+ // None selected should show all models
+ if (count(array_filter($option)) === 0) {
+ $models = array_keys($this->models);
+ } else {
+ $models = array_unique(array_merge(...array_values(array_intersect_key($this->results, array_filter($option)))));
+ }
+
+ $response->assertCount(count($models));
+
+ foreach ($models as $contain) {
+ $response->assertContains($this->models[$contain]);
+ }
+
+ foreach (array_diff(array_keys($this->models), $models) as $missing) {
+ $response->assertMissing($this->models[$missing]);
+ }
+ }
+ }
+}
diff --git a/tests/Filters/StringFilterTest.php b/tests/Filters/StringFilterTest.php
new file mode 100644
index 0000000..aaa07b9
--- /dev/null
+++ b/tests/Filters/StringFilterTest.php
@@ -0,0 +1,65 @@
+setUpDatabase($this->app, 'string');
+
+ $this->filter = new MockFilter(new EnumFilter('enum', StringEnum::class));
+
+ $this->models[0] = StringModel::create(['enum' => StringEnum::Moderator]);
+
+ $this->models[1] = StringModel::create(['enum' => StringEnum::Moderator]);
+
+ $this->models[2] = StringModel::create(['enum' => StringEnum::Administrator]);
+
+ $this->results = [
+ StringEnum::Moderator => [0, 1],
+ StringEnum::Administrator => [2],
+ StringEnum::Subscriber => [],
+ ];
+ }
+
+ /** @test */
+ public function it_contains_all_the_filter_values()
+ {
+ foreach (StringEnum::getValues() as $enum) {
+ $this->filter->assertHasOption($enum);
+ }
+ }
+
+ /** @test */
+ public function it_returns_the_correct_results()
+ {
+ foreach ($this->results as $enum => $models) {
+ $response = $this->filter->apply(StringModel::class, $enum);
+
+ $response->assertCount(count($models));
+
+ foreach ($models as $contain) {
+ $response->assertContains($this->models[$contain]);
+ }
+
+ foreach (array_diff(array_keys($this->models), $models) as $missing) {
+ $response->assertMissing($this->models[$missing]);
+ }
+ }
+ }
+}
diff --git a/tests/IntegerEnumTest.php b/tests/IntegerEnumTest.php
deleted file mode 100644
index be8366d..0000000
--- a/tests/IntegerEnumTest.php
+++ /dev/null
@@ -1,79 +0,0 @@
-field = Enum::make('Enum');
-
- $this->field->attachEnum(ExampleIntegerEnum::class);
- }
-
- /** @test */
- public function field_starts_with_zero_config()
- {
- $field = Enum::make('Enum');
-
- $this->assertEmpty($field->meta);
- $this->assertEmpty($field->rules);
- $this->assertNull($field->resolveCallback);
- $this->assertNull($field->displayCallback);
- }
-
- /** @test */
- public function an_enum_can_be_attached_to_the_field()
- {
- $this->assertArrayHasKey('options', $this->field->meta);
-
- $this->assertEquals([
- [
- 'label' => 'Administrator',
- 'value' => 0,
- ],
- [
- 'label' => 'Moderator',
- 'value' => 1,
- ],
- [
- 'label' => 'Subscriber',
- 'value' => 2,
- ],
- ], $this->field->meta['options']);
- }
-
- /** @test */
- public function attaching_an_enum_adds_correct_rules()
- {
- $this->assertContains('required', $this->field->rules);
-
- $this->assertContainsEquals(new EnumValue(ExampleIntegerEnum::class, false), $this->field->rules);
- }
-
- /** @test */
- public function field_resolves_correct_value()
- {
- $this->field->resolve(['enum' => ExampleIntegerEnum::Moderator()]);
-
- $this->assertSame(1, $this->field->value);
- }
-
- /** @test */
- public function field_displays_correct_description()
- {
- $this->field->resolveForDisplay(['enum' => ExampleIntegerEnum::Moderator()]);
-
- $this->assertSame('Moderator', $this->field->value);
- }
-}
diff --git a/tests/ModelTest.php b/tests/ModelTest.php
deleted file mode 100644
index 135aa4f..0000000
--- a/tests/ModelTest.php
+++ /dev/null
@@ -1,57 +0,0 @@
-model = ExampleModel::create(['enum' => ExampleIntegerEnum::Moderator()]);
- }
-
- /** @test */
- public function field_resolves_correct_value()
- {
- $field = Enum::make('Enum')->attachEnum(ExampleIntegerEnum::class);
-
- $field->resolve($this->model);
-
- $this->assertSame(1, $field->value);
- }
-
- /** @test */
- public function field_displays_correct_description()
- {
- $field = Enum::make('Enum')->attachEnum(ExampleIntegerEnum::class);
-
- $field->resolveForDisplay($this->model);
-
- $this->assertSame('Moderator', $field->value);
- }
-
- /** @test */
- public function field_fills_database_with_enum_value()
- {
- $field = Enum::make('Enum')->attachEnum(ExampleIntegerEnum::class);
-
- $request = new NovaRequest();
- $request->query->add(['enum' => ExampleIntegerEnum::Subscriber()]);
-
- $field->fill($request, $this->model);
-
- $this->model->save();
-
- $this->assertDatabaseHas('example_models', ['enum' => 2]);
- $this->assertDatabaseMissing('example_models', ['enum' => 1]);
- }
-}
diff --git a/tests/StringEnumTest.php b/tests/StringEnumTest.php
deleted file mode 100644
index 25ab8d6..0000000
--- a/tests/StringEnumTest.php
+++ /dev/null
@@ -1,38 +0,0 @@
-field = Enum::make('Enum');
-
- $this->field->attachEnum(ExampleStringEnum::class);
- }
-
- /** @test */
- public function field_resolves_correct_value()
- {
- $this->field->resolve(['enum' => ExampleStringEnum::Moderator()]);
-
- $this->assertSame('moderator', $this->field->value);
- }
-
- /** @test */
- public function field_displays_correct_description()
- {
- $this->field->resolveForDisplay(['enum' => ExampleStringEnum::Moderator()]);
-
- $this->assertSame('Moderator', $this->field->value);
- }
-}
diff --git a/tests/TestCase.php b/tests/TestCase.php
index 4187242..407fdb3 100644
--- a/tests/TestCase.php
+++ b/tests/TestCase.php
@@ -3,27 +3,25 @@
namespace SimpleSquid\Nova\Fields\Enum\Tests;
use Illuminate\Database\Schema\Blueprint;
+use Laravel\Nova\NovaServiceProvider;
use Orchestra\Testbench\TestCase as Orchestra;
abstract class TestCase extends Orchestra
{
- protected function setUp(): void
+ protected function getPackageProviders($app)
{
- parent::setUp();
-
- $this->setUpDatabase($this->app);
+ return [
+ NovaServiceProvider::class,
+ ];
}
- /**
- * @param \Illuminate\Foundation\Application $app
- */
- protected function setUpDatabase($app)
+ protected function setUpDatabase($app, $type = 'integer')
{
$this->artisan('migrate:fresh');
- $app['db']->connection()->getSchemaBuilder()->create('example_models', function (Blueprint $table) {
+ $app['db']->connection()->getSchemaBuilder()->create('example_models', function (Blueprint $table) use ($type) {
$table->increments('id');
- $table->integer('enum');
+ $table->$type('enum');
});
}
}