Skip to content

Commit

Permalink
Merge pull request PrestaShop#21345 from zuk3975/m/product/seo-rep
Browse files Browse the repository at this point in the history
Refactor UpdateProductSeoHandler to use ProductRepository
  • Loading branch information
matks authored Nov 24, 2020
2 parents 348dff9 + 5773867 commit 3ea3a56
Show file tree
Hide file tree
Showing 12 changed files with 618 additions and 140 deletions.
60 changes: 60 additions & 0 deletions src/Adapter/Category/Repository/CategoryRepository.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to [email protected] so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <[email protected]>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
*/

declare(strict_types=1);

namespace PrestaShop\PrestaShop\Adapter\Category\Repository;

use PrestaShop\PrestaShop\Adapter\AbstractObjectModelRepository;
use PrestaShop\PrestaShop\Core\Domain\Category\Exception\CategoryException;
use PrestaShop\PrestaShop\Core\Domain\Category\Exception\CategoryNotFoundException;
use PrestaShop\PrestaShop\Core\Domain\Category\ValueObject\CategoryId;
use PrestaShop\PrestaShop\Core\Exception\CoreException;

/**
* Provides access to Category data source
*/
class CategoryRepository extends AbstractObjectModelRepository
{
/**
* @param CategoryId $categoryId
*
* @throws CategoryNotFoundException
* @throws CoreException
*/
public function assertCategoryExists(CategoryId $categoryId): void
{
try {
$this->assertObjectModelExists(
$categoryId->getValue(),
'category',
CategoryException::class
);
} catch (CategoryException $e) {
throw new CategoryNotFoundException($categoryId, $e);
}
}
}
127 changes: 48 additions & 79 deletions src/Adapter/Product/CommandHandler/UpdateProductSeoHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,11 @@
namespace PrestaShop\PrestaShop\Adapter\Product\CommandHandler;

use PrestaShop\PrestaShop\Adapter\Product\AbstractProductHandler;
use PrestaShop\PrestaShop\Core\Domain\Category\Exception\CategoryNotFoundException;
use PrestaShop\PrestaShop\Core\Domain\Category\ValueObject\CategoryId;
use PrestaShop\PrestaShop\Adapter\Product\Repository\ProductRepository;
use PrestaShop\PrestaShop\Adapter\Product\Update\ProductSeoPropertiesFiller;
use PrestaShop\PrestaShop\Core\Domain\Product\Command\UpdateProductSeoCommand;
use PrestaShop\PrestaShop\Core\Domain\Product\CommandHandler\UpdateProductSeoHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\Product\Exception\CannotUpdateProductException;
use PrestaShop\PrestaShop\Core\Domain\Product\Exception\ProductConstraintException;
use PrestaShop\PrestaShop\Core\Domain\Product\Exception\ProductException;
use PrestaShop\PrestaShop\Core\Domain\Product\Exception\ProductNotFoundException;
use PrestaShop\PrestaShop\Core\Domain\Product\ValueObject\RedirectOption;
use Product;

/**
Expand All @@ -46,100 +42,73 @@
class UpdateProductSeoHandler extends AbstractProductHandler implements UpdateProductSeoHandlerInterface
{
/**
* {@inheritdoc}
* @var ProductRepository
*/
public function handle(UpdateProductSeoCommand $command): void
{
$product = $this->getProduct($command->getProductId());
$this->fillUpdatableFieldsWithCommandData($product, $command);
private $productRepository;

/**
* @var ProductSeoPropertiesFiller
*/
private $productSeoPropertiesFiller;

$this->performUpdate($product, CannotUpdateProductException::FAILED_UPDATE_SEO);
/**
* @param ProductRepository $productRepository
* @param ProductSeoPropertiesFiller $productSeoPropertiesFiller
*/
public function __construct(
ProductRepository $productRepository,
ProductSeoPropertiesFiller $productSeoPropertiesFiller
) {
$this->productRepository = $productRepository;
$this->productSeoPropertiesFiller = $productSeoPropertiesFiller;
}

/**
* @param Product $product
* @param UpdateProductSeoCommand $command
* {@inheritdoc}
*/
private function fillUpdatableFieldsWithCommandData(Product $product, UpdateProductSeoCommand $command): void
public function handle(UpdateProductSeoCommand $command): void
{
$redirectOption = $command->getRedirectOption();

if (null !== $redirectOption) {
$this->fillRedirectOptionValues($product, $redirectOption);
}

if (null !== $command->getLocalizedMetaDescriptions()) {
$product->meta_description = $command->getLocalizedMetaDescriptions();
$this->validateLocalizedField($product, 'meta_description', ProductConstraintException::INVALID_META_DESCRIPTION);
$this->fieldsToUpdate['meta_description'] = true;
}

if (null !== $command->getLocalizedMetaTitles()) {
$product->meta_title = $command->getLocalizedMetaTitles();
$this->validateLocalizedField($product, 'meta_title', ProductConstraintException::INVALID_META_TITLE);
$this->fieldsToUpdate['meta_title'] = true;
}
$product = $this->getProduct($command->getProductId());
$updatableProperties = $this->fillUpdatableProperties($product, $command);

if (null !== $command->getLocalizedLinkRewrites()) {
$product->link_rewrite = $command->getLocalizedLinkRewrites();
$this->validateLocalizedField($product, 'link_rewrite', ProductConstraintException::INVALID_LINK_REWRITE);
$this->fieldsToUpdate['link_rewrite'] = true;
}
$this->productRepository->partialUpdate($product, $updatableProperties, CannotUpdateProductException::FAILED_UPDATE_SEO);
}

/**
* @param Product $product
* @param RedirectOption $redirectOption
* @param UpdateProductSeoCommand $command
*
* @throws CategoryNotFoundException
* @throws ProductException
* @throws ProductNotFoundException
* @return array
*/
private function fillRedirectOptionValues(Product $product, RedirectOption $redirectOption): void
private function fillUpdatableProperties(Product $product, UpdateProductSeoCommand $command): array
{
$redirectType = $redirectOption->getRedirectType();
$redirectTarget = $redirectOption->getRedirectTarget();
$updatableProperties = [];

if ($redirectType->isProductType()) {
$this->assertProductExists($redirectTarget->getValue());
} elseif (!$redirectType->isTypeNotFound() && !$redirectTarget->isNoTarget()) {
$this->assertCategoryExists($redirectTarget->getValue());
if (null !== $command->getRedirectOption()) {
$updatableProperties = array_merge(
$updatableProperties,
$this->productSeoPropertiesFiller->fillWithRedirectOption($product, $command->getRedirectOption())
);
}

$product->redirect_type = $redirectType->getValue();
$product->id_type_redirected = $redirectTarget->getValue();
$this->fieldsToUpdate['redirect_type'] = true;
$this->fieldsToUpdate['id_type_redirected'] = true;
}
$localizedMetaDescriptions = $command->getLocalizedMetaDescriptions();
if (null !== $localizedMetaDescriptions) {
$product->meta_description = $localizedMetaDescriptions;
$updatableProperties['meta_description'] = array_keys($localizedMetaDescriptions);
}

/**
* @param int $categoryId
*
* @throws CategoryNotFoundException
* @throws ProductException
*/
private function assertCategoryExists(int $categoryId): void
{
if (!$this->entityExists('category', $categoryId)) {
throw new CategoryNotFoundException(
new CategoryId($categoryId),
sprintf('Category #%d does not exist', $categoryId)
);
$localizedMetaTitles = $command->getLocalizedMetaTitles();
if (null !== $localizedMetaTitles) {
$product->meta_title = $localizedMetaTitles;
$updatableProperties['meta_title'] = array_keys($localizedMetaTitles);
}
}

/**
* @param int $productId
*
* @throws ProductException
* @throws ProductNotFoundException
*/
private function assertProductExists(int $productId): void
{
if (!$this->entityExists('product', $productId)) {
throw new ProductNotFoundException(
sprintf('Product #%d does not exist', $productId)
);
$localizedLinkRewrites = $command->getLocalizedLinkRewrites();
if (null !== $localizedLinkRewrites) {
$product->link_rewrite = $localizedLinkRewrites;
$updatableProperties['link_rewrite'] = array_keys($localizedLinkRewrites);
}

return $updatableProperties;
}
}
95 changes: 95 additions & 0 deletions src/Adapter/Product/Update/ProductSeoPropertiesFiller.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to [email protected] so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <[email protected]>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
*/

declare(strict_types=1);

namespace PrestaShop\PrestaShop\Adapter\Product\Update;

use PrestaShop\PrestaShop\Adapter\Category\Repository\CategoryRepository;
use PrestaShop\PrestaShop\Adapter\Product\Repository\ProductRepository;
use PrestaShop\PrestaShop\Core\Domain\Category\Exception\CategoryNotFoundException;
use PrestaShop\PrestaShop\Core\Domain\Category\ValueObject\CategoryId;
use PrestaShop\PrestaShop\Core\Domain\Product\ValueObject\ProductId;
use PrestaShop\PrestaShop\Core\Domain\Product\ValueObject\RedirectOption;
use PrestaShop\PrestaShop\Core\Exception\CoreException;
use Product;

/**
* Fills Product object model SEO information fields according to domain specifics
*/
class ProductSeoPropertiesFiller
{
/**
* @var ProductRepository
*/
private $productRepository;

/**
* @var CategoryRepository
*/
private $categoryRepository;

/**
* @param ProductRepository $productRepository
* @param CategoryRepository $categoryRepository
*/
public function __construct(
ProductRepository $productRepository,
CategoryRepository $categoryRepository
) {
$this->productRepository = $productRepository;
$this->categoryRepository = $categoryRepository;
}

/**
* @param Product $product
* @param RedirectOption $redirectOption
*
* @return string[] updatable properties
*
* @throws CategoryNotFoundException
* @throws CoreException
*/
public function fillWithRedirectOption(Product $product, RedirectOption $redirectOption): array
{
$redirectType = $redirectOption->getRedirectType();
$redirectTarget = $redirectOption->getRedirectTarget();

if ($redirectType->isProductType()) {
$this->productRepository->assertProductExists(new ProductId($redirectTarget->getValue()));
} elseif ($redirectType->isCategoryType() && !$redirectTarget->isNoTarget()) {
$this->categoryRepository->assertCategoryExists(new CategoryId($redirectTarget->getValue()));
}

$product->redirect_type = $redirectType->getValue();
$product->id_type_redirected = $redirectTarget->getValue();

return [
'redirect_type',
'id_type_redirected',
];
}
}
14 changes: 13 additions & 1 deletion src/Adapter/Product/Validate/ProductValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public function validate(Product $product): void
$this->validateOptions($product);
$this->validateShipping($product);
$this->validateStock($product);
//@todo; more properties when refactoring other handlers to use updater/validator
$this->validateSeo($product);
}

/**
Expand Down Expand Up @@ -254,6 +254,18 @@ private function checkPackStockType(Product $product): void
);
}

/**
* @param Product $product
*/
private function validateSeo(Product $product): void
{
$this->validateProductProperty($product, 'redirect_type', ProductConstraintException::INVALID_REDIRECT_TYPE);
$this->validateProductProperty($product, 'id_type_redirected', ProductConstraintException::INVALID_REDIRECT_TARGET);
$this->validateProductLocalizedProperty($product, 'meta_description', ProductConstraintException::INVALID_META_DESCRIPTION);
$this->validateProductLocalizedProperty($product, 'meta_title', ProductConstraintException::INVALID_META_TITLE);
$this->validateProductLocalizedProperty($product, 'link_rewrite', ProductConstraintException::INVALID_LINK_REWRITE);
}

/**
* @param Product $product
* @param string $propertyName
Expand Down
16 changes: 15 additions & 1 deletion src/Core/Domain/Product/ValueObject/RedirectOption.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class RedirectOption
public function __construct(string $redirectType, int $redirectTarget)
{
$this->redirectType = new RedirectType($redirectType);
$this->redirectTarget = new RedirectTarget($redirectTarget);
$this->setRedirectTarget($redirectTarget);
$this->assertTypeAndTargetIntegrity();
}

Expand All @@ -72,6 +72,20 @@ public function getRedirectTarget(): RedirectTarget
return $this->redirectTarget;
}

/**
* @param int $value
*
* @throws ProductConstraintException
*/
private function setRedirectTarget(int $value): void
{
if ($this->redirectType->isTypeNotFound()) {
$value = RedirectTarget::NO_TARGET;
}

$this->redirectTarget = new RedirectTarget($value);
}

/**
* Checks if redirect type is compatible with provided redirect target
*/
Expand Down
8 changes: 8 additions & 0 deletions src/Core/Domain/Product/ValueObject/RedirectType.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,14 @@ public function isProductType(): bool
return in_array($this->value, [static::TYPE_PRODUCT_PERMANENT, static::TYPE_PRODUCT_TEMPORARY]);
}

/**
* @return bool
*/
public function isCategoryType(): bool
{
return in_array($this->value, [static::TYPE_CATEGORY_PERMANENT, static::TYPE_CATEGORY_TEMPORARY]);
}

/**
* @return bool
*/
Expand Down
Loading

0 comments on commit 3ea3a56

Please sign in to comment.