From 6810af48f709db549556d1b2c0df982369788c43 Mon Sep 17 00:00:00 2001 From: James Brooks Date: Fri, 12 Jul 2019 10:29:23 +0100 Subject: [PATCH] Tag syncing is now done within the Component commands --- .../Component/CreateComponentCommand.php | 28 +++-- .../Component/UpdateComponentCommand.php | 12 +- .../CreateComponentCommandHandler.php | 9 ++ .../UpdateComponentCommandHandler.php | 9 ++ .../Controllers/Api/ComponentController.php | 32 +---- .../Dashboard/ComponentController.php | 26 +--- app/Models/Tag.php | 27 +++++ app/Models/Traits/HasTags.php | 112 +++++++++++++++++- .../Component/CreateComponentCommandTest.php | 4 +- .../Component/UpdateComponentCommandTest.php | 2 + 10 files changed, 197 insertions(+), 64 deletions(-) diff --git a/app/Bus/Commands/Component/CreateComponentCommand.php b/app/Bus/Commands/Component/CreateComponentCommand.php index eeb36054..4244f42c 100644 --- a/app/Bus/Commands/Component/CreateComponentCommand.php +++ b/app/Bus/Commands/Component/CreateComponentCommand.php @@ -74,6 +74,13 @@ final class CreateComponentCommand */ public $meta; + /** + * Tags string. + * + * @var string + */ + public $tags; + /** * The validation rules. * @@ -88,23 +95,25 @@ final class CreateComponentCommand 'group_id' => 'nullable|int', 'enabled' => 'nullable|bool', 'meta' => 'nullable|array', + 'tags' => 'nullable|string', ]; /** * Create a new add component command instance. * - * @param string $name - * @param string $description - * @param int $status - * @param string $link - * @param int $order - * @param int $group_id - * @param bool $enabled - * @param array|null $meta + * @param string $name + * @param string $description + * @param int $status + * @param string $link + * @param int $order + * @param int $group_id + * @param bool $enabled + * @param array|null $meta + * @param string|null $tags * * @return void */ - public function __construct($name, $description, $status, $link, $order, $group_id, $enabled, $meta) + public function __construct($name, $description, $status, $link, $order, $group_id, $enabled, $meta, $tags = null) { $this->name = $name; $this->description = $description; @@ -114,5 +123,6 @@ final class CreateComponentCommand $this->group_id = $group_id; $this->enabled = $enabled; $this->meta = $meta; + $this->tags = $tags; } } diff --git a/app/Bus/Commands/Component/UpdateComponentCommand.php b/app/Bus/Commands/Component/UpdateComponentCommand.php index 96550fd6..99093397 100644 --- a/app/Bus/Commands/Component/UpdateComponentCommand.php +++ b/app/Bus/Commands/Component/UpdateComponentCommand.php @@ -78,6 +78,13 @@ final class UpdateComponentCommand */ public $meta; + /** + * The tags. + * + * @var string|null + */ + public $tags; + /** * If this is true, we won't notify subscribers of the change. * @@ -114,11 +121,12 @@ final class UpdateComponentCommand * @param int|null $group_id * @param bool|null $enabled * @param array|null $meta + * @param string|null $tags * @param bool $silent * * @return void */ - public function __construct(Component $component, $name = null, $description = null, $status = null, $link = null, $order = null, $group_id = null, $enabled = null, $meta = null, $silent = null) + public function __construct(Component $component, $name = null, $description = null, $status = null, $link = null, $order = null, $group_id = null, $enabled = null, $meta = null, $tags = null, $silent = null) { $this->component = $component; $this->name = $name; @@ -129,6 +137,8 @@ final class UpdateComponentCommand $this->group_id = $group_id; $this->enabled = $enabled; $this->meta = $meta; + $this->tags = $tags; $this->silent = $silent; + $this->tags = $tags; } } diff --git a/app/Bus/Handlers/Commands/Component/CreateComponentCommandHandler.php b/app/Bus/Handlers/Commands/Component/CreateComponentCommandHandler.php index d9bcc339..867f49fb 100644 --- a/app/Bus/Handlers/Commands/Component/CreateComponentCommandHandler.php +++ b/app/Bus/Handlers/Commands/Component/CreateComponentCommandHandler.php @@ -53,6 +53,15 @@ class CreateComponentCommandHandler { $component = Component::create($this->filter($command)); + // Sync the tags into the component. + if ($command->tags) { + collect(preg_split('/ ?, ?/', $command->tags))->filter()->map(function ($tag) { + return trim($tag); + })->pipe(function ($tags) use ($component) { + $component->attachTags($tags); + }); + } + event(new ComponentWasCreatedEvent($this->auth->user(), $component)); return $component; diff --git a/app/Bus/Handlers/Commands/Component/UpdateComponentCommandHandler.php b/app/Bus/Handlers/Commands/Component/UpdateComponentCommandHandler.php index 1fdaf007..f0ad1e44 100644 --- a/app/Bus/Handlers/Commands/Component/UpdateComponentCommandHandler.php +++ b/app/Bus/Handlers/Commands/Component/UpdateComponentCommandHandler.php @@ -56,6 +56,15 @@ class UpdateComponentCommandHandler $component->update($this->filter($command)); + // Sync the tags into the component. + if ($command->tags) { + collect(preg_split('/ ?, ?/', $command->tags))->filter()->map(function ($tag) { + return trim($tag); + })->pipe(function ($tags) use ($component) { + $component->syncTags($tags); + }); + } + event(new ComponentWasUpdatedEvent($this->auth->user(), $component)); return $component; diff --git a/app/Http/Controllers/Api/ComponentController.php b/app/Http/Controllers/Api/ComponentController.php index 609eeba6..e2de04f0 100644 --- a/app/Http/Controllers/Api/ComponentController.php +++ b/app/Http/Controllers/Api/ComponentController.php @@ -84,25 +84,13 @@ class ComponentController extends AbstractApiController Binput::get('order'), Binput::get('group_id'), (bool) Binput::get('enabled', true), - Binput::get('meta', null) + Binput::get('meta'), + Binput::get('tags'), )); } catch (QueryException $e) { throw new BadRequestHttpException(); } - if (Binput::has('tags')) { - $component->tags()->delete(); - - // The component was added successfully, so now let's deal with the tags. - Collection::make(preg_split('/ ?, ?/', $tags))->map(function ($tag) { - return trim($tag); - })->map(function ($tag) { - return execute(new CreateTagCommand($tag)); - })->each(function ($tag) use ($component) { - execute(new ApplyTagCommand($component, $tag)); - }); - } - return $this->item($component); } @@ -125,26 +113,14 @@ class ComponentController extends AbstractApiController Binput::get('order'), Binput::get('group_id'), Binput::get('enabled', $component->enabled), - Binput::get('meta', null), + Binput::get('meta'), + Binput::get('tags'), (bool) Binput::get('silent', false) )); } catch (QueryException $e) { throw new BadRequestHttpException(); } - if (Binput::has('tags')) { - $component->tags()->delete(); - - // The component was added successfully, so now let's deal with the tags. - Collection::make(preg_split('/ ?, ?/', $tags))->map(function ($tag) { - return trim($tag); - })->map(function ($tag) { - return execute(new CreateTagCommand($tag)); - })->each(function ($tag) use ($component) { - execute(new ApplyTagCommand($component, $tag)); - }); - } - return $this->item($component); } diff --git a/app/Http/Controllers/Dashboard/ComponentController.php b/app/Http/Controllers/Dashboard/ComponentController.php index 7e79c12d..31026b2b 100644 --- a/app/Http/Controllers/Dashboard/ComponentController.php +++ b/app/Http/Controllers/Dashboard/ComponentController.php @@ -113,7 +113,6 @@ class ComponentController extends Controller public function updateComponentAction(Component $component) { $componentData = Binput::get('component'); - $tags = Arr::pull($componentData, 'tags'); try { $component = execute(new UpdateComponentCommand( @@ -126,6 +125,7 @@ class ComponentController extends Controller $componentData['group_id'], $componentData['enabled'], null, // Meta data cannot be supplied through the dashboard yet. + $componentData['tags'], // Meta data cannot be supplied through the dashboard yet. true // Silent since we're not really making changes to the component (this should be optional) )); } catch (ValidationException $e) { @@ -135,17 +135,6 @@ class ComponentController extends Controller ->withErrors($e->getMessageBag()); } - $component->tags()->delete(); - - // The component was added successfully, so now let's deal with the tags. - Collection::make(preg_split('/ ?, ?/', $tags))->map(function ($tag) { - return trim($tag); - })->map(function ($tag) { - return execute(new CreateTagCommand($tag)); - })->each(function ($tag) use ($component) { - execute(new ApplyTagCommand($component, $tag)); - }); - return cachet_redirect('dashboard.components.edit', [$component->id]) ->withSuccess(sprintf('%s %s', trans('dashboard.notifications.awesome'), trans('dashboard.components.edit.success'))); } @@ -170,7 +159,6 @@ class ComponentController extends Controller public function createComponentAction() { $componentData = Binput::get('component'); - $tags = Arr::pull($componentData, 'tags'); try { $component = execute(new CreateComponentCommand( @@ -181,7 +169,8 @@ class ComponentController extends Controller $componentData['order'], $componentData['group_id'], $componentData['enabled'], - null // Meta data cannot be supplied through the dashboard yet. + null, // Meta data cannot be supplied through the dashboard yet. + $componentData['tags'] )); } catch (ValidationException $e) { return cachet_redirect('dashboard.components.create') @@ -190,15 +179,6 @@ class ComponentController extends Controller ->withErrors($e->getMessageBag()); } - // The component was added successfully, so now let's deal with the tags. - Collection::make(preg_split('/ ?, ?/', $tags))->map(function ($tag) { - return trim($tag); - })->map(function ($tag) { - return execute(new CreateTagCommand($tag)); - })->each(function ($tag) use ($component) { - execute(new ApplyTagCommand($component, $tag)); - }); - return cachet_redirect('dashboard.components') ->withSuccess(sprintf('%s %s', trans('dashboard.notifications.awesome'), trans('dashboard.components.add.success'))); } diff --git a/app/Models/Tag.php b/app/Models/Tag.php index f0a18eec..874b07ca 100644 --- a/app/Models/Tag.php +++ b/app/Models/Tag.php @@ -55,4 +55,31 @@ class Tag extends Model { return $this->belongsToMany(Component::class); } + + /** + * @param array|\ArrayAccess $values + * + * @return \CachetHQ\Cachet\Models\Tag|static + */ + public static function findOrCreate($values) + { + $tags = collect($values)->map(function ($value) { + if ($value instanceof Tag) { + return $value; + } + + $tag = static::where('name', '=', $value)->first(); + + if (!$tag instanceof Tag) { + $tag = static::create([ + 'name' => $value, + 'slug' => Str::slug($value), + ]); + } + + return $tag; + }); + + return is_string($values) ? $tags->first() : $tags; + } } diff --git a/app/Models/Traits/HasTags.php b/app/Models/Traits/HasTags.php index dbb62b75..75f1a606 100644 --- a/app/Models/Traits/HasTags.php +++ b/app/Models/Traits/HasTags.php @@ -13,6 +13,7 @@ namespace CachetHQ\Cachet\Models\Traits; use CachetHQ\Cachet\Models\Tag; use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Model; /** * This is the has tags trait. @@ -21,6 +22,33 @@ use Illuminate\Database\Eloquent\Builder; */ trait HasTags { + /** + * @var array + */ + protected $queuedTags = []; + + /** + * Boot the trait. + * + * @return void + */ + public static function bootHasTags() + { + static::created(function (Model $taggableModel) { + if (count($taggableModel->queuedTags) > 0) { + $taggableModel->attachTags($taggableModel->queuedTags); + + $taggableModel->queuedTags = []; + } + }); + + static::deleted(function (Model $deletedModel) { + $tags = $deletedModel->tags()->get(); + + $deletedModel->detachTags($tags); + }); + } + /** * Get the tags relation. * @@ -31,6 +59,20 @@ trait HasTags return $this->morphToMany(Tag::class, 'taggable'); } + /** + * @param string|array|\ArrayAccess|\CachetHQ\Cachet\Models\Tag $tags + */ + public function setTagsAttribute($tags) + { + if (! $this->exists) { + $this->queuedTags = $tags; + + return; + } + + $this->attachTags($tags); + } + /** * @param \Illuminate\Database\Eloquent\Builder $query * @param array|\ArrayAccess $tags @@ -61,12 +103,78 @@ trait HasTags $tags = static::convertToTags($tags); return $query->whereHas('tags', function (Builder $query) use ($tags) { - $tagIds = $tags->pluck('id')->toArray(); + $tagIds = collect($tags)->pluck('id'); - $query->whereIn('taggables.tag_id', $tagIds); + $query->whereIn('tags.id', $tagIds); }); } + /** + * @param array|\ArrayAccess|\CachetHQ\Cachet\Models\Tag $tags + * + * @return $this + */ + public function attachTags($tags) + { + $tags = collect(Tag::findOrCreate($tags)); + + $this->tags()->syncWithoutDetaching($tags->pluck('id')->toArray()); + + return $this; + } + + /** + * @param string|\CachetHQ\Cachet\Models\Tag $tag + * + * @return $this + */ + public function attachTag($tag) + { + return $this->attachTags([$tag]); + } + + /** + * @param array|\ArrayAccess $tags + * + * @return $this + */ + public function detachTags($tags) + { + $tags = static::convertToTags($tags); + + collect($tags) + ->filter() + ->each(function (Tag $tag) { + $this->tags()->detach($tag); + }); + + return $this; + } + + /** + * @param string|\CachetHQ\Cachet\Models\Tag $tag + * + * @return $this + */ + public function detachTag($tag) + { + return $this->detachTags([$tag]); + } + + /** + * @param array|\ArrayAccess $tags + * + * @return $this + */ + public function syncTags($tags) + { + $tags = collect(Tag::findOrCreate($tags)); + + $this->tags()->sync($tags->pluck('id')->toArray()); + + return $this; + } + /** * Convert a list of tags into a collection of \CachetHQ\Cachet\Models\Tag. * diff --git a/tests/Bus/Commands/Component/CreateComponentCommandTest.php b/tests/Bus/Commands/Component/CreateComponentCommandTest.php index 454beeb5..a2f3e272 100644 --- a/tests/Bus/Commands/Component/CreateComponentCommandTest.php +++ b/tests/Bus/Commands/Component/CreateComponentCommandTest.php @@ -37,6 +37,7 @@ class CreateComponentCommandTest extends AbstractTestCase 'group_id' => 0, 'enabled' => true, 'meta' => null, + 'tags' => 'Foo, Bar', ]; $object = new CreateComponentCommand( $params['name'], @@ -46,7 +47,8 @@ class CreateComponentCommandTest extends AbstractTestCase $params['order'], $params['group_id'], $params['enabled'], - $params['meta'] + $params['meta'], + $params['tags'] ); return compact('params', 'object'); diff --git a/tests/Bus/Commands/Component/UpdateComponentCommandTest.php b/tests/Bus/Commands/Component/UpdateComponentCommandTest.php index 59f808be..4360b28f 100644 --- a/tests/Bus/Commands/Component/UpdateComponentCommandTest.php +++ b/tests/Bus/Commands/Component/UpdateComponentCommandTest.php @@ -39,6 +39,7 @@ class UpdateComponentCommandTest extends AbstractTestCase 'group_id' => 0, 'enabled' => true, 'meta' => null, + 'tags' => null, 'silent' => false, ]; @@ -52,6 +53,7 @@ class UpdateComponentCommandTest extends AbstractTestCase $params['group_id'], $params['enabled'], $params['meta'], + $params['tags'], $params['silent'] );