diff options
author | Frederic Guillot <fred@kanboard.net> | 2016-06-24 11:40:58 -0400 |
---|---|---|
committer | Frederic Guillot <fred@kanboard.net> | 2016-06-24 11:40:58 -0400 |
commit | b2e92480c29acb15586bc8ea305c8416927a667c (patch) | |
tree | 3a81aaac07b88a4af08795203e997b04d50a96c6 | |
parent | 700b4e8f0265e4eabd7a7c0eb6a06088d50554fe (diff) |
Added filter class for tags
-rw-r--r-- | app/Filter/TaskTagFilter.php | 74 | ||||
-rw-r--r-- | app/Model/TaskTagModel.php | 20 | ||||
-rw-r--r-- | app/ServiceProvider/FilterProvider.php | 4 | ||||
-rw-r--r-- | doc/search.markdown | 6 | ||||
-rw-r--r-- | tests/units/Base.php | 4 | ||||
-rw-r--r-- | tests/units/Core/Filter/LexerTest.php | 12 | ||||
-rw-r--r-- | tests/units/Filter/TaskTagFilterTest.php | 121 | ||||
-rw-r--r-- | tests/units/Model/TaskTagModelTest.php | 14 |
8 files changed, 243 insertions, 12 deletions
diff --git a/app/Filter/TaskTagFilter.php b/app/Filter/TaskTagFilter.php new file mode 100644 index 00000000..01b6f625 --- /dev/null +++ b/app/Filter/TaskTagFilter.php @@ -0,0 +1,74 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\TagModel; +use Kanboard\Model\TaskModel; +use Kanboard\Model\TaskTagModel; +use PicoDb\Database; + +/** + * Class TaskTagFilter + * + * @package Kanboard\Filter + * @author Frederic Guillot + */ +class TaskTagFilter extends BaseFilter implements FilterInterface +{ + /** + * Database object + * + * @access private + * @var Database + */ + private $db; + + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('tag'); + } + + /** + * Set database object + * + * @access public + * @param Database $db + * @return $this + */ + public function setDatabase(Database $db) + { + $this->db = $db; + return $this; + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + $task_ids = $this->db + ->table(TagModel::TABLE) + ->ilike(TagModel::TABLE.'.name', $this->value) + ->asc(TagModel::TABLE.'.project_id') + ->join(TaskTagModel::TABLE, 'tag_id', 'id') + ->findAllByColumn(TaskTagModel::TABLE.'.task_id'); + + if (empty($task_ids)) { + $task_ids = array(-1); + } + + $this->query->in(TaskModel::TABLE.'.id', $task_ids); + + return $this; + } +} diff --git a/app/Model/TaskTagModel.php b/app/Model/TaskTagModel.php index 3dd1dd88..91dfd224 100644 --- a/app/Model/TaskTagModel.php +++ b/app/Model/TaskTagModel.php @@ -74,9 +74,9 @@ class TaskTagModel extends Base * Add or update a list of tags to a task * * @access public - * @param integer $project_id - * @param integer $task_id - * @param string[] $tags + * @param integer $project_id + * @param integer $task_id + * @param string[] $tags * @return boolean */ public function save($project_id, $task_id, array $tags) @@ -123,10 +123,10 @@ class TaskTagModel extends Base * Associate missing tags * * @access protected - * @param integer $project_id - * @param integer $task_id - * @param array $task_tags - * @param array $tags + * @param integer $project_id + * @param integer $task_id + * @param array $task_tags + * @param string[] $tags * @return bool */ protected function associateTags($project_id, $task_id, $task_tags, $tags) @@ -146,9 +146,9 @@ class TaskTagModel extends Base * Dissociate removed tags * * @access protected - * @param integer $task_id - * @param array $task_tags - * @param array $tags + * @param integer $task_id + * @param array $task_tags + * @param string[] $tags * @return bool */ protected function dissociateTags($task_id, $task_tags, $tags) diff --git a/app/ServiceProvider/FilterProvider.php b/app/ServiceProvider/FilterProvider.php index cdef9ed8..20281a09 100644 --- a/app/ServiceProvider/FilterProvider.php +++ b/app/ServiceProvider/FilterProvider.php @@ -26,6 +26,7 @@ use Kanboard\Filter\TaskReferenceFilter; use Kanboard\Filter\TaskStatusFilter; use Kanboard\Filter\TaskSubtaskAssigneeFilter; use Kanboard\Filter\TaskSwimlaneFilter; +use Kanboard\Filter\TaskTagFilter; use Kanboard\Filter\TaskTitleFilter; use Kanboard\Model\ProjectModel; use Kanboard\Model\ProjectGroupRoleModel; @@ -163,6 +164,9 @@ class FilterProvider implements ServiceProviderInterface ->setDatabase($c['db']) ) ->withFilter(new TaskSwimlaneFilter()) + ->withFilter(TaskTagFilter::getInstance() + ->setDatabase($c['db']) + ) ->withFilter(new TaskTitleFilter(), true) ; diff --git a/doc/search.markdown b/doc/search.markdown index 37bb8625..ab8e0b5a 100644 --- a/doc/search.markdown +++ b/doc/search.markdown @@ -152,6 +152,12 @@ Attribute: **comment** - Find comments that contains this title: `comment:"My comment message"` +### Search by tags + +Attribute: **tag** + +- Example: `tag:"My tag"` + Activity stream search ---------------------- diff --git a/tests/units/Base.php b/tests/units/Base.php index f7bee241..9dbfb280 100644 --- a/tests/units/Base.php +++ b/tests/units/Base.php @@ -48,8 +48,8 @@ abstract class Base extends PHPUnit_Framework_TestCase new Stopwatch ); - $this->container['db']->logQueries = true; - $this->container['logger'] = new Logger; + $this->container['db']->getStatementHandler()->withLogging(); + $this->container['logger'] = new Logger(); $this->container['httpClient'] = $this ->getMockBuilder('\Kanboard\Core\Http\Client') diff --git a/tests/units/Core/Filter/LexerTest.php b/tests/units/Core/Filter/LexerTest.php index c72231c4..b777531d 100644 --- a/tests/units/Core/Filter/LexerTest.php +++ b/tests/units/Core/Filter/LexerTest.php @@ -202,4 +202,16 @@ class LexerTest extends Base $this->assertSame($expected, $lexer->tokenize('६Δↈ五一')); } + + public function testTokenizeWithMultipleValues() + { + $lexer = new Lexer(); + $lexer->addToken("/^(tag:)/", 'T_TAG'); + + $expected = array( + 'T_TAG' => array('tag 1', 'tag2'), + ); + + $this->assertSame($expected, $lexer->tokenize('tag:"tag 1" tag:tag2')); + } } diff --git a/tests/units/Filter/TaskTagFilterTest.php b/tests/units/Filter/TaskTagFilterTest.php new file mode 100644 index 00000000..a36d3475 --- /dev/null +++ b/tests/units/Filter/TaskTagFilterTest.php @@ -0,0 +1,121 @@ +<?php + +use Kanboard\Filter\TaskTagFilter; +use Kanboard\Model\ProjectModel; +use Kanboard\Model\TaskCreationModel; +use Kanboard\Model\TaskFinderModel; +use Kanboard\Model\TaskTagModel; + +require_once __DIR__.'/../Base.php'; + +class TaskTagFilterTest extends Base +{ + public function testWithMultipleMatches() + { + $taskFinderModel = new TaskFinderModel($this->container); + $projectModel = new ProjectModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + $taskTagModel = new TaskTagModel($this->container); + $query = $taskFinderModel->getExtendedQuery(); + + $this->assertEquals(1, $projectModel->create(array('name' => 'Test'))); + $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test1'))); + $this->assertEquals(2, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test2'))); + $this->assertEquals(3, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test3'))); + + $this->assertTrue($taskTagModel->save(1, 1, array('My tag 1', 'My tag 2', 'My tag 3'))); + $this->assertTrue($taskTagModel->save(1, 2, array('My tag 3'))); + + $filter = new TaskTagFilter(); + $filter->setDatabase($this->container['db']); + $filter->withQuery($query); + $filter->withValue('my tag 3'); + $filter->apply(); + + $tasks = $query->findAll(); + $this->assertCount(2, $tasks); + $this->assertEquals('test1', $tasks[0]['title']); + $this->assertEquals('test2', $tasks[1]['title']); + } + + public function testWithSingleMatch() + { + $taskFinderModel = new TaskFinderModel($this->container); + $projectModel = new ProjectModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + $taskTagModel = new TaskTagModel($this->container); + $query = $taskFinderModel->getExtendedQuery(); + + $this->assertEquals(1, $projectModel->create(array('name' => 'Test'))); + $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test1'))); + $this->assertEquals(2, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test2'))); + $this->assertEquals(3, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test3'))); + + $this->assertTrue($taskTagModel->save(1, 1, array('My tag 1', 'My tag 2', 'My tag 3'))); + $this->assertTrue($taskTagModel->save(1, 2, array('My tag 3'))); + + $filter = new TaskTagFilter(); + $filter->setDatabase($this->container['db']); + $filter->withQuery($query); + $filter->withValue('my tag 2'); + $filter->apply(); + + $tasks = $query->findAll(); + $this->assertCount(1, $tasks); + $this->assertEquals('test1', $tasks[0]['title']); + } + + public function testWithNoMatch() + { + $taskFinderModel = new TaskFinderModel($this->container); + $projectModel = new ProjectModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + $taskTagModel = new TaskTagModel($this->container); + $query = $taskFinderModel->getExtendedQuery(); + + $this->assertEquals(1, $projectModel->create(array('name' => 'Test'))); + $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test1'))); + $this->assertEquals(2, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test2'))); + $this->assertEquals(3, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test3'))); + + $this->assertTrue($taskTagModel->save(1, 1, array('My tag 1', 'My tag 2', 'My tag 3'))); + $this->assertTrue($taskTagModel->save(1, 2, array('My tag 3'))); + + $filter = new TaskTagFilter(); + $filter->setDatabase($this->container['db']); + $filter->withQuery($query); + $filter->withValue('my tag 42'); + $filter->apply(); + + $tasks = $query->findAll(); + $this->assertCount(0, $tasks); + } + + public function testWithSameTagInMultipleProjects() + { + $taskFinderModel = new TaskFinderModel($this->container); + $projectModel = new ProjectModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + $taskTagModel = new TaskTagModel($this->container); + $query = $taskFinderModel->getExtendedQuery(); + + $this->assertEquals(1, $projectModel->create(array('name' => 'Test'))); + $this->assertEquals(2, $projectModel->create(array('name' => 'Test'))); + $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test1'))); + $this->assertEquals(2, $taskCreationModel->create(array('project_id' => 2, 'title' => 'test2'))); + + $this->assertTrue($taskTagModel->save(1, 1, array('My tag'))); + $this->assertTrue($taskTagModel->save(2, 2, array('My tag'))); + + $filter = new TaskTagFilter(); + $filter->setDatabase($this->container['db']); + $filter->withQuery($query); + $filter->withValue('my tag'); + $filter->apply(); + + $tasks = $query->findAll(); + $this->assertCount(2, $tasks); + $this->assertEquals('test1', $tasks[0]['title']); + $this->assertEquals('test2', $tasks[1]['title']); + } +} diff --git a/tests/units/Model/TaskTagModelTest.php b/tests/units/Model/TaskTagModelTest.php index 819f55b8..73bbeac1 100644 --- a/tests/units/Model/TaskTagModelTest.php +++ b/tests/units/Model/TaskTagModelTest.php @@ -110,4 +110,18 @@ class TaskTagModelTest extends Base $this->assertEquals($expected, $tags); } + + public function testGetTagsForTasksWithEmptyList() + { + $projectModel = new ProjectModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + $taskTagModel = new TaskTagModel($this->container); + + $this->assertEquals(1, $projectModel->create(array('name' => 'Test'))); + $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test1'))); + $this->assertTrue($taskTagModel->save(1, 1, array('My tag 1', 'My tag 2', 'My tag 3'))); + + $tags = $taskTagModel->getTagsByTasks(array()); + $this->assertEquals(array(), $tags); + } } |