From 9e29441c68a6a88b46d786a8954a5e82a5a59d72 Mon Sep 17 00:00:00 2001 From: Marcin Kurczewski Date: Thu, 31 Jul 2014 11:53:19 +0200 Subject: [PATCH] Added sorting by tag usage dates to tag list --- src/Models/Entities/TagEntity.php | 14 +++ src/Models/PostModel.php | 94 ++++++++++++-------- src/Models/SearchParsers/TagSearchParser.php | 4 + src/Upgrades/mysql/Upgrade17.sql | 15 ++++ src/Upgrades/sqlite/Upgrade17.sql | 15 ++++ src/Views/tag/tag-list.phtml | 2 + tests/Tests/ModelTests/TagModelTest.php | 16 ++++ 7 files changed, 124 insertions(+), 36 deletions(-) create mode 100644 src/Upgrades/mysql/Upgrade17.sql create mode 100644 src/Upgrades/sqlite/Upgrade17.sql create mode 100644 tests/Tests/ModelTests/TagModelTest.php diff --git a/src/Models/Entities/TagEntity.php b/src/Models/Entities/TagEntity.php index bed9542e..5b97c25e 100644 --- a/src/Models/Entities/TagEntity.php +++ b/src/Models/Entities/TagEntity.php @@ -4,6 +4,8 @@ use \Chibi\Sql as Sql; final class TagEntity extends AbstractEntity implements IValidatable, ISerializable { private $name; + private $creationDate; + private $updateDate; public function fillNew() { @@ -13,6 +15,8 @@ final class TagEntity extends AbstractEntity implements IValidatable, ISerializa { $this->id = (int) $row['id']; $this->name = $row['name']; + $this->creationDate = TextHelper::toIntegerOrNull($row['creation_date']); + $this->updateDate = TextHelper::toIntegerOrNull($row['update_date']); if (isset($row['post_count'])) $this->setCache('post_count', (int) $row['post_count']); @@ -57,6 +61,16 @@ final class TagEntity extends AbstractEntity implements IValidatable, ISerializa return $this->name; } + public function getCreationDate() + { + return $this->creationDate; + } + + public function getUpdateDate() + { + return $this->updateDate; + } + public function getPostCount() { if ($this->hasCache('post_count')) diff --git a/src/Models/PostModel.php b/src/Models/PostModel.php index 7063e473..5481ace3 100644 --- a/src/Models/PostModel.php +++ b/src/Models/PostModel.php @@ -43,47 +43,69 @@ final class PostModel extends AbstractCrudModel $stmt->setCriterion(Sql\Functors::equals('id', new Sql\Binding($post->getId()))); Core::getDatabase()->execute($stmt); - //tags - $tags = $post->getTags(); - - $stmt = Sql\Statements::delete(); - $stmt->setTable('post_tag'); - $stmt->setCriterion(Sql\Functors::equals('post_id', new Sql\Binding($post->getId()))); - Core::getDatabase()->execute($stmt); - - foreach ($tags as $postTag) - { - $stmt = Sql\Statements::insert(); - $stmt->setTable('post_tag'); - $stmt->setColumn('post_id', new Sql\Binding($post->getId())); - $stmt->setColumn('tag_id', new Sql\Binding($postTag->getId())); - Core::getDatabase()->execute($stmt); - } - - //relations - $relations = $post->getRelations(); - - $stmt = Sql\Statements::delete(); - $stmt->setTable('crossref'); - $binding = new Sql\Binding($post->getId()); - $stmt->setCriterion(Sql\Functors::disjunction() - ->add(Sql\Functors::equals('post_id', $binding)) - ->add(Sql\Functors::equals('post2_id', $binding))); - Core::getDatabase()->execute($stmt); - - foreach ($relations as $relatedPost) - { - $stmt = Sql\Statements::insert(); - $stmt->setTable('crossref'); - $stmt->setColumn('post_id', new Sql\Binding($post->getId())); - $stmt->setColumn('post2_id', new Sql\Binding($relatedPost->getId())); - Core::getDatabase()->execute($stmt); - } + self::saveTags($post); + self::saveRelations($post); }); return $post; } + private static function saveTags($post) + { + $newTagIds = array_map(function($tag) { return $tag->getId(); }, $post->getTags()); + + $stmt = Sql\Statements::select(); + $stmt->setTable('post_tag'); + $stmt->setColumn('tag_id'); + $stmt->setCriterion(Sql\Functors::equals('post_id', new Sql\Binding($post->getId()))); + $rows = Core::getDatabase()->fetchAll($stmt); + $oldTagIds = array_map(function($row) { return $row['tag_id']; }, $rows); + + $tagIdsToInsert = array_diff($newTagIds, $oldTagIds); + $tagIdsToDelete = array_diff($oldTagIds, $newTagIds); + + if (count($tagIdsToDelete)) + { + $stmt = Sql\Statements::delete(); + $stmt->setTable('post_tag'); + $stmt->setCriterion(Sql\Functors::conjunction() + ->add(Sql\Functors::in('tag_id', Sql\Binding::fromArray($tagIdsToDelete))) + ->add(Sql\Functors::equals('post_id', new Sql\Binding($post->getId())))); + Core::getDatabase()->execute($stmt); + } + + foreach ($tagIdsToInsert as $tagIdToInsert) + { + $stmt = Sql\Statements::insert(); + $stmt->setTable('post_tag'); + $stmt->setColumn('post_id', new Sql\Binding($post->getId())); + $stmt->setColumn('tag_id', new Sql\Binding($tagIdToInsert)); + Core::getDatabase()->execute($stmt); + } + } + + private static function saveRelations($post) + { + $relations = $post->getRelations(); + + $stmt = Sql\Statements::delete(); + $stmt->setTable('crossref'); + $binding = new Sql\Binding($post->getId()); + $stmt->setCriterion(Sql\Functors::disjunction() + ->add(Sql\Functors::equals('post_id', $binding)) + ->add(Sql\Functors::equals('post2_id', $binding))); + Core::getDatabase()->execute($stmt); + + foreach ($relations as $relatedPost) + { + $stmt = Sql\Statements::insert(); + $stmt->setTable('crossref'); + $stmt->setColumn('post_id', new Sql\Binding($post->getId())); + $stmt->setColumn('post2_id', new Sql\Binding($relatedPost->getId())); + Core::getDatabase()->execute($stmt); + } + } + protected static function removeSingle($post) { Core::getDatabase()->transaction(function() use ($post) diff --git a/src/Models/SearchParsers/TagSearchParser.php b/src/Models/SearchParsers/TagSearchParser.php index 7d4a1acc..ed50142d 100644 --- a/src/Models/SearchParsers/TagSearchParser.php +++ b/src/Models/SearchParsers/TagSearchParser.php @@ -38,6 +38,10 @@ class TagSearchParser extends AbstractSearchParser { if ($orderByString == 'popularity') $this->statement->setOrderBy('post_count', $orderDir); + elseif ($orderByString == 'creation_date') + $this->statement->setOrderBy('tag.creation_date', $orderDir); + elseif ($orderByString == 'update_date') + $this->statement->setOrderBy('tag.update_date', $orderDir); elseif ($orderByString == 'alpha') $this->statement->setOrderBy(Sql\Functors::{'case'}('tag.name'), $orderDir); else diff --git a/src/Upgrades/mysql/Upgrade17.sql b/src/Upgrades/mysql/Upgrade17.sql new file mode 100644 index 00000000..595d840e --- /dev/null +++ b/src/Upgrades/mysql/Upgrade17.sql @@ -0,0 +1,15 @@ +ALTER TABLE tag ADD COLUMN creation_date INTEGER DEFAULT NULL; +ALTER TABLE tag ADD COLUMN update_date INTEGER DEFAULT NULL; + +DROP TRIGGER post_tag_insert; +CREATE TRIGGER post_tag_insert AFTER INSERT ON post_tag FOR EACH ROW +BEGIN + UPDATE post SET tag_count = tag_count + 1 WHERE post.id = NEW.post_id; + UPDATE tag SET update_date = UNIX_TIMESTAMP() WHERE tag.id = NEW.tag_id; +END; + +CREATE TRIGGER tag_insert BEFORE INSERT ON tag FOR EACH ROW +BEGIN + SET NEW.creation_date = UNIX_TIMESTAMP(); + SET NEW.update_date = UNIX_TIMESTAMP(); +END; diff --git a/src/Upgrades/sqlite/Upgrade17.sql b/src/Upgrades/sqlite/Upgrade17.sql new file mode 100644 index 00000000..360f6ca8 --- /dev/null +++ b/src/Upgrades/sqlite/Upgrade17.sql @@ -0,0 +1,15 @@ +ALTER TABLE tag ADD COLUMN creation_date INTEGER DEFAULT NULL; +ALTER TABLE tag ADD COLUMN update_date INTEGER DEFAULT NULL; + +DROP TRIGGER post_tag_insert; +CREATE TRIGGER post_tag_insert AFTER INSERT ON post_tag FOR EACH ROW +BEGIN + UPDATE post SET tag_count = tag_count + 1 WHERE post.id = NEW.post_id; + UPDATE tag SET update_date = STRFTIME('%s', 'NOW') WHERE tag.id = NEW.tag_id; +END; + +CREATE TRIGGER tag_insert AFTER INSERT ON tag FOR EACH ROW +BEGIN + UPDATE tag SET creation_date = STRFTIME('%s', 'NOW') WHERE tag.id = NEW.id; + UPDATE tag SET update_date = STRFTIME('%s', 'NOW') WHERE tag.id = NEW.id; +END; diff --git a/src/Views/tag/tag-list.phtml b/src/Views/tag/tag-list.phtml index 1b1b3bf0..75cbc85e 100644 --- a/src/Views/tag/tag-list.phtml +++ b/src/Views/tag/tag-list.phtml @@ -7,6 +7,8 @@ 'order:alpha,desc' => 'Sort Z→A', 'order:popularity,desc' => 'Often used first', 'order:popularity,asc' => 'Rarely used first', + 'order:update_date,desc' => 'Recently used', + 'order:creation_date,desc' => 'Recently created', ]; ?> diff --git a/tests/Tests/ModelTests/TagModelTest.php b/tests/Tests/ModelTests/TagModelTest.php new file mode 100644 index 00000000..1a836d0d --- /dev/null +++ b/tests/Tests/ModelTests/TagModelTest.php @@ -0,0 +1,16 @@ +setName('test'); + + TagModel::save($tag); + $otherTag = TagModel::getById($tag->getId()); + + $this->assert->areEqual($tag->getName(), $otherTag->getName()); + $this->assert->areEqual(time(), $otherTag->getCreationDate()); + $this->assert->areEqual(time(), $otherTag->getUpdateDate()); + } +}