Skip to content

Commit

Permalink
add entity compatible SluggableBehavior with tests
Browse files Browse the repository at this point in the history
  • Loading branch information
albertborsos committed Aug 27, 2019
1 parent 1e77fc7 commit 6b743d3
Show file tree
Hide file tree
Showing 7 changed files with 261 additions and 1 deletion.
135 changes: 135 additions & 0 deletions src/behaviors/SluggableBehavior.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
<?php

namespace albertborsos\ddd\behaviors;

use albertborsos\ddd\base\EntityEvent;
use albertborsos\ddd\interfaces\ActiveRepositoryInterface;
use albertborsos\ddd\interfaces\EntityInterface;
use albertborsos\ddd\interfaces\RepositoryInterface;
use albertborsos\ddd\traits\EvaluateAttributesTrait;
use Yii;
use yii\base\InvalidConfigException;
use yii\db\BaseActiveRecord;
use yii\helpers\ArrayHelper;
use yii\validators\UniqueValidator;

class SluggableBehavior extends \yii\behaviors\SluggableBehavior
{
use EvaluateAttributesTrait;

/** @var ActiveRepositoryInterface */
public $repository;

public function init()
{
if (empty($this->attributes)) {
$this->attributes = [
EntityInterface::EVENT_BEFORE_INSERT => $this->slugAttribute,
EntityInterface::EVENT_BEFORE_UPDATE => $this->slugAttribute,
];
}

if ($this->attribute === null && $this->value === null) {
throw new InvalidConfigException('Either "attribute" or "value" property must be specified.');
}

if ($this->ensureUnique && empty($this->repository)) {
throw new InvalidConfigException(get_called_class() . '::$repository must be set!');
}

if ($this->repository) {
/** @var ActiveRepositoryInterface $repository */
$repository = \Yii::createObject($this->repository);
$this->setRepository($repository);
}
}

/**
* @return ActiveRepositoryInterface
*/
protected function getRepository()
{
return $this->repository;
}

/**
* @param ActiveRepositoryInterface $repository
*/
protected function setRepository(ActiveRepositoryInterface $repository): void
{
$this->repository = $repository;
}

/**
* {@inheritdoc}
*/
protected function getValue($event)
{
if (!$this->isNewSlugNeededByEvent($event)) {
return $this->owner->{$this->slugAttribute};
}

if ($this->attribute !== null) {
$slugParts = [];
foreach ((array)$this->attribute as $attribute) {
$part = ArrayHelper::getValue($this->owner, $attribute);
if ($this->skipOnEmpty && $this->isEmpty($part)) {
return $this->owner->{$this->slugAttribute};
}
$slugParts[] = $part;
}
$slug = $this->generateSlug($slugParts);
} else {
$slug = parent::getValue($event);
}

return $this->ensureUnique ? $this->makeUnique($slug) : $slug;
}

protected function isNewSlugNeededByEvent(EntityEvent $event)
{
if (empty($this->owner->{$this->slugAttribute})) {
return true;
}

if ($this->immutable) {
return false;
}

if ($this->attribute === null) {
return true;
}

foreach ((array)$this->attribute as $attribute) {
if (in_array($attribute, array_keys($event->dirtyAttributes))) {
return true;
}
}

return false;
}

/**
* Checks if given slug value is unique.
* @param string $slug slug value
* @return bool whether slug is unique.
*/
protected function validateSlug($slug)
{
/* @var $validator UniqueValidator */
/* @var $model BaseActiveRecord */
$validator = Yii::createObject(array_merge(
[
'class' => UniqueValidator::className(),
],
$this->uniqueValidator
));

$model = Yii::createObject($this->repository->getDataModelClass());
$model->clearErrors();
$model->{$this->slugAttribute} = $slug;

$validator->validateAttribute($model, $this->slugAttribute);
return !$model->hasErrors();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
namespace albertborsos\ddd\tests\support\base\domains\customer\entities;

use albertborsos\ddd\behaviors\BlameableBehavior;
use albertborsos\ddd\behaviors\SluggableBehavior;
use albertborsos\ddd\behaviors\TimestampBehavior;
use albertborsos\ddd\models\AbstractEntity;
use albertborsos\ddd\tests\support\base\domains\customer\mysql\CustomerWithBehaviorsActiveRepository;

/**
* Class Customer
Expand All @@ -14,6 +16,7 @@ class CustomerWithBehaviors extends AbstractEntity
{
public $id;
public $name;
public $slug;
public $createdAt;
public $createdBy;
public $updatedAt;
Expand All @@ -27,6 +30,12 @@ public function behaviors()
return [
'timestamp' => TimestampBehavior::class,
'blameable' => BlameableBehavior::class,
'sluggable' => [
'class' => SluggableBehavior::class,
'attribute' => 'name',
'ensureUnique' => true,
'repository' => CustomerWithBehaviorsActiveRepository::class,
],
];
}

Expand All @@ -35,6 +44,7 @@ public function fields()
return [
'id',
'name',
'slug',
'createdAt',
'createdBy',
'updatedAt',
Expand Down
3 changes: 2 additions & 1 deletion tests/_support/base/domains/customer/mysql/Customer.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*
* @property int $id
* @property string $name
* @property string $slug
*
* @property CustomerAddress[] $customerAddresses
*/
Expand All @@ -30,7 +31,7 @@ public static function tableName()
public function rules()
{
return [
[['name'], 'string', 'max' => 255],
[['name', 'slug'], 'string', 'max' => 255],
];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public function attributeLabels()
return [
'id' => 'ID',
'name' => 'Name',
'slug' => 'Slug',
];
}
}
2 changes: 2 additions & 0 deletions tests/fixtures/data/customer-with-behaviors.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
[
'id' => 1,
'name' => 'Albert',
'slug' => 'albert',
'created_at' => 1,
'created_by' => 10,
'updated_at' => 1,
Expand All @@ -11,6 +12,7 @@
[
'id' => 2,
'name' => 'Noncsi',
'slug' => 'noncsi',
'created_at' => 1,
'created_by' => 10,
'updated_at' => 2,
Expand Down
1 change: 1 addition & 0 deletions tests/migrations/m190807_151625_add_customer_tables.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public function up()
$this->createTable(self::TABLE_NAME_CUSTOMER, [
'id' => $this->bigPrimaryKey(),
'name' => $this->string(),
'slug' => $this->string(),
'created_at' => $this->bigInteger(),
'created_by' => $this->bigInteger(),
'updated_at' => $this->bigInteger(),
Expand Down
110 changes: 110 additions & 0 deletions tests/unit/behaviors/SluggableBehaviorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<?php

namespace albertborsos\ddd\tests\behaviors;

use albertborsos\ddd\repositories\AbstractActiveRepository;
use albertborsos\ddd\tests\fixtures\CustomerWithBehaviorsFixtures;
use albertborsos\ddd\tests\support\base\domains\customer\entities\CustomerWithBehaviors;
use albertborsos\ddd\tests\support\base\domains\customer\mysql\CustomerWithBehaviorsActiveRepository;
use albertborsos\ddd\tests\support\base\domains\customer\mysql\CustomerWithModifiedBehaviorsActiveRepository;
use Codeception\PHPUnit\TestCase;
use yii\test\FixtureTrait;

class SluggableBehaviorTest extends TestCase
{
use FixtureTrait;

public function fixtures()
{
return [
'customers' => CustomerWithBehaviorsFixtures::class,
];
}

protected function setUp(): void
{
parent::setUp();
$this->initFixtures();
}

public function testInsert()
{
$data = [
'id' => 3,
'name' => 'Test slug attribute is filled on insert',
];

/** @var AbstractActiveRepository $repository */
$repository = \Yii::createObject(CustomerWithBehaviorsActiveRepository::class);

$entity = $repository->hydrate($data);
$this->assertTrue($repository->insert($entity));

/** @var CustomerWithBehaviors $entity */
$entity = $repository->findOne($data['id']);

$this->assertNotEmpty($entity->slug);
}

public function testUpdate()
{
$data = [
'id' => 1,
'name' => 'Test slug attribute is modified on update',
];

/** @var AbstractActiveRepository $repository */
$repository = \Yii::createObject(CustomerWithBehaviorsActiveRepository::class);
/** @var CustomerWithBehaviors $entity */
$entity = $repository->findOne($data['id']);

$oldSlug = $entity->slug;

$entity->setAttributes($data, false);
$this->assertTrue($repository->update($entity));

$entity = $repository->findOne($data['id']);

$this->assertNotEquals($oldSlug, $entity->slug);
}

public function testUpdateWithoutModification()
{
$data = [
'id' => 1,
'name' => 'Albert',
];

/** @var AbstractActiveRepository $repository */
$repository = \Yii::createObject(CustomerWithBehaviorsActiveRepository::class);
/** @var CustomerWithBehaviors $entity */
$entity = $repository->findOne($data['id']);

$oldSlug = $entity->slug;

$entity->setAttributes($data, false);
$this->assertTrue($repository->update($entity));

$entity = $repository->findOne($data['id']);

$this->assertEquals($oldSlug, $entity->slug);
}

public function testUniqueSlug()
{
$data = [
'id' => 3,
'name' => 'Albert',
];

/** @var AbstractActiveRepository $repository */
$repository = \Yii::createObject(CustomerWithBehaviorsActiveRepository::class);
/** @var CustomerWithBehaviors $entity */
$entity = $repository->hydrate($data);
$this->assertTrue($repository->insert($entity));

$entity = $repository->findOne($data['id']);

$this->assertNotEquals('albert', $entity->slug);
}
}

0 comments on commit 6b743d3

Please sign in to comment.