diff --git a/CHANGELOG.md b/CHANGELOG.md index 763b12e..40edc5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to `laravel-eloquent-flag` will be documented in this file. +## 3.3.0 - 2017-01-12 + +### Added + +- `verified_at` classic timestamp flag added. + ## 3.2.0 - 2017-01-12 ### Added diff --git a/README.md b/README.md index b34b894..35fd96e 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,8 @@ Eloquent boolean & timestamp flagged attributes behavior. Enhance eloquent model | `HasKeptFlag` | Classic | `is_kept` | Boolean | - | | `HasPublishedAt` | Classic | `published_at` | Timestamp | `HasPublishedFlag` | | `HasPublishedFlag` | Classic | `is_published` | Boolean | `HasPublishedAt` | -| `HasVerifiedFlag` | Classic | `is_verified` | Boolean | - | +| `HasVerifiedAt` | Classic | `verified_at` | Timestamp | `HasVerifiedFlag` | +| `HasVerifiedFlag` | Classic | `is_verified` | Boolean | `HasVerifiedAt` | Any entity can has more than one flag at the same time. If flags can't work for the same entity simultaneously they are listed in `Conflict` column. @@ -353,6 +354,8 @@ Post::where('id', 4)->unpublish(); ### Setup a verifiable model +#### With boolean flag + ```php + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Cog\Flag\Scopes\Classic; + +use Carbon\Carbon; +use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Scope; + +/** + * Class VerifiedAtScope. + * + * @package Cog\Flag\Scopes\Classic + */ +class VerifiedAtScope implements Scope +{ + /** + * All of the extensions to be added to the builder. + * + * @var array + */ + protected $extensions = ['Verify', 'Unverify', 'WithUnverified', 'WithoutUnverified', 'OnlyUnverified']; + + /** + * Apply the scope to a given Eloquent query builder. + * + * @param \Illuminate\Database\Eloquent\Builder $builder + * @param \Illuminate\Database\Eloquent\Model $model + * @return \Illuminate\Database\Eloquent\Builder + */ + public function apply(Builder $builder, Model $model) + { + return $builder->whereNotNull('verified_at'); + } + + /** + * Extend the query builder with the needed functions. + * + * @param \Illuminate\Database\Eloquent\Builder $builder + * @return void + */ + public function extend(Builder $builder) + { + foreach ($this->extensions as $extension) { + $this->{"add{$extension}"}($builder); + } + } + + /** + * Add the `verify` extension to the builder. + * + * @param \Illuminate\Database\Eloquent\Builder $builder + * @return void + */ + protected function addVerify(Builder $builder) + { + $builder->macro('verify', function (Builder $builder) { + $builder->withUnverified(); + + return $builder->update(['verified_at' => Carbon::now()]); + }); + } + + /** + * Add the `unverify` extension to the builder. + * + * @param \Illuminate\Database\Eloquent\Builder $builder + * @return void + */ + protected function addUnverify(Builder $builder) + { + $builder->macro('unverify', function (Builder $builder) { + return $builder->update(['verified_at' => null]); + }); + } + + /** + * Add the `withUnverified` extension to the builder. + * + * @param \Illuminate\Database\Eloquent\Builder $builder + * @return void + */ + protected function addWithUnverified(Builder $builder) + { + $builder->macro('withUnverified', function (Builder $builder) { + return $builder->withoutGlobalScope($this); + }); + } + + /** + * Add the `withoutUnverified` extension to the builder. + * + * @param \Illuminate\Database\Eloquent\Builder $builder + * @return void + */ + protected function addWithoutUnverified(Builder $builder) + { + $builder->macro('withoutUnverified', function (Builder $builder) { + return $builder->withoutGlobalScope($this)->whereNotNull('verified_at'); + }); + } + + /** + * Add the `onlyUnverified` extension to the builder. + * + * @param \Illuminate\Database\Eloquent\Builder $builder + * @return void + */ + protected function addOnlyUnverified(Builder $builder) + { + $builder->macro('onlyUnverified', function (Builder $builder) { + return $builder->withoutGlobalScope($this)->whereNull('verified_at'); + }); + } +} diff --git a/src/Traits/Classic/HasActiveFlagScope.php b/src/Traits/Classic/HasActiveFlagScope.php index d674960..d26b729 100644 --- a/src/Traits/Classic/HasActiveFlagScope.php +++ b/src/Traits/Classic/HasActiveFlagScope.php @@ -21,7 +21,7 @@ trait HasActiveFlagScope { /** - * Boot the HasKeptFlagScope trait for a model. + * Boot the HasActiveFlagScope trait for a model. * * @return void */ diff --git a/src/Traits/Classic/HasVerifiedAt.php b/src/Traits/Classic/HasVerifiedAt.php new file mode 100644 index 0000000..9d57793 --- /dev/null +++ b/src/Traits/Classic/HasVerifiedAt.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Cog\Flag\Traits\Classic; + +/** + * Class HasVerifiedAt. + * + * @package Cog\Flag\Traits\Classic + */ +trait HasVerifiedAt +{ + use HasVerifiedAtHelpers, + HasVerifiedAtScope; +} diff --git a/src/Traits/Classic/HasVerifiedAtHelpers.php b/src/Traits/Classic/HasVerifiedAtHelpers.php new file mode 100644 index 0000000..bf11238 --- /dev/null +++ b/src/Traits/Classic/HasVerifiedAtHelpers.php @@ -0,0 +1,90 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Cog\Flag\Traits\Classic; + +use Carbon\Carbon; + +/** + * Class HasVerifiedAtHelpers. + * + * @package Cog\Flag\Traits\Classic + */ +trait HasVerifiedAtHelpers +{ + /** + * Set verified flag. + * + * @return static + */ + public function setVerifiedFlag() + { + $this->verified_at = Carbon::now(); + + return $this; + } + + /** + * Unset verified flag. + * + * @return static + */ + public function unsetVerifiedFlag() + { + $this->verified_at = null; + + return $this; + } + + /** + * If entity is verified. + * + * @return bool + */ + public function isVerified() + { + return !is_null($this->verified_at); + } + + /** + * If entity is unverified. + * + * @return bool + */ + public function isUnverified() + { + return !$this->isVerified(); + } + + /** + * Mark entity as verified. + * + * @return void + */ + public function verify() + { + $this->setVerifiedFlag()->save(); + + // :TODO: Fire an event here + } + + /** + * Mark entity as rejected. + * + * @return void + */ + public function unverify() + { + $this->unsetVerifiedFlag()->save(); + + // :TODO: Fire an event here + } +} diff --git a/src/Traits/Classic/HasVerifiedAtScope.php b/src/Traits/Classic/HasVerifiedAtScope.php new file mode 100644 index 0000000..1a0b4ec --- /dev/null +++ b/src/Traits/Classic/HasVerifiedAtScope.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Cog\Flag\Traits\Classic; + +use Cog\Flag\Scopes\Classic\VerifiedAtScope; + +/** + * Class HasVerifiedAtScope. + * + * @package Cog\Flag\Traits\Classic + */ +trait HasVerifiedAtScope +{ + /** + * Boot the HasVerifiedAtScope for a model. + * + * @return void + */ + public static function bootHasVerifiedAtScope() + { + static::addGlobalScope(new VerifiedAtScope); + } +} diff --git a/src/Traits/Classic/HasVerifiedFlagHelpers.php b/src/Traits/Classic/HasVerifiedFlagHelpers.php index 9a911f4..56c21ad 100644 --- a/src/Traits/Classic/HasVerifiedFlagHelpers.php +++ b/src/Traits/Classic/HasVerifiedFlagHelpers.php @@ -18,5 +18,71 @@ */ trait HasVerifiedFlagHelpers { - // + /** + * Set verified flag. + * + * @return static + */ + public function setVerifiedFlag() + { + $this->is_verified = true; + + return $this; + } + + /** + * Unset verified flag. + * + * @return static + */ + public function unsetVerifiedFlag() + { + $this->is_verified = false; + + return $this; + } + + /** + * If entity is verified. + * + * @return bool + */ + public function isVerified() + { + return boolval($this->is_verified); + } + + /** + * If entity is unverified. + * + * @return bool + */ + public function isUnverified() + { + return !$this->isVerified(); + } + + /** + * Mark entity as verified. + * + * @return void + */ + public function verify() + { + $this->setVerifiedFlag()->save(); + + // :TODO: Fire an event here + } + + /** + * Mark entity as rejected. + * + * @return void + */ + public function unverify() + { + $this->unsetVerifiedFlag()->save(); + + // :TODO: Fire an event here + } } diff --git a/src/Traits/Classic/HasVerifiedFlagScope.php b/src/Traits/Classic/HasVerifiedFlagScope.php index b88b2ca..8f351de 100644 --- a/src/Traits/Classic/HasVerifiedFlagScope.php +++ b/src/Traits/Classic/HasVerifiedFlagScope.php @@ -21,7 +21,7 @@ trait HasVerifiedFlagScope { /** - * Boot the HasVerifiedTraitScope for a model. + * Boot the HasVerifiedFlagScope for a model. * * @return void */ diff --git a/tests/database/factories/EntityWithVerifiedAtFactory.php b/tests/database/factories/EntityWithVerifiedAtFactory.php new file mode 100644 index 0000000..f9504d5 --- /dev/null +++ b/tests/database/factories/EntityWithVerifiedAtFactory.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +$factory->define(\Cog\Flag\Tests\Stubs\Models\Classic\EntityWithVerifiedAt::class, function (\Faker\Generator $faker) { + return [ + 'name' => $faker->word, + 'verified_at' => null, + ]; +}); diff --git a/tests/database/migrations/2016_09_25_182750_create_entity_with_verified_at_table.php b/tests/database/migrations/2016_09_25_182750_create_entity_with_verified_at_table.php new file mode 100644 index 0000000..367f066 --- /dev/null +++ b/tests/database/migrations/2016_09_25_182750_create_entity_with_verified_at_table.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Illuminate\Database\Migrations\Migration; +use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; + +/** + * Class CreateEntityWithVerifiedAtTable. + */ +class CreateEntityWithVerifiedAtTable extends Migration +{ + /** + * Run the migrations. + * + * @return void + */ + public function up() + { + Schema::create('entity_with_verified_at', function (Blueprint $table) { + $table->increments('id'); + $table->string('name'); + $table->timestamp('verified_at')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('entity_with_verified_at'); + } +} diff --git a/tests/stubs/Models/Classic/EntityWithVerifiedAt.php b/tests/stubs/Models/Classic/EntityWithVerifiedAt.php new file mode 100644 index 0000000..4801f0d --- /dev/null +++ b/tests/stubs/Models/Classic/EntityWithVerifiedAt.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Cog\Flag\Tests\Stubs\Models\Classic; + +use Cog\Flag\Traits\Classic\HasVerifiedAt; +use Illuminate\Database\Eloquent\Model; + +/** + * Class EntityWithVerifiedAt. + * + * @package Cog\Flag\Tests\Stubs\Models\Classic + */ +class EntityWithVerifiedAt extends Model +{ + use HasVerifiedAt; + + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'entity_with_verified_at'; + + /** + * The attributes that are mass assignable. + * + * @var array + */ + protected $fillable = [ + 'name', + ]; + + /** + * The attributes that should be cast to native types. + * + * @var array + */ + protected $casts = [ + 'verified_at' => 'datetime', + ]; +} diff --git a/tests/unit/Scopes/Classic/VerifiedAtScopeTest.php b/tests/unit/Scopes/Classic/VerifiedAtScopeTest.php new file mode 100644 index 0000000..a93473f --- /dev/null +++ b/tests/unit/Scopes/Classic/VerifiedAtScopeTest.php @@ -0,0 +1,112 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Cog\Flag\Tests\Unit\Scopes\Classic; + +use Carbon\Carbon; +use Cog\Flag\Tests\Stubs\Models\Classic\EntityWithVerifiedAt; +use Cog\Flag\Tests\TestCase; + +/** + * Class VerifiedAtScopeTest. + * + * @package Cog\Flag\Tests\Unit\Scopes\Classic + */ +class VerifiedAtScopeTest extends TestCase +{ + /** @test */ + public function it_can_get_only_verified() + { + factory(EntityWithVerifiedAt::class, 3)->create([ + 'verified_at' => Carbon::now()->subDay(), + ]); + factory(EntityWithVerifiedAt::class, 2)->create([ + 'verified_at' => null, + ]); + + $entities = EntityWithVerifiedAt::all(); + + $this->assertCount(3, $entities); + } + + /** @test */ + public function it_can_get_without_unverified() + { + factory(EntityWithVerifiedAt::class, 3)->create([ + 'verified_at' => Carbon::now()->subDay(), + ]); + factory(EntityWithVerifiedAt::class, 2)->create([ + 'verified_at' => null, + ]); + + $entities = EntityWithVerifiedAt::withoutUnverified()->get(); + + $this->assertCount(3, $entities); + } + + /** @test */ + public function it_can_get_with_unverified() + { + factory(EntityWithVerifiedAt::class, 3)->create([ + 'verified_at' => Carbon::now()->subDay(), + ]); + factory(EntityWithVerifiedAt::class, 2)->create([ + 'verified_at' => null, + ]); + + $entities = EntityWithVerifiedAt::withUnverified()->get(); + + $this->assertCount(5, $entities); + } + + /** @test */ + public function it_can_get_only_unverified() + { + factory(EntityWithVerifiedAt::class, 3)->create([ + 'verified_at' => Carbon::now()->subDay(), + ]); + factory(EntityWithVerifiedAt::class, 2)->create([ + 'verified_at' => null, + ]); + + $entities = EntityWithVerifiedAt::onlyUnverified()->get(); + + $this->assertCount(2, $entities); + } + + /** @test */ + public function it_can_verify_model() + { + $model = factory(EntityWithVerifiedAt::class)->create([ + 'verified_at' => null, + ]); + + EntityWithVerifiedAt::where('id', $model->id)->verify(); + + $model = EntityWithVerifiedAt::where('id', $model->id)->first(); + + $this->assertNotNull($model->verified_at); + } + + /** @test */ + public function it_can_unverify_model() + { + $model = factory(EntityWithVerifiedAt::class)->create([ + 'verified_at' => Carbon::now()->subDay(), + ]); + + EntityWithVerifiedAt::where('id', $model->id)->unverify(); + + $model = EntityWithVerifiedAt::withUnverified()->where('id', $model->id)->first(); + + $this->assertNull($model->verified_at); + } +} diff --git a/tests/unit/Scopes/Classic/VerifiedFlagScopeTest.php b/tests/unit/Scopes/Classic/VerifiedFlagScopeTest.php index 5940bba..5f958fd 100644 --- a/tests/unit/Scopes/Classic/VerifiedFlagScopeTest.php +++ b/tests/unit/Scopes/Classic/VerifiedFlagScopeTest.php @@ -82,7 +82,7 @@ public function it_can_get_only_unverified() } /** @test */ - public function it_can_publish_model() + public function it_can_verify_model() { $model = factory(EntityWithVerifiedFlag::class)->create([ 'is_verified' => false, diff --git a/tests/unit/Traits/Classic/HasVerifiedAtHelpersTest.php b/tests/unit/Traits/Classic/HasVerifiedAtHelpersTest.php new file mode 100644 index 0000000..e8213c7 --- /dev/null +++ b/tests/unit/Traits/Classic/HasVerifiedAtHelpersTest.php @@ -0,0 +1,102 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Cog\Flag\Tests\Unit\Traits\Classic; + +use Carbon\Carbon; +use Cog\Flag\Tests\Stubs\Models\Classic\EntityWithVerifiedAt; +use Cog\Flag\Tests\TestCase; + +/** + * Class HasVerifiedAtHelpersTest. + * + * @package Cog\Flag\Tests\Unit\Traits\Classic + */ +class HasVerifiedAtHelpersTest extends TestCase +{ + /** @test */ + public function it_can_set_verified_flag() + { + $entity = factory(EntityWithVerifiedAt::class, 1)->create([ + 'verified_at' => null, + ]); + + $entity->setVerifiedFlag(); + + $this->assertNotNull($entity->verified_at); + } + + /** @test */ + public function it_can_unset_verified_flag() + { + $entity = factory(EntityWithVerifiedAt::class, 1)->create([ + 'verified_at' => Carbon::now(), + ]); + + $entity->unsetVerifiedFlag(); + + $this->assertNull($entity->verified_at); + } + + /** @test */ + public function it_can_check_if_entity_is_verified() + { + $verifiedEntity = factory(EntityWithVerifiedAt::class, 1)->create([ + 'verified_at' => Carbon::now(), + ]); + + $unverifiedEntity = factory(EntityWithVerifiedAt::class, 1)->create([ + 'verified_at' => null, + ]); + + $this->assertTrue($verifiedEntity->isVerified()); + $this->assertFalse($unverifiedEntity->isVerified()); + } + + /** @test */ + public function it_can_check_if_entity_is_unverified() + { + $verifiedEntity = factory(EntityWithVerifiedAt::class, 1)->create([ + 'verified_at' => Carbon::now(), + ]); + + $unverifiedEntity = factory(EntityWithVerifiedAt::class, 1)->create([ + 'verified_at' => null, + ]); + + $this->assertFalse($verifiedEntity->isUnverified()); + $this->assertTrue($unverifiedEntity->isUnverified()); + } + + /** @test */ + public function it_can_verify_entity() + { + $entity = factory(EntityWithVerifiedAt::class, 1)->create([ + 'verified_at' => null, + ]); + + $entity->verify(); + + $this->assertNotNull($entity->verified_at); + } + + /** @test */ + public function it_can_unverify_entity() + { + $entity = factory(EntityWithVerifiedAt::class, 1)->create([ + 'verified_at' => Carbon::now(), + ]); + + $entity->unverify(); + + $this->assertNull($entity->verified_at); + } +} diff --git a/tests/unit/Traits/Classic/HasVerifiedFlagHelpersTest.php b/tests/unit/Traits/Classic/HasVerifiedFlagHelpersTest.php new file mode 100644 index 0000000..cf73cd9 --- /dev/null +++ b/tests/unit/Traits/Classic/HasVerifiedFlagHelpersTest.php @@ -0,0 +1,101 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Cog\Flag\Tests\Unit\Traits\Classic; + +use Cog\Flag\Tests\Stubs\Models\Classic\EntityWithVerifiedFlag; +use Cog\Flag\Tests\TestCase; + +/** + * Class HasVerifiedFlagHelpersTest. + * + * @package Cog\Flag\Tests\Unit\Traits\Classic + */ +class HasVerifiedFlagHelpersTest extends TestCase +{ + /** @test */ + public function it_can_set_verified_flag() + { + $entity = factory(EntityWithVerifiedFlag::class, 1)->create([ + 'is_verified' => false, + ]); + + $entity->setVerifiedFlag(); + + $this->assertTrue($entity->is_verified); + } + + /** @test */ + public function it_can_unset_verified_flag() + { + $entity = factory(EntityWithVerifiedFlag::class, 1)->create([ + 'is_verified' => true, + ]); + + $entity->unsetVerifiedFlag(); + + $this->assertFalse($entity->is_verified); + } + + /** @test */ + public function it_can_check_if_entity_is_verified() + { + $verifiedEntity = factory(EntityWithVerifiedFlag::class, 1)->create([ + 'is_verified' => true, + ]); + + $unverifiedEntity = factory(EntityWithVerifiedFlag::class, 1)->create([ + 'is_verified' => false, + ]); + + $this->assertTrue($verifiedEntity->isVerified()); + $this->assertFalse($unverifiedEntity->isVerified()); + } + + /** @test */ + public function it_can_check_if_entity_is_unverified() + { + $verifiedEntity = factory(EntityWithVerifiedFlag::class, 1)->create([ + 'is_verified' => true, + ]); + + $unverifiedEntity = factory(EntityWithVerifiedFlag::class, 1)->create([ + 'is_verified' => false, + ]); + + $this->assertFalse($verifiedEntity->isUnverified()); + $this->assertTrue($unverifiedEntity->isUnverified()); + } + + /** @test */ + public function it_can_verify_entity() + { + $entity = factory(EntityWithVerifiedFlag::class, 1)->create([ + 'is_verified' => false, + ]); + + $entity->verify(); + + $this->assertTrue($entity->is_verified); + } + + /** @test */ + public function it_can_unverify_entity() + { + $entity = factory(EntityWithVerifiedFlag::class, 1)->create([ + 'is_verified' => true, + ]); + + $entity->unverify(); + + $this->assertFalse($entity->is_verified); + } +}