diff --git a/src/Dao/CommentDao.php b/src/Dao/CommentDao.php new file mode 100644 index 00000000..734db687 --- /dev/null +++ b/src/Dao/CommentDao.php @@ -0,0 +1,44 @@ +userDao = $userDao; + $this->postDao = $postDao; + } + + public function findByPost(\Szurubooru\Entities\Post $post) + { + return $this->findBy('postId', $post->getId()); + } + + protected function afterLoad(\Szurubooru\Entities\Entity $comment) + { + $comment->setLazyLoader( + \Szurubooru\Entities\Comment::LAZY_LOADER_USER, + function (\Szurubooru\Entities\Comment $comment) + { + return $this->userDao->findById($comment->getUserId()); + }); + + $comment->setLazyLoader( + \Szurubooru\Entities\Comment::LAZY_LOADER_POST, + function (\Szurubooru\Entities\Comment $comment) + { + return $this->postDao->findById($comment->getPostId()); + }); + } +} diff --git a/src/Dao/EntityConverters/CommentEntityConverter.php b/src/Dao/EntityConverters/CommentEntityConverter.php new file mode 100644 index 00000000..c39363c2 --- /dev/null +++ b/src/Dao/EntityConverters/CommentEntityConverter.php @@ -0,0 +1,29 @@ + $entity->getId(), + 'userId' => $entity->getUserId(), + 'postId' => $entity->getPostId(), + 'text' => $entity->getText(), + 'lastEditTime' => $this->entityTimeToDbTime($entity->getCreationTime()), + 'creationTime' => $this->entityTimeToDbTime($entity->getLastEditTime()), + ]; + } + + public function toBasicEntity(array $array) + { + $entity = new \Szurubooru\Entities\Comment($array['id']); + $entity->setUserId($array['userId']); + $entity->setPostId($array['postId']); + $entity->setText($array['text']); + $entity->setCreationTime($this->dbTimeToEntityTime($array['creationTime'])); + $entity->setLastEditTime($this->dbTimeToEntityTime($array['lastEditTime'])); + return $entity; + } +} diff --git a/src/Dao/EntityConverters/PostEntityConverter.php b/src/Dao/EntityConverters/PostEntityConverter.php index cfe44f96..3fbc3916 100644 --- a/src/Dao/EntityConverters/PostEntityConverter.php +++ b/src/Dao/EntityConverters/PostEntityConverter.php @@ -46,6 +46,7 @@ class PostEntityConverter extends AbstractEntityConverter implements IEntityConv $entity->setLastFeatureTime($this->dbTimeToEntityTime($array['lastFeatureTime'])); $entity->setMeta(\Szurubooru\Entities\Post::META_TAG_COUNT, intval($array['tagCount'])); $entity->setMeta(\Szurubooru\Entities\Post::META_FAV_COUNT, intval($array['favCount'])); + $entity->setMeta(\Szurubooru\Entities\Post::META_COMMENT_COUNT, intval($array['commentCount'])); $entity->setMeta(\Szurubooru\Entities\Post::META_SCORE, intval($array['score'])); return $entity; } diff --git a/src/Entities/Comment.php b/src/Entities/Comment.php new file mode 100644 index 00000000..3449995e --- /dev/null +++ b/src/Entities/Comment.php @@ -0,0 +1,86 @@ +userId; + } + + public function setUserId($userId) + { + $this->userId = $userId; + } + + public function getPostId() + { + return $this->postId; + } + + public function setPostId($postId) + { + $this->postId = $postId; + } + + public function getCreationTime() + { + return $this->creationTime; + } + + public function setCreationTime($creationTime) + { + $this->creationTime = $creationTime; + } + + public function getLastEditTime() + { + return $this->lastEditTime; + } + + public function setLastEditTime($lastEditTime) + { + $this->lastEditTime = $lastEditTime; + } + + public function getText() + { + return $this->text; + } + + public function setText($text) + { + $this->text = $text; + } + + public function getUser() + { + return $this->lazyLoad(self::LAZY_LOADER_USER, null); + } + + public function setUser(\Szurubooru\Entities\User $user) + { + $this->lazySave(self::LAZY_LOADER_USER, $user); + $this->userId = $user->getId(); + } + + public function getPost() + { + return $this->lazyLoad(self::LAZY_LOADER_POST, null); + } + + public function setPost(\Szurubooru\Entities\Post $post) + { + $this->lazySave(self::LAZY_LOADER_POST, $post); + $this->postId = $post->getId(); + } +} diff --git a/src/Entities/Post.php b/src/Entities/Post.php index bbc14005..59aea9ee 100644 --- a/src/Entities/Post.php +++ b/src/Entities/Post.php @@ -20,6 +20,7 @@ final class Post extends Entity const META_TAG_COUNT = 'tagCount'; const META_FAV_COUNT = 'favCount'; + const META_COMMENT_COUNT = 'commentCount'; const META_SCORE = 'score'; protected $name; @@ -265,6 +266,11 @@ final class Post extends Entity return $this->getMeta(self::META_FAV_COUNT, 0); } + public function getCommentCount() + { + return $this->getMeta(self::META_COMMENT_COUNT, 0); + } + public function getScore() { return $this->getMeta(self::META_SCORE, 0); diff --git a/src/Upgrades/Upgrade14.php b/src/Upgrades/Upgrade14.php new file mode 100644 index 00000000..d3dd4926 --- /dev/null +++ b/src/Upgrades/Upgrade14.php @@ -0,0 +1,54 @@ +getPDO(); + $driver = $databaseConnection->getDriver(); + + $pdo->exec('CREATE TABLE comments + ( + id INTEGER PRIMARY KEY ' . ($driver === 'mysql' ? 'AUTO_INCREMENT' : 'AUTOINCREMENT') . ', + postId INTEGER NOT NULL, + userId INTEGER, + creationTime DATETIME NOT NULL, + lastEditTime DATETIME NOT NULL, + text TEXT + )'); + + $pdo->exec(' + CREATE TRIGGER commentsDelete AFTER DELETE ON comments + FOR EACH ROW + BEGIN + UPDATE posts SET + commentCount = (SELECT COUNT(1) FROM comments WHERE comments.postId = posts.id), + lastCommentTime = (SELECT MAX(lastEditTime) FROM comments WHERE comments.postId = posts.id) + WHERE posts.id = OLD.postId; + END'); + + $pdo->exec(' + CREATE TRIGGER commentsInsert AFTER INSERT ON comments + FOR EACH ROW + BEGIN + UPDATE posts SET + commentCount = (SELECT COUNT(1) FROM comments WHERE comments.postId = posts.id), + lastCommentTime = (SELECT MAX(lastEditTime) FROM comments WHERE comments.postId = posts.id) + WHERE posts.id = NEW.postId; + END'); + + $pdo->exec(' + CREATE TRIGGER commentsUpdate AFTER UPDATE ON comments + FOR EACH ROW + BEGIN + UPDATE posts SET + commentCount = (SELECT COUNT(1) FROM comments WHERE comments.postId = posts.id), + lastCommentTime = (SELECT MAX(lastEditTime) FROM comments WHERE comments.postId = posts.id) + WHERE posts.id IN (OLD.postId, NEW.postId); + END'); + + $pdo->exec('ALTER TABLE posts ADD COLUMN commentCount INTEGER NOT NULL DEFAULT 0'); + $pdo->exec('ALTER TABLE posts ADD COLUMN lastCommentTime DATETIME'); + } +} diff --git a/src/di.php b/src/di.php index b898a0c9..c0693f05 100644 --- a/src/di.php +++ b/src/di.php @@ -29,6 +29,7 @@ return [ $container->get(\Szurubooru\Upgrades\Upgrade11::class), $container->get(\Szurubooru\Upgrades\Upgrade12::class), $container->get(\Szurubooru\Upgrades\Upgrade13::class), + $container->get(\Szurubooru\Upgrades\Upgrade14::class), ]; }), diff --git a/tests/AbstractDatabaseTestCase.php b/tests/AbstractDatabaseTestCase.php index cb5ba04c..ab2a14d2 100644 --- a/tests/AbstractDatabaseTestCase.php +++ b/tests/AbstractDatabaseTestCase.php @@ -29,4 +29,26 @@ abstract class AbstractDatabaseTestCase extends \Szurubooru\Tests\AbstractTestCa if ($this->databaseConnection) $this->databaseConnection->close(); } + + protected static function getTestPost() + { + $post = new \Szurubooru\Entities\Post(); + $post->setName('test'); + $post->setUploadTime(date('c')); + $post->setSafety(\Szurubooru\Entities\Post::POST_SAFETY_SAFE); + $post->setContentType(\Szurubooru\Entities\Post::POST_TYPE_YOUTUBE); + $post->setContentChecksum('whatever'); + return $post; + } + + protected static function getTestUser($userName = 'test') + { + $user = new \Szurubooru\Entities\User(); + $user->setName($userName); + $user->setPasswordHash('whatever'); + $user->setLastLoginTime(date('c', mktime(1, 2, 3))); + $user->setRegistrationTime(date('c', mktime(3, 2, 1))); + $user->setAccessRank(\Szurubooru\Entities\User::ACCESS_RANK_REGULAR_USER); + return $user; + } } diff --git a/tests/Dao/CommentDaoTest.php b/tests/Dao/CommentDaoTest.php new file mode 100644 index 00000000..1c8044fb --- /dev/null +++ b/tests/Dao/CommentDaoTest.php @@ -0,0 +1,102 @@ +userDaoMock = $this->mock(\Szurubooru\Dao\UserDao::class); + $this->postDaoMock = $this->mock(\Szurubooru\Dao\PostDao::class); + } + + public function testSaving() + { + $user = new \Szurubooru\Entities\User(1); + $user->setName('olivia'); + + $post = new \Szurubooru\Entities\Post(2); + $post->setName('sword'); + + $comment = new \Szurubooru\Entities\Comment(); + $comment->setUser($user); + $comment->setPost($post); + $comment->setCreationTime(date('c')); + $comment->setLastEditTime(date('c')); + $comment->setText('whatever'); + $commentDao = $this->getCommentDao(); + $commentDao->save($comment); + + $this->userDaoMock->expects($this->once())->method('findById')->with(1)->willReturn($user); + $this->postDaoMock->expects($this->once())->method('findById')->with(2)->willReturn($post); + + $savedComment = $commentDao->findById($comment->getId()); + $this->assertEquals(1, $savedComment->getUserId()); + $this->assertEquals(2, $savedComment->getPostId()); + $this->assertEquals($comment->getCreationTime(), $savedComment->getCreationTime()); + $this->assertEquals($comment->getLastEditTime(), $savedComment->getLastEditTime()); + $this->assertEquals($comment->getText(), $savedComment->getText()); + $this->assertEntitiesEqual($user, $savedComment->getUser()); + $this->assertEntitiesEqual($post, $savedComment->getPost()); + } + + public function testPostMetadataSyncInsert() + { + $userDao = \Szurubooru\Injector::get(\Szurubooru\Dao\UserDao::class); + $postDao = \Szurubooru\Injector::get(\Szurubooru\Dao\PostDao::class); + $commentDao = \Szurubooru\Injector::get(\Szurubooru\Dao\CommentDao::class); + + $user = self::getTestUser('olivia'); + $userDao->save($user); + + $post = self::getTestPost(); + $postDao->save($post); + + $this->assertEquals(0, $post->getCommentCount()); + $this->assertNotNull($post->getId()); + + $comment = new \Szurubooru\Entities\Comment(); + $comment->setUser($user); + $comment->setPost($post); + $comment->setCreationTime(date('c')); + $comment->setLastEditTime(date('c')); + $comment->setText('whatever'); + $commentDao->save($comment); + + $post = $postDao->findById($post->getId()); + $this->assertNotNull($post); + $this->assertEquals(1, $post->getCommentCount()); + + return [$postDao, $commentDao, $post, $comment]; + } + + /** + * @depends testPostMetadataSyncInsert + */ + public function testPostMetadataSyncDelete($args) + { + list ($postDao, $commentDao, $post, $comment) = $args; + + $commentDao->deleteById($comment->getId()); + + $post = $postDao->findById($post->getId()); + $this->assertNotNull($post); + $this->assertEquals(0, $post->getCommentCount()); + } + + public function findByPost(\Szurubooru\Entities\Post $post) + { + return $this->findOneBy('postId', $post->getId()); + } + + private function getCommentDao() + { + return new \Szurubooru\Dao\CommentDao( + $this->databaseConnection, + $this->userDaoMock, + $this->postDaoMock); + } +} diff --git a/tests/Dao/PostDaoTest.php b/tests/Dao/PostDaoTest.php index afed77e8..13063b59 100644 --- a/tests/Dao/PostDaoTest.php +++ b/tests/Dao/PostDaoTest.php @@ -24,7 +24,7 @@ final class PostDaoTest extends \Szurubooru\Tests\AbstractDatabaseTestCase { $postDao = $this->getPostDao(); - $post = $this->getPost(); + $post = self::getTestPost(); $savedPost = $postDao->save($post); $this->assertEquals('test', $post->getName()); $this->assertNotNull($savedPost->getId()); @@ -33,7 +33,7 @@ final class PostDaoTest extends \Szurubooru\Tests\AbstractDatabaseTestCase public function testUpdating() { $postDao = $this->getPostDao(); - $post = $this->getPost(); + $post = self::getTestPost(); $post = $postDao->save($post); $this->assertEquals('test', $post->getName()); $id = $post->getId(); @@ -47,8 +47,8 @@ final class PostDaoTest extends \Szurubooru\Tests\AbstractDatabaseTestCase { $postDao = $this->getPostDao(); - $post1 = $this->getPost(); - $post2 = $this->getPost(); + $post1 = self::getTestPost(); + $post2 = self::getTestPost(); $postDao->save($post1); $postDao->save($post2); @@ -67,9 +67,9 @@ final class PostDaoTest extends \Szurubooru\Tests\AbstractDatabaseTestCase { $postDao = $this->getPostDao(); - $post1 = $this->getPost(); - $post2 = $this->getPost(); - $post3 = $this->getPost(); + $post1 = self::getTestPost(); + $post2 = self::getTestPost(); + $post3 = self::getTestPost(); $post1->setOriginalFileSize(1249812); $post2->setOriginalFileSize(128); $post3->setOriginalFileSize(null); @@ -88,8 +88,8 @@ final class PostDaoTest extends \Szurubooru\Tests\AbstractDatabaseTestCase { $postDao = $this->getPostDao(); - $post1 = $this->getPost(); - $post2 = $this->getPost(); + $post1 = self::getTestPost(); + $post2 = self::getTestPost(); $postDao->save($post1); $postDao->save($post2); @@ -103,8 +103,8 @@ final class PostDaoTest extends \Szurubooru\Tests\AbstractDatabaseTestCase { $postDao = $this->getPostDao(); - $post1 = $this->getPost(); - $post2 = $this->getPost(); + $post1 = self::getTestPost(); + $post2 = self::getTestPost(); $postDao->save($post1); $postDao->save($post2); @@ -121,8 +121,8 @@ final class PostDaoTest extends \Szurubooru\Tests\AbstractDatabaseTestCase { $postDao = $this->getPostDao(); - $post1 = $this->getPost(); - $post2 = $this->getPost(); + $post1 = self::getTestPost(); + $post2 = self::getTestPost(); $postDao->save($post1); $postDao->save($post2); @@ -144,7 +144,7 @@ final class PostDaoTest extends \Szurubooru\Tests\AbstractDatabaseTestCase $testTags = ['tag1' => $tag1, 'tag2' => $tag2]; $postDao = $this->getPostDao(); - $post = $this->getPost(); + $post = self::getTestPost(); $post->setTags($testTags); $postDao->save($post); @@ -161,12 +161,12 @@ final class PostDaoTest extends \Szurubooru\Tests\AbstractDatabaseTestCase public function testSavingUnsavedRelations() { - $post1 = $this->getPost(); - $post2 = $this->getPost(); + $post1 = self::getTestPost(); + $post2 = self::getTestPost(); $testPosts = [$post1, $post2]; $postDao = $this->getPostDao(); - $post = $this->getPost(); + $post = self::getTestPost(); $post->setRelatedPosts($testPosts); $this->setExpectedException(\Exception::class, 'Unsaved entities found'); $postDao->save($post); @@ -174,14 +174,14 @@ final class PostDaoTest extends \Szurubooru\Tests\AbstractDatabaseTestCase public function testSavingRelations() { - $post1 = $this->getPost(); - $post2 = $this->getPost(); + $post1 = self::getTestPost(); + $post2 = self::getTestPost(); $testPosts = [$post1, $post2]; $postDao = $this->getPostDao(); $postDao->save($post1); $postDao->save($post2); - $post = $this->getPost(); + $post = self::getTestPost(); $post->setRelatedPosts($testPosts); $postDao->save($post); @@ -196,7 +196,7 @@ final class PostDaoTest extends \Szurubooru\Tests\AbstractDatabaseTestCase $testUser->setName('it\'s me'); $postDao = $this->getPostDao(); - $post = $this->getPost(); + $post = self::getTestPost(); $post->setUser($testUser); $postDao->save($post); @@ -208,14 +208,14 @@ final class PostDaoTest extends \Szurubooru\Tests\AbstractDatabaseTestCase public function testNotLoadingContentForNewPosts() { $postDao = $this->getPostDao(); - $newlyCreatedPost = $this->getPost(); + $newlyCreatedPost = self::getTestPost(); $this->assertNull($newlyCreatedPost->getContent()); } public function testLoadingContentPostsForExistingPosts() { $postDao = $this->getPostDao(); - $post = $this->getPost(); + $post = self::getTestPost(); $postDao->save($post); $post = $postDao->findById($post->getId()); @@ -232,7 +232,7 @@ final class PostDaoTest extends \Szurubooru\Tests\AbstractDatabaseTestCase public function testSavingContent() { $postDao = $this->getPostDao(); - $post = $this->getPost(); + $post = self::getTestPost(); $post->setContent('whatever'); $this->thumbnailServiceMock @@ -251,10 +251,11 @@ final class PostDaoTest extends \Szurubooru\Tests\AbstractDatabaseTestCase $postDao->save($post); } + public function testSavingContentAndThumbnail() { $postDao = $this->getPostDao(); - $post = $this->getPost(); + $post = self::getTestPost(); $post->setContent('whatever'); $post->setThumbnailSourceContent('an image of sharks'); @@ -291,15 +292,4 @@ final class PostDaoTest extends \Szurubooru\Tests\AbstractDatabaseTestCase { return $this->tagDao; } - - private function getPost() - { - $post = new \Szurubooru\Entities\Post(); - $post->setName('test'); - $post->setUploadTime(date('c')); - $post->setSafety(\Szurubooru\Entities\Post::POST_SAFETY_SAFE); - $post->setContentType(\Szurubooru\Entities\Post::POST_TYPE_YOUTUBE); - $post->setContentChecksum('whatever'); - return $post; - } } diff --git a/tests/Dao/UserDaoFilterTest.php b/tests/Dao/UserDaoFilterTest.php index abf8e3f5..e7becb4e 100644 --- a/tests/Dao/UserDaoFilterTest.php +++ b/tests/Dao/UserDaoFilterTest.php @@ -145,15 +145,4 @@ class UserDaoFilterTest extends \Szurubooru\Tests\AbstractDatabaseTestCase $this->fileServiceMock, $this->thumbnailServiceMock); } - - private static function getTestUser($userName) - { - $user = new \Szurubooru\Entities\User(); - $user->setName($userName); - $user->setPasswordHash('whatever'); - $user->setLastLoginTime(date('c', mktime(1, 2, 3))); - $user->setRegistrationTime(date('c', mktime(3, 2, 1))); - $user->setAccessRank(\Szurubooru\Entities\User::ACCESS_RANK_REGULAR_USER); - return $user; - } } diff --git a/tests/Dao/UserDaoTest.php b/tests/Dao/UserDaoTest.php index 2d7c1c78..f98f42af 100644 --- a/tests/Dao/UserDaoTest.php +++ b/tests/Dao/UserDaoTest.php @@ -18,7 +18,7 @@ final class UserDaoTest extends \Szurubooru\Tests\AbstractDatabaseTestCase { $userDao = $this->getUserDao(); - $user = $this->getTestUser(); + $user = self::getTestUser(); $userDao->save($user); $expected = $user; @@ -40,7 +40,7 @@ final class UserDaoTest extends \Szurubooru\Tests\AbstractDatabaseTestCase $userDao = $this->getUserDao(); $this->assertFalse($userDao->hasAnyUsers()); - $user = $this->getTestUser(); + $user = self::getTestUser(); $userDao->save($user); $this->assertTrue($userDao->hasAnyUsers()); } @@ -48,7 +48,7 @@ final class UserDaoTest extends \Szurubooru\Tests\AbstractDatabaseTestCase public function testNotLoadingAvatarContentForNewUsers() { $userDao = $this->getUserDao(); - $user = $this->getTestUser(); + $user = self::getTestUser(); $user->setAvatarStyle(\Szurubooru\Entities\User::AVATAR_STYLE_MANUAL); $userDao->save($user); @@ -58,7 +58,7 @@ final class UserDaoTest extends \Szurubooru\Tests\AbstractDatabaseTestCase public function testLoadingContentUsersForExistingUsers() { $userDao = $this->getUserDao(); - $user = $this->getTestUser(); + $user = self::getTestUser(); $user->setAvatarStyle(\Szurubooru\Entities\User::AVATAR_STYLE_MANUAL); $userDao->save($user); @@ -75,7 +75,7 @@ final class UserDaoTest extends \Szurubooru\Tests\AbstractDatabaseTestCase public function testSavingContent() { $userDao = $this->getUserDao(); - $user = $this->getTestUser(); + $user = self::getTestUser(); $user->setAvatarStyle(\Szurubooru\Entities\User::AVATAR_STYLE_MANUAL); $user->setCustomAvatarSourceContent('whatever'); @@ -108,15 +108,4 @@ final class UserDaoTest extends \Szurubooru\Tests\AbstractDatabaseTestCase $this->fileServiceMock, $this->thumbnailServiceMock); } - - private function getTestUser() - { - $user = new \Szurubooru\Entities\User(); - $user->setName('test'); - $user->setPasswordHash('whatever'); - $user->setLastLoginTime(date('c', mktime(1, 2, 3))); - $user->setRegistrationTime(date('c', mktime(3, 2, 1))); - $user->setAccessRank(\Szurubooru\Entities\User::ACCESS_RANK_REGULAR_USER); - return $user; - } }