From 2e9f0092b333e8e4ecdbc25ab7dac79d905c24f3 Mon Sep 17 00:00:00 2001 From: Tomasz Kowalewski Date: Tue, 12 Nov 2024 13:15:37 +0100 Subject: [PATCH] OXDEV-8778 Code refactor --- .../Controller/ArticleDetailsController.php | 33 ++--- src/ProductVote/Dao/ProductVoteDao.php | 21 ++- .../Dao/ProductVoteDaoInterface.php | 6 +- .../Dao/{ResultDao.php => VoteResultDao.php} | 20 ++- ...terface.php => VoteResultDaoInterface.php} | 6 +- ...ataMapper.php => VoteResultDataMapper.php} | 8 +- ....php => VoteResultDataMapperInterface.php} | 6 +- src/ProductVote/DataType/ProductVote.php | 17 ++- .../{Result.php => ProductVoteInterface.php} | 11 +- src/ProductVote/DataType/VoteResult.php | 35 +++++ .../DataType/VoteResultInterface.php | 17 +++ src/ProductVote/Service/VoteService.php | 48 +++++++ .../Service/VoteServiceInterface.php | 24 ++++ src/ProductVote/Widget/ArticleDetails.php | 25 +--- src/ProductVote/services.yaml | 14 +- .../ArticleDetailsControllerTest.php | 66 ++++++---- .../ProductVote/Dao/ResultDaoTest.php | 20 +-- .../ProductVote/Widget/ArticleDetailsTest.php | 44 +++---- .../DataMapper/ResultDataMapperTest.php | 16 +-- .../ProductVote/Service/VoteServiceTest.php | 121 ++++++++++++++++++ .../page/details/inc/productmain.html.twig | 13 +- 21 files changed, 404 insertions(+), 167 deletions(-) rename src/ProductVote/Dao/{ResultDao.php => VoteResultDao.php} (65%) rename src/ProductVote/Dao/{ResultDaoInterface.php => VoteResultDaoInterface.php} (51%) rename src/ProductVote/DataMapper/{ResultDataMapper.php => VoteResultDataMapper.php} (60%) rename src/ProductVote/DataMapper/{ResultDataMapperInterface.php => VoteResultDataMapperInterface.php} (56%) rename src/ProductVote/DataType/{Result.php => ProductVoteInterface.php} (55%) create mode 100644 src/ProductVote/DataType/VoteResult.php create mode 100644 src/ProductVote/DataType/VoteResultInterface.php create mode 100644 src/ProductVote/Service/VoteService.php create mode 100644 src/ProductVote/Service/VoteServiceInterface.php create mode 100644 tests/Unit/ProductVote/Service/VoteServiceTest.php diff --git a/src/ProductVote/Controller/ArticleDetailsController.php b/src/ProductVote/Controller/ArticleDetailsController.php index 45ae514..b1390f9 100644 --- a/src/ProductVote/Controller/ArticleDetailsController.php +++ b/src/ProductVote/Controller/ArticleDetailsController.php @@ -10,8 +10,7 @@ namespace OxidEsales\ModuleTemplate\ProductVote\Controller; use OxidEsales\Eshop\Application\Model\User; -use OxidEsales\ModuleTemplate\ProductVote\Dao\ProductVoteDaoInterface; -use OxidEsales\ModuleTemplate\ProductVote\DataType\ProductVote; +use OxidEsales\ModuleTemplate\ProductVote\Service\VoteServiceInterface; /** * @extendable-class @@ -35,39 +34,23 @@ public function voteDown(): void public function resetVote(): void { - $userId = $this->getUserId(); - if (!$userId) { + $user = $this->getUser(); + if (!($user instanceof User)) { return; } - $productVoteDao = $this->getProductVoteDao(); - $productVoteDao->resetProductVote($this->getProduct()->getId(), $userId); + $voteService = $this->getService(VoteServiceInterface::class); + $voteService->resetProductVote($this->getProduct(), $user); } private function vote(bool $isUp): void - { - $userId = $this->getUserId(); - if (!$userId) { - return; - } - - $productVoteDao = $this->getProductVoteDao(); - $vote = new ProductVote($this->getProduct()->getId(), $userId, $isUp); - $productVoteDao->setProductVote($vote); - } - - private function getUserId(): ?string { $user = $this->getUser(); if (!($user instanceof User)) { - return null; + return; } - return $user->getId(); - } - - private function getProductVoteDao(): ProductVoteDaoInterface - { - return $this->getService(ProductVoteDaoInterface::class); + $voteService = $this->getService(VoteServiceInterface::class); + $voteService->setProductVote($this->getProduct(), $user, $isUp); } } diff --git a/src/ProductVote/Dao/ProductVoteDao.php b/src/ProductVote/Dao/ProductVoteDao.php index c897426..a0a8293 100644 --- a/src/ProductVote/Dao/ProductVoteDao.php +++ b/src/ProductVote/Dao/ProductVoteDao.php @@ -12,8 +12,7 @@ use Doctrine\DBAL\Result; use OxidEsales\EshopCommunity\Internal\Framework\Database\QueryBuilderFactoryInterface; use OxidEsales\ModuleTemplate\ProductVote\DataMapper\ProductVoteDataMapperInterface; -use OxidEsales\ModuleTemplate\ProductVote\DataType\ProductVote; -use RuntimeException; +use OxidEsales\ModuleTemplate\ProductVote\DataType\ProductVoteInterface; readonly class ProductVoteDao implements ProductVoteDaoInterface { @@ -23,7 +22,7 @@ public function __construct( ) { } - public function getProductVote(string $productId, string $userId): ?ProductVote + public function getProductVote(string $productId, string $userId): ?ProductVoteInterface { $queryBuilder = $this->queryBuilderFactory->create(); $queryBuilder @@ -40,12 +39,10 @@ public function getProductVote(string $productId, string $userId): ?ProductVote 'userId' => $userId, ]); + /** @var Result $result */ $result = $queryBuilder->execute(); - if (!($result instanceof Result)) { - throw new RuntimeException('Query returned error.'); - } - $row = $result->fetchAssociative(); + if ($row === false) { return null; } @@ -53,9 +50,9 @@ public function getProductVote(string $productId, string $userId): ?ProductVote return $this->dataMapper->map($row); } - public function setProductVote(ProductVote $vote): void + public function setProductVote(ProductVoteInterface $vote): void { - $this->resetProductVote($vote->productId, $vote->userId); + $this->resetProductVote($vote->getProductId(), $vote->getUserId()); $queryBuilder = $this->queryBuilderFactory->create(); $queryBuilder @@ -68,9 +65,9 @@ public function setProductVote(ProductVote $vote): void ]) ->setParameters([ 'oxid' => uniqid(), - 'productId' => $vote->productId, - 'userId' => $vote->userId, - 'vote' => (int)$vote->vote, + 'productId' => $vote->getProductId(), + 'userId' => $vote->getUserId(), + 'vote' => (int)$vote->isVoteUp(), ]) ->execute(); } diff --git a/src/ProductVote/Dao/ProductVoteDaoInterface.php b/src/ProductVote/Dao/ProductVoteDaoInterface.php index 477912c..a44d103 100644 --- a/src/ProductVote/Dao/ProductVoteDaoInterface.php +++ b/src/ProductVote/Dao/ProductVoteDaoInterface.php @@ -9,12 +9,12 @@ namespace OxidEsales\ModuleTemplate\ProductVote\Dao; -use OxidEsales\ModuleTemplate\ProductVote\DataType\ProductVote; +use OxidEsales\ModuleTemplate\ProductVote\DataType\ProductVoteInterface; interface ProductVoteDaoInterface { - public function getProductVote(string $productId, string $userId): ?ProductVote; + public function getProductVote(string $productId, string $userId): ?ProductVoteInterface; - public function setProductVote(ProductVote $vote): void; + public function setProductVote(ProductVoteInterface $vote): void; public function resetProductVote(string $productId, string $userId): void; } diff --git a/src/ProductVote/Dao/ResultDao.php b/src/ProductVote/Dao/VoteResultDao.php similarity index 65% rename from src/ProductVote/Dao/ResultDao.php rename to src/ProductVote/Dao/VoteResultDao.php index 06adabd..dfa2f6d 100644 --- a/src/ProductVote/Dao/ResultDao.php +++ b/src/ProductVote/Dao/VoteResultDao.php @@ -9,20 +9,21 @@ namespace OxidEsales\ModuleTemplate\ProductVote\Dao; +use Doctrine\DBAL\Result; use OxidEsales\EshopCommunity\Internal\Framework\Database\QueryBuilderFactoryInterface; -use OxidEsales\ModuleTemplate\ProductVote\DataMapper\ResultDataMapperInterface; -use OxidEsales\ModuleTemplate\ProductVote\DataType\Result; -use RuntimeException; +use OxidEsales\ModuleTemplate\ProductVote\DataMapper\VoteResultDataMapperInterface; +use OxidEsales\ModuleTemplate\ProductVote\DataType\VoteResult; +use OxidEsales\ModuleTemplate\ProductVote\DataType\VoteResultInterface; -readonly class ResultDao implements ResultDaoInterface +readonly class VoteResultDao implements VoteResultDaoInterface { public function __construct( private QueryBuilderFactoryInterface $queryBuilderFactory, - private ResultDataMapperInterface $dataMapper, + private VoteResultDataMapperInterface $dataMapper, ) { } - public function getProductVoteResult(string $productId): Result + public function getProductVoteResult(string $productId): VoteResultInterface { $queryBuilder = $this->queryBuilderFactory->create(); $queryBuilder @@ -38,15 +39,12 @@ public function getProductVoteResult(string $productId): Result 'productId' => $productId, ]); + /** @var Result $queryResult */ $queryResult = $queryBuilder->execute(); - if (!($queryResult instanceof \Doctrine\DBAL\Result)) { - throw new RuntimeException('Query returned error.'); - } - $row = $queryResult->fetchAssociative(); if (!$row) { - return new Result($productId, 0, 0); + return new VoteResult($productId, 0, 0); } return $this->dataMapper->map($row); } diff --git a/src/ProductVote/Dao/ResultDaoInterface.php b/src/ProductVote/Dao/VoteResultDaoInterface.php similarity index 51% rename from src/ProductVote/Dao/ResultDaoInterface.php rename to src/ProductVote/Dao/VoteResultDaoInterface.php index 94ceddb..16f4c1d 100644 --- a/src/ProductVote/Dao/ResultDaoInterface.php +++ b/src/ProductVote/Dao/VoteResultDaoInterface.php @@ -9,9 +9,9 @@ namespace OxidEsales\ModuleTemplate\ProductVote\Dao; -use OxidEsales\ModuleTemplate\ProductVote\DataType\Result; +use OxidEsales\ModuleTemplate\ProductVote\DataType\VoteResultInterface; -interface ResultDaoInterface +interface VoteResultDaoInterface { - public function getProductVoteResult(string $productId): array|Result; + public function getProductVoteResult(string $productId): VoteResultInterface; } diff --git a/src/ProductVote/DataMapper/ResultDataMapper.php b/src/ProductVote/DataMapper/VoteResultDataMapper.php similarity index 60% rename from src/ProductVote/DataMapper/ResultDataMapper.php rename to src/ProductVote/DataMapper/VoteResultDataMapper.php index d692b46..f85d53f 100644 --- a/src/ProductVote/DataMapper/ResultDataMapper.php +++ b/src/ProductVote/DataMapper/VoteResultDataMapper.php @@ -9,17 +9,17 @@ namespace OxidEsales\ModuleTemplate\ProductVote\DataMapper; -use OxidEsales\ModuleTemplate\ProductVote\DataType\Result; +use OxidEsales\ModuleTemplate\ProductVote\DataType\VoteResult; use OxidEsales\ModuleTemplate\ProductVote\Exception\MapDataTypeException; -readonly class ResultDataMapper implements ResultDataMapperInterface +readonly class VoteResultDataMapper implements VoteResultDataMapperInterface { - public function map(array $data): Result + public function map(array $data): VoteResult { if (!isset($data['ProductId']) || !isset($data['VoteUp']) || !isset($data['VoteDown'])) { throw new MapDataTypeException(); } - return new Result($data['ProductId'], (int)$data['VoteUp'], (int)$data['VoteDown']); + return new VoteResult($data['ProductId'], (int)$data['VoteUp'], (int)$data['VoteDown']); } } diff --git a/src/ProductVote/DataMapper/ResultDataMapperInterface.php b/src/ProductVote/DataMapper/VoteResultDataMapperInterface.php similarity index 56% rename from src/ProductVote/DataMapper/ResultDataMapperInterface.php rename to src/ProductVote/DataMapper/VoteResultDataMapperInterface.php index 1037b95..7466971 100644 --- a/src/ProductVote/DataMapper/ResultDataMapperInterface.php +++ b/src/ProductVote/DataMapper/VoteResultDataMapperInterface.php @@ -9,9 +9,9 @@ namespace OxidEsales\ModuleTemplate\ProductVote\DataMapper; -use OxidEsales\ModuleTemplate\ProductVote\DataType\Result; +use OxidEsales\ModuleTemplate\ProductVote\DataType\VoteResult; -interface ResultDataMapperInterface +interface VoteResultDataMapperInterface { - public function map(array $data): Result; + public function map(array $data): VoteResult; } diff --git a/src/ProductVote/DataType/ProductVote.php b/src/ProductVote/DataType/ProductVote.php index d456fd1..238d660 100644 --- a/src/ProductVote/DataType/ProductVote.php +++ b/src/ProductVote/DataType/ProductVote.php @@ -9,7 +9,7 @@ namespace OxidEsales\ModuleTemplate\ProductVote\DataType; -readonly class ProductVote +readonly class ProductVote implements ProductVoteInterface { public function __construct( public string $productId, @@ -17,4 +17,19 @@ public function __construct( public bool $vote, ) { } + + public function getProductId(): string + { + return $this->productId; + } + + public function getUserId(): string + { + return $this->userId; + } + + public function isVoteUp(): bool + { + return $this->vote; + } } diff --git a/src/ProductVote/DataType/Result.php b/src/ProductVote/DataType/ProductVoteInterface.php similarity index 55% rename from src/ProductVote/DataType/Result.php rename to src/ProductVote/DataType/ProductVoteInterface.php index 2280702..f39ba31 100644 --- a/src/ProductVote/DataType/Result.php +++ b/src/ProductVote/DataType/ProductVoteInterface.php @@ -9,12 +9,9 @@ namespace OxidEsales\ModuleTemplate\ProductVote\DataType; -readonly class Result +interface ProductVoteInterface { - public function __construct( - public string $productId, - public int $voteUp, - public int $voteDown, - ) { - } + public function getProductId(): string; + public function getUserId(): string; + public function isVoteUp(): bool; } diff --git a/src/ProductVote/DataType/VoteResult.php b/src/ProductVote/DataType/VoteResult.php new file mode 100644 index 0000000..6ddaafc --- /dev/null +++ b/src/ProductVote/DataType/VoteResult.php @@ -0,0 +1,35 @@ +productId; + } + + public function getVoteUp(): int + { + return $this->voteUp; + } + + public function getVoteDown(): int + { + return $this->voteDown; + } +} diff --git a/src/ProductVote/DataType/VoteResultInterface.php b/src/ProductVote/DataType/VoteResultInterface.php new file mode 100644 index 0000000..34f42f8 --- /dev/null +++ b/src/ProductVote/DataType/VoteResultInterface.php @@ -0,0 +1,17 @@ +productVoteDao->getProductVote($product->getId(), $user->getId()); + } + + public function setProductVote(Article $product, User $user, bool $vote): void + { + $vote = new ProductVote($product->getId(), $user->getId(), $vote); + $this->productVoteDao->setProductVote($vote); + } + + public function resetProductVote(Article $product, User $user): void + { + $this->productVoteDao->resetProductVote($product->getId(), $user->getId()); + } + + public function getProductVoteResult(Article $product): VoteResultInterface + { + return $this->voteResultDao->getProductVoteResult($product->getId()); + } +} diff --git a/src/ProductVote/Service/VoteServiceInterface.php b/src/ProductVote/Service/VoteServiceInterface.php new file mode 100644 index 0000000..74ea2f2 --- /dev/null +++ b/src/ProductVote/Service/VoteServiceInterface.php @@ -0,0 +1,24 @@ +prepareVoteData(); + $this->oemtPrepareVoteData(); return parent::render(); } - public function prepareVoteData(): void + public function oemtPrepareVoteData(): void { /** @var Article $product */ $product = $this->getProduct(); /** @var User $user */ $user = $this->getUser(); + $voteService = $this->getService(VoteServiceInterface::class); + if ($user instanceof User) { - $productVoteDao = $this->getProductVoteDao(); - $this->_aViewData['productVote'] = $productVoteDao->getProductVote($product->getId(), $user->getId()); + $this->_aViewData['productVote'] = $voteService->getProductVote($product, $user); } - $resultDao = $this->getProductVoteResultDao(); - $this->_aViewData['productVoteResult'] = $resultDao->getProductVoteResult($product->getId()); - } - - private function getProductVoteDao(): ProductVoteDaoInterface - { - return $this->getService(ProductVoteDaoInterface::class); - } - - private function getProductVoteResultDao(): ResultDaoInterface - { - return $this->getService(ResultDaoInterface::class); + $this->_aViewData['productVoteResult'] = $voteService->getProductVoteResult($product); } } diff --git a/src/ProductVote/services.yaml b/src/ProductVote/services.yaml index e46c3cd..631b6ed 100644 --- a/src/ProductVote/services.yaml +++ b/src/ProductVote/services.yaml @@ -5,14 +5,16 @@ services: OxidEsales\ModuleTemplate\ProductVote\Dao\ProductVoteDaoInterface: class: OxidEsales\ModuleTemplate\ProductVote\Dao\ProductVoteDao - public: true - OxidEsales\ModuleTemplate\ProductVote\Dao\ResultDaoInterface: - class: OxidEsales\ModuleTemplate\ProductVote\Dao\ResultDao - public: true + OxidEsales\ModuleTemplate\ProductVote\Dao\VoteResultDaoInterface: + class: OxidEsales\ModuleTemplate\ProductVote\Dao\VoteResultDao OxidEsales\ModuleTemplate\ProductVote\DataMapper\ProductVoteDataMapperInterface: class: OxidEsales\ModuleTemplate\ProductVote\DataMapper\ProductVoteDataMapper - OxidEsales\ModuleTemplate\ProductVote\DataMapper\ResultDataMapperInterface: - class: OxidEsales\ModuleTemplate\ProductVote\DataMapper\ResultDataMapper + OxidEsales\ModuleTemplate\ProductVote\DataMapper\VoteResultDataMapperInterface: + class: OxidEsales\ModuleTemplate\ProductVote\DataMapper\VoteResultDataMapper + + OxidEsales\ModuleTemplate\ProductVote\Service\VoteServiceInterface: + class: OxidEsales\ModuleTemplate\ProductVote\Service\VoteService + public: true diff --git a/tests/Integration/ProductVote/Controller/ArticleDetailsControllerTest.php b/tests/Integration/ProductVote/Controller/ArticleDetailsControllerTest.php index 3548a1e..8651c53 100644 --- a/tests/Integration/ProductVote/Controller/ArticleDetailsControllerTest.php +++ b/tests/Integration/ProductVote/Controller/ArticleDetailsControllerTest.php @@ -12,11 +12,9 @@ use OxidEsales\Eshop\Application\Model\Article; use OxidEsales\Eshop\Application\Model\User; use OxidEsales\ModuleTemplate\ProductVote\Controller\ArticleDetailsController; -use OxidEsales\ModuleTemplate\ProductVote\Dao\ProductVoteDaoInterface; -use OxidEsales\ModuleTemplate\ProductVote\DataType\ProductVote; +use OxidEsales\ModuleTemplate\ProductVote\Service\VoteServiceInterface; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Test; -use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\MockObject\Stub; use PHPUnit\Framework\TestCase; @@ -29,15 +27,28 @@ final class ArticleDetailsControllerTest extends TestCase #[Test] public function voteNotLoggedIn(): void { - $daoSpy = $this->createMock(ProductVoteDaoInterface::class); - $daoSpy + $voteServiceSpy = $this->createMock(VoteServiceInterface::class); + $voteServiceSpy ->expects($this->never()) ->method('setProductVote'); - $daoSpy + $voteServiceSpy ->expects($this->never()) ->method('resetProductVote'); - $sut = $this->getSutMock($daoSpy, null, $this->getProductStub()); + $sut = $this + ->getMockBuilder(ArticleDetailsController::class) + ->onlyMethods(['getService', 'getProduct', 'getUser']) + ->getMock(); + $sut + ->method('getService') + ->with(VoteServiceInterface::class) + ->willReturn($voteServiceSpy); + $sut + ->method('getUser') + ->willReturn(null); + $sut + ->method('getProduct') + ->willReturn($this->getProductStub()); $sut->voteUp(); $sut->voteDown(); @@ -47,11 +58,13 @@ public function voteNotLoggedIn(): void #[Test] public function voteUp(): void { - $daoSpy = $this->getDaoSpy( + $voteServiceSpy = $this->getVoteServiceSpy( 'setProductVote', - new ProductVote(self::TEST_PRODUCT_ID, self::TEST_USER_ID, true) + $this->getProductStub(), + $this->getUserStub(), + true, ); - $sut = $this->getSutMock($daoSpy, $this->getUserStub(), $this->getProductStub()); + $sut = $this->getSutMock($voteServiceSpy); $sut->voteUp(); } @@ -59,11 +72,13 @@ public function voteUp(): void #[Test] public function voteDown(): void { - $daoSpy = $this->getDaoSpy( + $voteServiceSpy = $this->getVoteServiceSpy( 'setProductVote', - new ProductVote(self::TEST_PRODUCT_ID, self::TEST_USER_ID, false) + $this->getProductStub(), + $this->getUserStub(), + false, ); - $sut = $this->getSutMock($daoSpy, $this->getUserStub(), $this->getProductStub()); + $sut = $this->getSutMock($voteServiceSpy); $sut->voteDown(); } @@ -71,15 +86,19 @@ public function voteDown(): void #[Test] public function resetVote(): void { - $daoSpy = $this->getDaoSpy('resetProductVote', self::TEST_PRODUCT_ID, self::TEST_USER_ID); - $sut = $this->getSutMock($daoSpy, $this->getUserStub(), $this->getProductStub()); + $voteServiceSpy = $this->getVoteServiceSpy( + 'resetProductVote', + $this->getProductStub(), + $this->getUserStub(), + ); + $sut = $this->getSutMock($voteServiceSpy); $sut->resetVote(); } - private function getDaoSpy(string $method, mixed ...$arguments): ProductVoteDaoInterface|MockObject + private function getVoteServiceSpy(string $method, mixed ...$arguments): VoteServiceInterface { - $daoSpy = $this->createMock(ProductVoteDaoInterface::class); + $daoSpy = $this->createMock(VoteServiceInterface::class); $daoSpy ->expects($this->once()) ->method($method) @@ -109,23 +128,22 @@ private function getUserStub(): User|Stub } private function getSutMock( - ProductVoteDaoInterface|MockObject $daoSpy, - Stub|User|null $userStub, - Article|Stub $productStub, - ): ArticleDetailsController|MockObject { + VoteServiceInterface $voteServiceSpy, + ): ArticleDetailsController { $sut = $this ->getMockBuilder(ArticleDetailsController::class) ->onlyMethods(['getService', 'getProduct', 'getUser']) ->getMock(); $sut ->method('getService') - ->with(ProductVoteDaoInterface::class)->willReturn($daoSpy); + ->with(VoteServiceInterface::class) + ->willReturn($voteServiceSpy); $sut ->method('getUser') - ->willReturn($userStub); + ->willReturn($this->getUserStub()); $sut ->method('getProduct') - ->willReturn($productStub); + ->willReturn($this->getProductStub()); return $sut; } diff --git a/tests/Integration/ProductVote/Dao/ResultDaoTest.php b/tests/Integration/ProductVote/Dao/ResultDaoTest.php index b40f065..7465346 100644 --- a/tests/Integration/ProductVote/Dao/ResultDaoTest.php +++ b/tests/Integration/ProductVote/Dao/ResultDaoTest.php @@ -10,13 +10,13 @@ namespace OxidEsales\ModuleTemplate\Tests\Integration\ProductVote\Dao; use OxidEsales\EshopCommunity\Tests\Integration\IntegrationTestCase; -use OxidEsales\ModuleTemplate\ProductVote\Dao\ResultDao; -use OxidEsales\ModuleTemplate\ProductVote\Dao\ResultDaoInterface; -use OxidEsales\ModuleTemplate\ProductVote\DataType\Result; +use OxidEsales\ModuleTemplate\ProductVote\Dao\VoteResultDao; +use OxidEsales\ModuleTemplate\ProductVote\Dao\VoteResultDaoInterface; +use OxidEsales\ModuleTemplate\ProductVote\DataType\VoteResult; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Test; -#[CoversClass(ResultDao::class)] +#[CoversClass(VoteResultDao::class)] final class ResultDaoTest extends IntegrationTestCase { use DaoTestTrait; @@ -24,10 +24,10 @@ final class ResultDaoTest extends IntegrationTestCase #[Test] public function calculateNoVotes(): void { - $sut = $this->get(ResultDaoInterface::class); + $sut = $this->get(VoteResultDaoInterface::class); $result = $sut->getProductVoteResult(self::TEST_PRODUCT_ID); - $this->assertEquals(new Result(self::TEST_PRODUCT_ID, 0, 0), $result); + $this->assertEquals(new VoteResult(self::TEST_PRODUCT_ID, 0, 0), $result); } #[Test] @@ -35,9 +35,9 @@ public function calculateVoteResult(): void { $this->executeInsertVoteQuery(true, 'user_1'); - $sut = $this->get(ResultDaoInterface::class); + $sut = $this->get(VoteResultDaoInterface::class); $result = $sut->getProductVoteResult(self::TEST_PRODUCT_ID); - $this->assertEquals(new Result(self::TEST_PRODUCT_ID, 1, 0), $result); + $this->assertEquals(new VoteResult(self::TEST_PRODUCT_ID, 1, 0), $result); } #[Test] @@ -51,8 +51,8 @@ public function calculateVotesResult(): void $this->executeInsertVoteQuery(true, 'user_6'); // 3/3 $this->executeInsertVoteQuery(true, 'user_7'); // 4/3 - $sut = $this->get(ResultDaoInterface::class); + $sut = $this->get(VoteResultDaoInterface::class); $result = $sut->getProductVoteResult(self::TEST_PRODUCT_ID); - $this->assertEquals(new Result(self::TEST_PRODUCT_ID, 4, 3), $result); + $this->assertEquals(new VoteResult(self::TEST_PRODUCT_ID, 4, 3), $result); } } diff --git a/tests/Integration/ProductVote/Widget/ArticleDetailsTest.php b/tests/Integration/ProductVote/Widget/ArticleDetailsTest.php index 3c4c239..4872d08 100644 --- a/tests/Integration/ProductVote/Widget/ArticleDetailsTest.php +++ b/tests/Integration/ProductVote/Widget/ArticleDetailsTest.php @@ -11,10 +11,9 @@ use OxidEsales\Eshop\Application\Model\Article; use OxidEsales\Eshop\Application\Model\User; -use OxidEsales\ModuleTemplate\ProductVote\Dao\ProductVoteDaoInterface; -use OxidEsales\ModuleTemplate\ProductVote\Dao\ResultDaoInterface; use OxidEsales\ModuleTemplate\ProductVote\DataType\ProductVote; -use OxidEsales\ModuleTemplate\ProductVote\DataType\Result; +use OxidEsales\ModuleTemplate\ProductVote\DataType\VoteResult; +use OxidEsales\ModuleTemplate\ProductVote\Service\VoteServiceInterface; use OxidEsales\ModuleTemplate\ProductVote\Widget\ArticleDetails; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Test; @@ -37,12 +36,14 @@ public function prepareDataNotLoggedIn(): void ->willReturn(null); $sut ->method('getService') - ->with(ResultDaoInterface::class)->willReturn($this->getResultDaoStub()); + ->with(VoteServiceInterface::class) + ->willReturn($this->getVoteServiceStub()); - $sut->prepareVoteData(); + $sut->oemtPrepareVoteData(); $viewData = $sut->getViewData(); - $this->assertEquals($this->getResultDataType(), $viewData['productVoteResult']); + $this->assertEquals($this->getVoteResultDataType(), $viewData['productVoteResult']); + $this->assertArrayNotHasKey('productVote', $viewData); } #[Test] @@ -54,15 +55,13 @@ public function prepareDataLoggedIn(): void ->willReturn($this->getUserStub()); $sut ->method('getService') - ->willReturnMap([ - [ProductVoteDaoInterface::class, $this->getProductVoteDaoStub()], - [ResultDaoInterface::class, $this->getResultDaoStub()], - ]); + ->with(VoteServiceInterface::class) + ->willReturn($this->getVoteServiceStub()); - $sut->prepareVoteData(); + $sut->oemtPrepareVoteData(); $viewData = $sut->getViewData(); - $this->assertEquals($this->getResultDataType(), $viewData['productVoteResult']); + $this->assertEquals($this->getVoteResultDataType(), $viewData['productVoteResult']); $this->assertEquals($this->getProductVoteDataType(), $viewData['productVote']); } @@ -99,12 +98,15 @@ private function getUserStub(): User|Stub return $userStub; } - private function getProductVoteDaoStub(): ProductVoteDaoInterface|Stub + private function getVoteServiceStub(): VoteServiceInterface { - $stub = $this->createStub(ProductVoteDaoInterface::class); + $stub = $this->createStub(VoteServiceInterface::class); $stub ->method('getProductVote') ->willReturn($this->getProductVoteDataType()); + $stub + ->method('getProductVoteResult') + ->willReturn($this->getVoteResultDataType()); return $stub; } @@ -114,18 +116,8 @@ private function getProductVoteDataType(): ProductVote return new ProductVote(self::TEST_PRODUCT_ID, self::TEST_USER_ID, true); } - private function getResultDaoStub(): ResultDaoInterface|Stub - { - $stub = $this->createStub(ResultDaoInterface::class); - $stub - ->method('getProductVoteResult') - ->willReturn($this->getResultDataType()); - - return $stub; - } - - private function getResultDataType(): Result + private function getVoteResultDataType(): VoteResult { - return new Result(self::TEST_PRODUCT_ID, 3, 2); + return new VoteResult(self::TEST_PRODUCT_ID, 3, 2); } } diff --git a/tests/Unit/ProductVote/DataMapper/ResultDataMapperTest.php b/tests/Unit/ProductVote/DataMapper/ResultDataMapperTest.php index 3324f51..f454187 100644 --- a/tests/Unit/ProductVote/DataMapper/ResultDataMapperTest.php +++ b/tests/Unit/ProductVote/DataMapper/ResultDataMapperTest.php @@ -10,22 +10,22 @@ namespace OxidEsales\ModuleTemplate\Tests\Unit\ProductVote\DataMapper; use Generator; -use OxidEsales\ModuleTemplate\ProductVote\DataMapper\ResultDataMapper; -use OxidEsales\ModuleTemplate\ProductVote\DataType\Result; +use OxidEsales\ModuleTemplate\ProductVote\DataMapper\VoteResultDataMapper; +use OxidEsales\ModuleTemplate\ProductVote\DataType\VoteResult; use OxidEsales\ModuleTemplate\ProductVote\Exception\MapDataTypeException; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; -#[CoversClass(ResultDataMapper::class)] +#[CoversClass(VoteResultDataMapper::class)] final class ResultDataMapperTest extends TestCase { #[Test] #[DataProvider('mapMalformedDataProvider')] public function mapMalformedData(array $data): void { - $sut = new ResultDataMapper(); + $sut = new VoteResultDataMapper(); $this->expectException(MapDataTypeException::class); $sut->map($data); @@ -45,16 +45,16 @@ public static function mapMalformedDataProvider(): Generator #[Test] #[DataProvider('mapDataProvider')] - public function mapData(Result $expectedResult, array $data): void + public function mapData(VoteResult $expectedResult, array $data): void { - $sut = new ResultDataMapper(); + $sut = new VoteResultDataMapper(); $this->assertEquals($expectedResult, $sut->map($data)); } public static function mapDataProvider(): Generator { yield [ - 'expectedResult' => new Result( + 'expectedResult' => new VoteResult( 'test_product_id', 2, 3, @@ -67,7 +67,7 @@ public static function mapDataProvider(): Generator ]; yield [ - 'expectedResult' => new Result( + 'expectedResult' => new VoteResult( 'another_product_id', 100, 0, diff --git a/tests/Unit/ProductVote/Service/VoteServiceTest.php b/tests/Unit/ProductVote/Service/VoteServiceTest.php new file mode 100644 index 0000000..0e299fe --- /dev/null +++ b/tests/Unit/ProductVote/Service/VoteServiceTest.php @@ -0,0 +1,121 @@ +createMock(ProductVoteDaoInterface::class); + $productVoteDaoSpy + ->expects($this->once()) + ->method('getProductVote') + ->with(self::TEST_PRODUCT_ID, self::TEST_USER_ID) + ->willReturn($this->getProductVoteDataType()); + + $sut = $this->getSut(productVoteDao: $productVoteDaoSpy); + $productVote = $sut->getProductVote($this->getProductStub(), $this->getUserStub()); + + $this->assertEquals($productVote, $this->getProductVoteDataType()); + } + + public function testSetProductVote(): void + { + $productVoteDaoSpy = $this->createMock(ProductVoteDaoInterface::class); + $productVoteDaoSpy + ->expects($this->once()) + ->method('setProductVote') + ->with($this->getProductVoteDataType()); + + $sut = $this->getSut(productVoteDao: $productVoteDaoSpy); + $sut->setProductVote($this->getProductStub(), $this->getUserStub(), true); + } + + public function testResetProductVote(): void + { + $productVoteDaoSpy = $this->createMock(ProductVoteDaoInterface::class); + $productVoteDaoSpy + ->expects($this->once()) + ->method('resetProductVote') + ->with(self::TEST_PRODUCT_ID, self::TEST_USER_ID); + + $sut = $this->getSut(productVoteDao: $productVoteDaoSpy); + $sut->resetProductVote($this->getProductStub(), $this->getUserStub()); + } + + public function testGetProductVoteResult(): void + { + $voteResultDaoSpy = $this->createMock(VoteResultDaoInterface::class); + $voteResultDaoSpy + ->expects($this->once()) + ->method('getProductVoteResult') + ->with(self::TEST_PRODUCT_ID) + ->willReturn($this->getVoteResultDataType()); + + $sut = $this->getSut(voteResultDao: $voteResultDaoSpy); + $voteResult = $sut->getProductVoteResult($this->getProductStub()); + + $this->assertEquals($voteResult, $this->getVoteResultDataType()); + } + + private function getSut( + ?ProductVoteDaoInterface $productVoteDao = null, + ?VoteResultDaoInterface $voteResultDao = null, + ): VoteService { + return new VoteService( + productVoteDao: $productVoteDao ?? $this->createStub(ProductVoteDaoInterface::class), + voteResultDao: $voteResultDao ?? $this->createStub(VoteResultDaoInterface::class), + ); + } + + private function getProductStub(): Article + { + $productStub = $this->createStub(Article::class); + $productStub + ->method('getId') + ->willReturn(self::TEST_PRODUCT_ID); + + return $productStub; + } + + private function getUserStub(): User + { + $userStub = $this->createStub(User::class); + $userStub + ->method('getId') + ->willReturn(self::TEST_USER_ID); + + return $userStub; + } + + private function getProductVoteDataType(): ProductVote + { + return new ProductVote(self::TEST_PRODUCT_ID, self::TEST_USER_ID, true); + } + + private function getVoteResultDataType(): VoteResult + { + return new VoteResult(self::TEST_PRODUCT_ID, 3, 2); + } +} diff --git a/views/twig/extensions/themes/default/page/details/inc/productmain.html.twig b/views/twig/extensions/themes/default/page/details/inc/productmain.html.twig index 2e090a8..eec031f 100644 --- a/views/twig/extensions/themes/default/page/details/inc/productmain.html.twig +++ b/views/twig/extensions/themes/default/page/details/inc/productmain.html.twig @@ -4,14 +4,15 @@ {{ parent() }} {% if oViewConf.getUser() %} {% set baseLink = oViewConf.getSelfLink() ~ 'cl=' ~ oViewConf.getTopActiveClassName() ~ '&anid=' ~ oDetailsProduct.oxarticles__oxnid.value %} - - {{ productVote.vote ? '👍' : '🖒' }} ({{ productVoteResult.voteUp }}) + {% set hue = ';filter:hue-rotate(300deg)' %} + + 👍 ({{ productVoteResult.voteUp }}) - - {{ productVote and (not productVote.vote) ? '👎' : '🖓' }} ({{ productVoteResult.voteDown }}) + + 👎 ({{ productVoteResult.voteDown }}) {% else %} - 🖒 ({{ productVoteResult.voteUp }}) - 🖓 ({{ productVoteResult.voteDown }}) + 👍 ({{ productVoteResult.voteUp }}) + 👎 ({{ productVoteResult.voteDown }}) {% endif %} {% endblock %}