From 1b1a3b28cd79cd67e30b793f9d3290a45937487a Mon Sep 17 00:00:00 2001 From: kiy0taka Date: Wed, 31 Mar 2021 17:28:58 +0900 Subject: [PATCH 1/3] =?UTF-8?q?=E5=95=86=E5=93=81=E6=83=85=E5=A0=B1?= =?UTF-8?q?=E5=8F=AF=E8=A6=96=E6=80=A7=E3=81=AE=E6=8B=A1=E5=BC=B5=E6=A9=9F?= =?UTF-8?q?=E6=A7=8B=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/config/eccube/services.yaml | 4 ++ src/Eccube/Controller/ProductController.php | 33 +++++----- .../Compiler/ProductVisibilityPass.php | 32 ++++++++++ src/Eccube/Kernel.php | 6 ++ src/Eccube/Repository/ProductRepository.php | 3 +- .../Service/Product/ProductVisibility.php | 33 ++++++++++ .../Service/Product/StatusVisibility.php | 62 +++++++++++++++++++ 7 files changed, 153 insertions(+), 20 deletions(-) create mode 100644 src/Eccube/DependencyInjection/Compiler/ProductVisibilityPass.php create mode 100644 src/Eccube/Service/Product/ProductVisibility.php create mode 100644 src/Eccube/Service/Product/StatusVisibility.php diff --git a/app/config/eccube/services.yaml b/app/config/eccube/services.yaml index 2b1952d7410..f9ca59ad46e 100644 --- a/app/config/eccube/services.yaml +++ b/app/config/eccube/services.yaml @@ -28,6 +28,7 @@ services: $shoppingPurchaseFlow: '@eccube.purchase.flow.shopping' $orderPurchaseFlow: '@eccube.purchase.flow.order' $_orderStateMachine: '@state_machine.order' + $productVisibilities: '@eccube.product.visibilities' # makes classes in src/ available to be used as services @@ -180,3 +181,6 @@ services: Eccube\Session\Storage\Handler\SameSiteNoneCompatSessionHandler: arguments: - '@native_file_session_handler' + + eccube.product.visibilities: + class: ArrayObject diff --git a/src/Eccube/Controller/ProductController.php b/src/Eccube/Controller/ProductController.php index b482d167366..ce83ec660c3 100644 --- a/src/Eccube/Controller/ProductController.php +++ b/src/Eccube/Controller/ProductController.php @@ -13,8 +13,8 @@ namespace Eccube\Controller; +use ArrayObject; use Eccube\Entity\BaseInfo; -use Eccube\Entity\Master\ProductStatus; use Eccube\Entity\Product; use Eccube\Event\EccubeEvents; use Eccube\Event\EventArgs; @@ -27,6 +27,7 @@ use Eccube\Repository\Master\ProductListMaxRepository; use Eccube\Repository\ProductRepository; use Eccube\Service\CartService; +use Eccube\Service\Product\ProductVisibility; use Eccube\Service\PurchaseFlow\PurchaseContext; use Eccube\Service\PurchaseFlow\PurchaseFlow; use Knp\Bundle\PaginatorBundle\Pagination\SlidingPagination; @@ -76,6 +77,11 @@ class ProductController extends AbstractController */ protected $productListMaxRepository; + /** + * @var ArrayObject + */ + protected $productVisibilities; + private $title = ''; /** @@ -88,6 +94,7 @@ class ProductController extends AbstractController * @param BaseInfoRepository $baseInfoRepository * @param AuthenticationUtils $helper * @param ProductListMaxRepository $productListMaxRepository + * @param ArrayObject $productVisibilities */ public function __construct( PurchaseFlow $cartPurchaseFlow, @@ -96,7 +103,8 @@ public function __construct( ProductRepository $productRepository, BaseInfoRepository $baseInfoRepository, AuthenticationUtils $helper, - ProductListMaxRepository $productListMaxRepository + ProductListMaxRepository $productListMaxRepository, + ArrayObject $productVisibilities ) { $this->purchaseFlow = $cartPurchaseFlow; $this->customerFavoriteProductRepository = $customerFavoriteProductRepository; @@ -105,6 +113,7 @@ public function __construct( $this->BaseInfo = $baseInfoRepository->get(); $this->helper = $helper; $this->productListMaxRepository = $productListMaxRepository; + $this->productVisibilities = $productVisibilities; } /** @@ -509,22 +518,10 @@ protected function getPageTitle($searchData) */ protected function checkVisibility(Product $Product) { - $is_admin = $this->session->has('_security_admin'); - - // 管理ユーザの場合はステータスやオプションにかかわらず閲覧可能. - if (!$is_admin) { - // 在庫なし商品の非表示オプションが有効な場合. - // if ($this->BaseInfo->isOptionNostockHidden()) { - // if (!$Product->getStockFind()) { - // return false; - // } - // } - // 公開ステータスでない商品は表示しない. - if ($Product->getStatus()->getId() !== ProductStatus::DISPLAY_SHOW) { - return false; - } - } + $invisible = array_filter($this->productVisibilities->getArrayCopy(), function (ProductVisibility $productVisibility) use ($Product) { + return $productVisibility->checkVisibility($Product) === false; + }); - return true; + return $invisible == null; } } diff --git a/src/Eccube/DependencyInjection/Compiler/ProductVisibilityPass.php b/src/Eccube/DependencyInjection/Compiler/ProductVisibilityPass.php new file mode 100644 index 00000000000..0ef1e3d532f --- /dev/null +++ b/src/Eccube/DependencyInjection/Compiler/ProductVisibilityPass.php @@ -0,0 +1,32 @@ +getDefinition('eccube.product.visibilities'); + $ids = $container->findTaggedServiceIds(self::TAG); + foreach ($ids as $id => $tag) { + $visibilitiesDef->addMethodCall('append', [new Reference($id)]); + } + } +} diff --git a/src/Eccube/Kernel.php b/src/Eccube/Kernel.php index 195314f1ab0..0bacea062e4 100644 --- a/src/Eccube/Kernel.php +++ b/src/Eccube/Kernel.php @@ -20,6 +20,7 @@ use Eccube\DependencyInjection\Compiler\NavCompilerPass; use Eccube\DependencyInjection\Compiler\PaymentMethodPass; use Eccube\DependencyInjection\Compiler\PluginPass; +use Eccube\DependencyInjection\Compiler\ProductVisibilityPass; use Eccube\DependencyInjection\Compiler\PurchaseFlowPass; use Eccube\DependencyInjection\Compiler\QueryCustomizerPass; use Eccube\DependencyInjection\Compiler\TwigBlockPass; @@ -31,6 +32,7 @@ use Eccube\Doctrine\ORM\Mapping\Driver\AnnotationDriver; use Eccube\Doctrine\Query\QueryCustomizer; use Eccube\Service\Payment\PaymentMethodInterface; +use Eccube\Service\Product\ProductVisibility; use Eccube\Service\PurchaseFlow\DiscountProcessor; use Eccube\Service\PurchaseFlow\ItemHolderPostValidator; use Eccube\Service\PurchaseFlow\ItemHolderPreprocessor; @@ -256,6 +258,10 @@ protected function build(ContainerBuilder $container) $container->registerForAutoconfiguration(PurchaseProcessor::class) ->addTag(PurchaseFlowPass::PURCHASE_PROCESSOR_TAG); $container->addCompilerPass(new PurchaseFlowPass()); + + $container->registerForAutoconfiguration(ProductVisibility::class) + ->addTag(ProductVisibilityPass::TAG); + $container->addCompilerPass(new ProductVisibilityPass()); } protected function addEntityExtensionPass(ContainerBuilder $container) diff --git a/src/Eccube/Repository/ProductRepository.php b/src/Eccube/Repository/ProductRepository.php index 62667001d53..7580f61f3e0 100644 --- a/src/Eccube/Repository/ProductRepository.php +++ b/src/Eccube/Repository/ProductRepository.php @@ -132,8 +132,7 @@ public function findProductsWithSortedClassCategories(array $ids, $indexBy = nul */ public function getQueryBuilderBySearchData($searchData) { - $qb = $this->createQueryBuilder('p') - ->andWhere('p.Status = 1'); + $qb = $this->createQueryBuilder('p'); // category $categoryJoin = false; diff --git a/src/Eccube/Service/Product/ProductVisibility.php b/src/Eccube/Service/Product/ProductVisibility.php new file mode 100644 index 00000000000..7c29d8894c1 --- /dev/null +++ b/src/Eccube/Service/Product/ProductVisibility.php @@ -0,0 +1,33 @@ +session = $session; + } + + public function checkVisibility(Product $Product) + { + $is_admin = $this->session->has('_security_admin'); + + // 管理ユーザの場合はステータスやオプションにかかわらず閲覧可能. + if (!$is_admin) { + // 在庫なし商品の非表示オプションが有効な場合. + // if ($this->BaseInfo->isOptionNostockHidden()) { + // if (!$Product->getStockFind()) { + // return false; + // } + // } + // 公開ステータスでない商品は表示しない. + if ($Product->getStatus()->getId() !== ProductStatus::DISPLAY_SHOW) { + return false; + } + } + + return true; + + } + + protected function createStatements($params, $queryKey) + { + return [ + WhereClause::eq('p.Status', ':ProductStatus', ProductStatus::DISPLAY_SHOW) + ]; + } + +} From 298ffc91de9e05fb817174114d01fad99a93ef03 Mon Sep 17 00:00:00 2001 From: kiy0taka Date: Thu, 1 Apr 2021 13:26:07 +0900 Subject: [PATCH 2/3] =?UTF-8?q?WhereClause=E3=81=ABand/or=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Eccube/Doctrine/Query/WhereClause.php | 46 +++++++++++++++++++ .../Tests/Doctrine/Query/WhereClauseTest.php | 26 +++++++++++ 2 files changed, 72 insertions(+) diff --git a/src/Eccube/Doctrine/Query/WhereClause.php b/src/Eccube/Doctrine/Query/WhereClause.php index af0e39cfffb..cafe5df6c63 100644 --- a/src/Eccube/Doctrine/Query/WhereClause.php +++ b/src/Eccube/Doctrine/Query/WhereClause.php @@ -286,6 +286,52 @@ public static function lte($x, $y, $param) return self::newWhereClause(self::expr()->lte($x, $y), $y, $param); } + /** + * AND演算子のファクトリメソッド。 + * Example: + * WhereClause::and( + * WhereClause::eq('status', ':status', '1'), + * WhereClause::eq('price', ':price', 1000), + * ) + * @param WhereClause ...$list + * @return WhereClause + */ + public static function and(WhereClause ...$list) + { + $expr = array_map(function(WhereClause $wc) { + return $wc->expr; + }, $list); + + $params = array_reduce($list, function($result, WhereClause $wc) { + return $wc->params ? array_merge($result, $wc->params) : $result; + }, []); + + return new WhereClause(self::expr()->andX(...$expr), $params); + } + + /** + * OR演算子のファクトリメソッド。 + * Example: + * WhereClause::or( + * WhereClause::eq('status', ':status1', '1'), + * WhereClause::eq('status', ':status2', '2'), + * ) + * @param WhereClause ...$list + * @return WhereClause + */ + public static function or(WhereClause ...$list) + { + $expr = array_map(function(WhereClause $wc) { + return $wc->expr; + }, $list); + + $params = array_reduce($list, function($result, WhereClause $wc) { + return $wc->params ? array_merge($result, $wc->params) : $result; + }, []); + + return new WhereClause(self::expr()->orX(...$expr), $params); + } + /** * @return Expr */ diff --git a/tests/Eccube/Tests/Doctrine/Query/WhereClauseTest.php b/tests/Eccube/Tests/Doctrine/Query/WhereClauseTest.php index eec61e0c956..8978a6a4cee 100644 --- a/tests/Eccube/Tests/Doctrine/Query/WhereClauseTest.php +++ b/tests/Eccube/Tests/Doctrine/Query/WhereClauseTest.php @@ -181,6 +181,32 @@ public function testParameterFalseEquivalent() self::assertEquals([new Parameter('Name', '0')], $this->getParams($actual)); } + public function testAnd() + { + $actual = WhereClause::and( + WhereClause::eq('name', ':Name', '0'), + WhereClause::eq('price', ':Price', 1000) + ); + self::assertEquals('name = :Name AND price = :Price', $this->asString($actual)); + self::assertEquals( + [new Parameter('Name', '0'), new Parameter('Price', 1000)], + $this->getParams($actual) + ); + } + + public function testOr() + { + $actual = WhereClause::or( + WhereClause::eq('name', ':Name', '0'), + WhereClause::eq('price', ':Price', 1000) + ); + self::assertEquals('name = :Name OR price = :Price', $this->asString($actual)); + self::assertEquals( + [new Parameter('Name', '0'), new Parameter('Price', 1000)], + $this->getParams($actual) + ); + } + /* * Helper methods. */ From 5f956d8506a496c15e6e80e5538a00bc2ef73645 Mon Sep 17 00:00:00 2001 From: kiy0taka Date: Thu, 1 Apr 2021 13:26:47 +0900 Subject: [PATCH 3/3] =?UTF-8?q?=E5=85=AC=E9=96=8B=E6=9C=9F=E9=96=93?= =?UTF-8?q?=E3=81=AB=E3=82=88=E8=A1=A8=E7=A4=BA=E5=88=B6=E5=BE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Eccube/Entity/Product.php | 60 +++++++++++++++++-- .../Service/Product/PublishTermVisibility.php | 53 ++++++++++++++++ 2 files changed, 107 insertions(+), 6 deletions(-) create mode 100644 src/Eccube/Service/Product/PublishTermVisibility.php diff --git a/src/Eccube/Entity/Product.php b/src/Eccube/Entity/Product.php index c39e0e0095b..76c6f52c42f 100644 --- a/src/Eccube/Entity/Product.php +++ b/src/Eccube/Entity/Product.php @@ -13,6 +13,7 @@ namespace Eccube\Entity; +use DateTime; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\ORM\Mapping as ORM; @@ -495,14 +496,14 @@ public function hasProductClass() private $free_area; /** - * @var \DateTime + * @var DateTime * * @ORM\Column(name="create_date", type="datetimetz") */ private $create_date; /** - * @var \DateTime + * @var DateTime * * @ORM\Column(name="update_date", type="datetimetz") */ @@ -566,6 +567,21 @@ public function hasProductClass() */ private $Status; + + /** + * @var DateTime + * + * @ORM\Column(name="publish_start", type="datetimetz", nullable=true) + */ + private $publishStart; + + /** + * @var DateTime + * + * @ORM\Column(name="publish_end", type="datetimetz", nullable=true) + */ + private $publishEnd; + /** * Constructor */ @@ -780,7 +796,7 @@ public function getFreeArea() /** * Set createDate. * - * @param \DateTime $createDate + * @param DateTime $createDate * * @return Product */ @@ -794,7 +810,7 @@ public function setCreateDate($createDate) /** * Get createDate. * - * @return \DateTime + * @return DateTime */ public function getCreateDate() { @@ -804,7 +820,7 @@ public function getCreateDate() /** * Set updateDate. * - * @param \DateTime $updateDate + * @param DateTime $updateDate * * @return Product */ @@ -818,7 +834,7 @@ public function setUpdateDate($updateDate) /** * Get updateDate. * - * @return \DateTime + * @return DateTime */ public function getUpdateDate() { @@ -1073,5 +1089,37 @@ public function getStatus() { return $this->Status; } + + /** + * @return DateTime + */ + public function getPublishStart() + { + return $this->publishStart; + } + + /** + * @param DateTime $publishStart + */ + public function setPublishStart(DateTime $publishStart) + { + $this->publishStart = $publishStart; + } + + /** + * @return DateTime + */ + public function getPublishEnd() + { + return $this->publishEnd; + } + + /** + * @param DateTime $publishEnd + */ + public function setPublishEnd(DateTime $publishEnd) + { + $this->publishEnd = $publishEnd; + } } } diff --git a/src/Eccube/Service/Product/PublishTermVisibility.php b/src/Eccube/Service/Product/PublishTermVisibility.php new file mode 100644 index 00000000000..b962e65815f --- /dev/null +++ b/src/Eccube/Service/Product/PublishTermVisibility.php @@ -0,0 +1,53 @@ +getPublishStart() && $Product->getPublishEnd()) { + return $Product->getPublishStart() <= $now && $Product->getPublishEnd() > $now; + } elseif ($Product->getPublishStart()) { + return $Product->getPublishStart() <= $now; + } elseif ($Product->getPublishEnd()) { + return $Product->getPublishEnd() > $now; + } + + return true; + } + + protected function createStatements($params, $queryKey) + { + $now = new DateTime(); + + $start = WhereClause::lt('p.publishStart', ':publishStart', $now); + $end = WhereClause::gte('p.publishEnd', ':publishEnd', $now); + $startIsNull = WhereClause::isNull('p.publishEnd'); + $endIsNull = WhereClause::isNull('p.publishStart'); + + return [WhereClause::or( + WhereClause::and($start, $end), + WhereClause::and($start, $startIsNull), + WhereClause::and($endIsNull, $end), + WhereClause::and($startIsNull, $endIsNull) + )]; + } +}