diff --git a/app/Bus/Commands/Tag/ApplyTagCommand.php b/app/Bus/Commands/Tag/ApplyTagCommand.php new file mode 100644 index 000000000000..1fbd74218355 --- /dev/null +++ b/app/Bus/Commands/Tag/ApplyTagCommand.php @@ -0,0 +1,51 @@ + + */ +final class ApplyTagCommand +{ + /** + * The model to apply the tag to. + * + * @var \Illuminate\Database\Eloquent\Model + */ + public $model; + + /** + * The tag to apply. + * + * @var \CachetHQ\Cachet\Models\Tag + */ + public $tag; + + /** + * Create a new apply tag command instance. + * + * @param \Illuminate\Database\Eloquent\Model $model + * @param \CachetHQ\Cachet\Models\Tag $tag + * + * @return void + */ + public function __construct(Model $model, Tag $tag) + { + $this->model = $model; + $this->tag = $tag; + } +} diff --git a/app/Bus/Commands/Tag/CreateTagCommand.php b/app/Bus/Commands/Tag/CreateTagCommand.php new file mode 100644 index 000000000000..69ae89eb66e6 --- /dev/null +++ b/app/Bus/Commands/Tag/CreateTagCommand.php @@ -0,0 +1,48 @@ + + */ +final class CreateTagCommand +{ + /** + * The tag name. + * + * @var string + */ + public $name; + + /** + * The tag slug. + * + * @var string|null + */ + public $slug; + + /** + * Create a new create tag command instance. + * + * @param string $name + * @param string|null $slug + * + * @return void + */ + public function __construct($name, $slug = null) + { + $this->name = $name; + $this->slug = $slug; + } +} diff --git a/app/Bus/Commands/Tag/DeleteTagCommand.php b/app/Bus/Commands/Tag/DeleteTagCommand.php new file mode 100644 index 000000000000..7ab9500146a4 --- /dev/null +++ b/app/Bus/Commands/Tag/DeleteTagCommand.php @@ -0,0 +1,41 @@ + + */ +final class DeleteTagCommand +{ + /** + * The tag. + * + * @var \CachetHQ\Cachet\Models\Tag + */ + public $tag; + + /** + * Create a new delete tag command instance. + * + * @param \CachetHQ\Cachet\Models\Tag $tag + * + * @return void + */ + public function __construct(Tag $tag) + { + $this->tag = $tag; + } +} diff --git a/app/Bus/Commands/Tag/UpdateTagCommand.php b/app/Bus/Commands/Tag/UpdateTagCommand.php new file mode 100644 index 000000000000..f27475006fa6 --- /dev/null +++ b/app/Bus/Commands/Tag/UpdateTagCommand.php @@ -0,0 +1,59 @@ + + */ +final class UpdateTagCommand +{ + /** + * The tag. + * + * @var \CachetHQ\Cachet\Models\Tag + */ + public $tag; + + /** + * The new tag name. + * + * @var string|null + */ + public $name; + + /** + * The new tag slug. + * + * @var string|null + */ + public $slug; + + /** + * Create a new update tag command instance. + * + * @param \CachetHQ\Cachet\Models\Tag $tag + * @param string|null $name + * @param string|null $slug + * + * @return void + */ + public function __construct(Tag $tag, $name, $slug) + { + $this->tag = $tag; + $this->name = $name; + $this->slug = $slug; + } +} diff --git a/app/Bus/Handlers/Commands/Tag/ApplyTagCommandHandler.php b/app/Bus/Handlers/Commands/Tag/ApplyTagCommandHandler.php new file mode 100644 index 000000000000..7f1c4f4b761f --- /dev/null +++ b/app/Bus/Handlers/Commands/Tag/ApplyTagCommandHandler.php @@ -0,0 +1,39 @@ + + */ +class ApplyTagCommandHandler +{ + /** + * Handle the command. + * + * @param \CachetHQ\Cachet\Bus\Commands\Tag\ApplyTagCommand $command + * + * @return void + */ + public function handle(ApplyTagCommand $command) + { + Taggable::firstOrCreate([ + 'tag_id' => $command->tag->id, + 'taggable_id' => $command->model->id, + 'taggable_type' => $command->model->getTable(), + ]); + } +} diff --git a/app/Bus/Handlers/Commands/Tag/CreateTagCommandHandler.php b/app/Bus/Handlers/Commands/Tag/CreateTagCommandHandler.php new file mode 100644 index 000000000000..71f616ddcc0e --- /dev/null +++ b/app/Bus/Handlers/Commands/Tag/CreateTagCommandHandler.php @@ -0,0 +1,39 @@ + + */ +class CreateTagCommandHandler +{ + /** + * Handle the command. + * + * @param \CachetHQ\Cachet\Bus\Commands\Tag\CreateTagCommand $command + * + * @return \CachetHQ\Cachet\Models\Tag + */ + public function handle(CreateTagCommand $command) + { + return Tag::firstOrCreate([ + 'name' => $command->name, + 'slug' => $command->slug ? $command->slug : Str::slug($command->name), + ]); + } +} diff --git a/app/Bus/Handlers/Commands/Tag/DeleteTagCommandHandler.php b/app/Bus/Handlers/Commands/Tag/DeleteTagCommandHandler.php new file mode 100644 index 000000000000..08189349e2d4 --- /dev/null +++ b/app/Bus/Handlers/Commands/Tag/DeleteTagCommandHandler.php @@ -0,0 +1,34 @@ + + */ +class DeleteTagCommandHandler +{ + /** + * Handle the command. + * + * @param \CachetHQ\Cachet\Bus\Commands\Tag\DeleteTagCommand $command + * + * @return \CachetHQ\Cachet\Models\Tag + */ + public function handle(DeleteTagCommand $command) + { + $command->tag->delete(); + } +} diff --git a/app/Bus/Handlers/Commands/Tag/UpdateTagCommandHandler.php b/app/Bus/Handlers/Commands/Tag/UpdateTagCommandHandler.php new file mode 100644 index 000000000000..5a21d7d6d671 --- /dev/null +++ b/app/Bus/Handlers/Commands/Tag/UpdateTagCommandHandler.php @@ -0,0 +1,38 @@ + + */ +class UpdateTagCommandHandler +{ + /** + * Handle the command. + * + * @param \CachetHQ\Cachet\Bus\Commands\Tag\UpdateTagCommand $command + * + * @return void + */ + public function handle(UpdateTagCommand $command) + { + return $command->tag->update([ + 'name' => $command->name, + 'slug' => $command->slug ? $command->slug : Str::slug($command->name), + ]); + } +} diff --git a/app/Http/Controllers/Api/ComponentController.php b/app/Http/Controllers/Api/ComponentController.php index 4b9d2964b948..b171a7add81f 100644 --- a/app/Http/Controllers/Api/ComponentController.php +++ b/app/Http/Controllers/Api/ComponentController.php @@ -14,8 +14,9 @@ use CachetHQ\Cachet\Bus\Commands\Component\CreateComponentCommand; use CachetHQ\Cachet\Bus\Commands\Component\RemoveComponentCommand; use CachetHQ\Cachet\Bus\Commands\Component\UpdateComponentCommand; +use CachetHQ\Cachet\Bus\Commands\Tag\ApplyTagCommand; +use CachetHQ\Cachet\Bus\Commands\Tag\CreateTagCommand; use CachetHQ\Cachet\Models\Component; -use CachetHQ\Cachet\Models\Tag; use GrahamCampbell\Binput\Facades\Binput; use Illuminate\Contracts\Auth\Guard; use Illuminate\Database\QueryException; @@ -85,17 +86,16 @@ public function store() } if (Binput::has('tags')) { - // The component was added successfully, so now let's deal with the tags. - $tags = preg_split('/ ?, ?/', Binput::get('tags')); - - // For every tag, do we need to create it? - $componentTags = array_map(function ($taggable) use ($component) { - return Tag::firstOrCreate([ - 'name' => $taggable, - ])->id; - }, $tags); + $component->tags()->delete(); - $component->tags()->sync($componentTags); + // 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 dispatch(new CreateTagCommand($tag)); + })->each(function ($tag) use ($component) { + dispatch(new ApplyTagCommand($component, $tag)); + }); } return $this->item($component); @@ -128,14 +128,16 @@ public function update(Component $component) } if (Binput::has('tags')) { - $tags = preg_split('/ ?, ?/', Binput::get('tags')); + $component->tags()->delete(); - // For every tag, do we need to create it? - $componentTags = array_map(function ($taggable) use ($component) { - return Tag::firstOrCreate(['name' => $taggable])->id; - }, $tags); - - $component->tags()->sync($componentTags); + // 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 dispatch(new CreateTagCommand($tag)); + })->each(function ($tag) use ($component) { + dispatch(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 205dc90a4d26..3bd0f0efcbe3 100644 --- a/app/Http/Controllers/Dashboard/ComponentController.php +++ b/app/Http/Controllers/Dashboard/ComponentController.php @@ -15,11 +15,13 @@ use CachetHQ\Cachet\Bus\Commands\Component\CreateComponentCommand; use CachetHQ\Cachet\Bus\Commands\Component\RemoveComponentCommand; use CachetHQ\Cachet\Bus\Commands\Component\UpdateComponentCommand; +use CachetHQ\Cachet\Bus\Commands\Tag\ApplyTagCommand; +use CachetHQ\Cachet\Bus\Commands\Tag\CreateTagCommand; use CachetHQ\Cachet\Models\Component; use CachetHQ\Cachet\Models\ComponentGroup; -use CachetHQ\Cachet\Models\Tag; use GrahamCampbell\Binput\Facades\Binput; use Illuminate\Routing\Controller; +use Illuminate\Support\Collection; use Illuminate\Support\Facades\View; /** @@ -132,15 +134,16 @@ public function updateComponentAction(Component $component) ->withErrors($e->getMessageBag()); } - // The component was added successfully, so now let's deal with the tags. - $tags = preg_split('/ ?, ?/', $tags); - - // For every tag, do we need to create it? - $componentTags = array_map(function ($taggable) { - return Tag::firstOrCreate(['name' => $taggable])->id; - }, $tags); + $component->tags()->delete(); - $component->tags()->sync($componentTags); + // 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 dispatch(new CreateTagCommand($tag)); + })->each(function ($tag) use ($component) { + dispatch(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'))); @@ -187,14 +190,13 @@ public function createComponentAction() } // The component was added successfully, so now let's deal with the tags. - $tags = preg_split('/ ?, ?/', $tags); - - // For every tag, do we need to create it? - $componentTags = array_map(function ($taggable) { - return Tag::firstOrCreate(['name' => $taggable])->id; - }, $tags); - - $component->tags()->sync($componentTags); + Collection::make(preg_split('/ ?, ?/', $tags))->map(function ($tag) { + return trim($tag); + })->map(function ($tag) { + return dispatch(new CreateTagCommand($tag)); + })->each(function ($tag) use ($component) { + dispatch(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/resources/views/dashboard/components/edit.blade.php b/resources/views/dashboard/components/edit.blade.php index 7acc83afec1e..006edc21d914 100644 --- a/resources/views/dashboard/components/edit.blade.php +++ b/resources/views/dashboard/components/edit.blade.php @@ -53,7 +53,7 @@
- + {{ trans('forms.components.tags-help') }}
diff --git a/tests/Bus/Commands/Tag/ApplyTagCommandTest.php b/tests/Bus/Commands/Tag/ApplyTagCommandTest.php new file mode 100644 index 000000000000..a1c8bd255441 --- /dev/null +++ b/tests/Bus/Commands/Tag/ApplyTagCommandTest.php @@ -0,0 +1,54 @@ + + */ +class ApplyTagCommandTest extends AbstractTestCase +{ + use CommandTrait; + + protected function getObjectAndParams() + { + $params = [ + 'model' => new Component(), + 'tag' => new Tag(), + ]; + + $object = new ApplyTagCommand( + $params['model'], + $params['tag'] + ); + + return compact('params', 'object'); + } + + protected function objectHasRules() + { + return false; + } + + protected function getHandlerClass() + { + return ApplyTagCommandHandler::class; + } +} diff --git a/tests/Bus/Commands/Tag/CreateTagCommandTest.php b/tests/Bus/Commands/Tag/CreateTagCommandTest.php new file mode 100644 index 000000000000..59bf63698975 --- /dev/null +++ b/tests/Bus/Commands/Tag/CreateTagCommandTest.php @@ -0,0 +1,52 @@ + + */ +class CreateTagCommandTest extends AbstractTestCase +{ + use CommandTrait; + + protected function getObjectAndParams() + { + $params = [ + 'name' => 'Test', + 'slug' => 'test', + ]; + + $object = new CreateTagCommand( + $params['name'], + $params['slug'] + ); + + return compact('params', 'object'); + } + + protected function objectHasRules() + { + return false; + } + + protected function getHandlerClass() + { + return CreateTagCommandHandler::class; + } +} diff --git a/tests/Bus/Commands/Tag/DeleteTagCommandTest.php b/tests/Bus/Commands/Tag/DeleteTagCommandTest.php new file mode 100644 index 000000000000..2bee7b9717b0 --- /dev/null +++ b/tests/Bus/Commands/Tag/DeleteTagCommandTest.php @@ -0,0 +1,51 @@ + + */ +class DeleteTagCommandTest extends AbstractTestCase +{ + use CommandTrait; + + protected function getObjectAndParams() + { + $params = [ + 'tag' => new Tag(), + ]; + + $object = new DeleteTagCommand( + $params['tag'] + ); + + return compact('params', 'object'); + } + + protected function objectHasRules() + { + return false; + } + + protected function getHandlerClass() + { + return DeleteTagCommandHandler::class; + } +} diff --git a/tests/Bus/Commands/Tag/UpdateTagCommandTest.php b/tests/Bus/Commands/Tag/UpdateTagCommandTest.php new file mode 100644 index 000000000000..163c7477b37a --- /dev/null +++ b/tests/Bus/Commands/Tag/UpdateTagCommandTest.php @@ -0,0 +1,55 @@ + + */ +class UpdateTagCommandTest extends AbstractTestCase +{ + use CommandTrait; + + protected function getObjectAndParams() + { + $params = [ + 'tag' => new Tag(), + 'name' => 'Test', + 'slug' => 'test', + ]; + + $object = new UpdateTagCommand( + $params['tag'], + $params['name'], + $params['slug'] + ); + + return compact('params', 'object'); + } + + protected function objectHasRules() + { + return false; + } + + protected function getHandlerClass() + { + return UpdateTagCommandHandler::class; + } +}