Subida del módulo y tema de PrestaShop

This commit is contained in:
Kaloyan
2026-04-09 18:31:51 +02:00
parent 12c253296f
commit 16b3ff9424
39262 changed files with 7418797 additions and 0 deletions

10
src/.htaccess Normal file
View File

@@ -0,0 +1,10 @@
# Apache 2.2
<IfModule !mod_authz_core.c>
Order deny,allow
Deny from all
</IfModule>
# Apache 2.4
<IfModule mod_authz_core.c>
Require all denied
</IfModule>

View File

@@ -0,0 +1,92 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
declare(strict_types=1);
namespace PrestaShop\PrestaShop\Adapter;
use Configuration;
use ObjectModel;
use PrestaShop\PrestaShop\Core\Exception\CoreException;
use PrestaShopException;
/**
* Reusable methods for validating legacy object models
*/
abstract class AbstractObjectModelValidator
{
/**
* @param ObjectModel $objectModel
* @param string $propertyName
* @param string $exceptionClass
* @param int $errorCode
*
* @throws CoreException
*/
protected function validateObjectModelProperty(ObjectModel $objectModel, string $propertyName, string $exceptionClass, int $errorCode = 0): void
{
try {
if (true !== $objectModel->validateField($propertyName, $objectModel->{$propertyName})) {
throw new $exceptionClass(
sprintf(
'Invalid %s %s. Got "%s"',
$objectModel::class,
$propertyName,
$objectModel->{$propertyName}
),
$errorCode
);
}
} catch (PrestaShopException $e) {
throw new CoreException(
sprintf('Error occurred when validating %s property "%s"', $objectModel::class, $propertyName),
0,
$e
);
}
}
/**
* @param ObjectModel $objectModel
* @param string $propertyName
* @param string $exceptionClass
* @param int $errorCode
*
* @throws CoreException
*/
protected function validateObjectModelLocalizedProperty(ObjectModel $objectModel, string $propertyName, string $exceptionClass, int $errorCode = 0)
{
$localizedValues = $objectModel->{$propertyName} ?? [];
try {
$defaultLang = (int) Configuration::get('PS_LANG_DEFAULT');
if (!isset($localizedValues[$defaultLang])) {
// The value for the default must always be set, so we put an empty string if it does not exist
$localizedValues[$defaultLang] = '';
}
foreach ($localizedValues as $langId => $value) {
if (true !== $objectModel->validateField($propertyName, $value, $langId)) {
throw new $exceptionClass(
sprintf(
'Invalid %s localized property "%s" for language with id "%d"',
$objectModel::class,
$propertyName,
$langId
),
$errorCode
);
}
}
} catch (PrestaShopException $e) {
throw new CoreException(
sprintf('Error occurred when trying to validate %s localized property "%s"', $objectModel::class, $propertyName),
0,
$e
);
}
}
}

View File

@@ -0,0 +1,75 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\Address;
use Address;
use PrestaShop\PrestaShop\Core\Domain\Address\Exception\AddressException;
use PrestaShop\PrestaShop\Core\Domain\Address\Exception\AddressNotFoundException;
use PrestaShop\PrestaShop\Core\Domain\Address\Exception\InvalidAddressFieldException;
use PrestaShop\PrestaShop\Core\Domain\Address\ValueObject\AddressId;
use PrestaShopException;
/**
* Provides reusable methods for address command/query handlers
*/
abstract class AbstractAddressHandler
{
/**
* @param AddressId $addressId
*
* @return Address
*
* @throws AddressException
* @throws AddressNotFoundException
*/
protected function getAddress(AddressId $addressId)
{
try {
$address = new Address($addressId->getValue());
} catch (PrestaShopException $e) {
throw new AddressException('Failed to create new address', 0, $e);
}
if ($address->id !== $addressId->getValue()) {
throw new AddressNotFoundException(sprintf('Address with id "%s" was not found.', $addressId->getValue()));
}
return $address;
}
/**
* Deletes legacy Address
*
* @param Address $address
*
* @return bool
*
* @throws AddressException
*/
protected function deleteAddress(Address $address): bool
{
try {
return $address->delete();
} catch (PrestaShopException) {
throw new AddressException(sprintf('An error occurred when deleting Address object with id "%s".', $address->id));
}
}
/**
* @param Address $address
*
* @throws InvalidAddressFieldException
* @throws PrestaShopException
*/
protected function validateAddress(Address $address): void
{
if (true !== ($validateResult = $address->validateFields(false, true))
|| true !== ($validateResult = $address->validateFieldsLang(false, true))) {
throw new InvalidAddressFieldException(sprintf('Address fields contain invalid values: %s', $validateResult));
}
}
}

View File

@@ -0,0 +1,40 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\Address;
use CustomerAddress;
use PrestaShop\PrestaShop\Core\Domain\Address\Exception\AddressException;
use PrestaShopDatabaseException;
abstract class AbstractCustomerAddressHandler extends AbstractAddressHandler
{
/**
* @return string[]
*
* @throws AddressException
*/
protected function getRequiredFields(): array
{
try {
$requiredFields = (new CustomerAddress())->getFieldsRequiredDatabase();
} catch (PrestaShopDatabaseException $e) {
throw new AddressException('Something went wrong while retrieving required fields for address', 0, $e);
}
if (empty($requiredFields)) {
return [];
}
$fields = [];
foreach ($requiredFields as $field) {
$fields[] = $field['field_name'];
}
return $fields;
}
}

View File

@@ -0,0 +1,29 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
declare(strict_types=1);
namespace PrestaShop\PrestaShop\Adapter\Address;
use Address;
use AddressFormat;
use PrestaShop\PrestaShop\Core\Address\AddressFormatterInterface;
use PrestaShop\PrestaShop\Core\Domain\Address\ValueObject\AddressId;
class AddressFormatter implements AddressFormatterInterface
{
/**
* @param AddressId $addressId
*
* @return string
*/
public function format(AddressId $addressId): string
{
return AddressFormat::generateAddress(
new Address($addressId->getValue())
);
}
}

View File

@@ -0,0 +1,75 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\Address\CommandHandler;
use Address;
use PrestaShop\PrestaShop\Adapter\Address\AbstractAddressHandler;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\Address\Command\AddCustomerAddressCommand;
use PrestaShop\PrestaShop\Core\Domain\Address\CommandHandler\AddCustomerAddressHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\Address\Exception\AddressConstraintException;
use PrestaShop\PrestaShop\Core\Domain\Address\Exception\AddressException;
use PrestaShop\PrestaShop\Core\Domain\Address\Exception\CannotAddAddressException;
use PrestaShop\PrestaShop\Core\Domain\Address\ValueObject\AddressId;
use PrestaShopException;
#[AsCommandHandler]
final class AddCustomerAddressHandler extends AbstractAddressHandler implements AddCustomerAddressHandlerInterface
{
/**
* {@inheritdoc}
*
* @throws AddressException
* @throws AddressConstraintException
* @throws CannotAddAddressException
*/
public function handle(AddCustomerAddressCommand $command): AddressId
{
$address = $this->createAddressFromCommand($command);
try {
$this->validateAddress($address);
if (false === $address->add()) {
throw new CannotAddAddressException(sprintf('Failed to add new address "%s"', $command->getAddress()));
}
} catch (PrestaShopException) {
throw new AddressException(sprintf('An error occurred when adding new address "%s"', $command->getAddress()));
}
return new AddressId((int) $address->id);
}
/**
* @param AddCustomerAddressCommand $command
*
* @return Address
*/
private function createAddressFromCommand(AddCustomerAddressCommand $command): Address
{
$address = new Address();
$address->id_customer = $command->getCustomerId()->getValue();
$address->lastname = $command->getLastName();
$address->firstname = $command->getFirstName();
$address->address1 = $command->getAddress();
$address->id_country = $command->getCountryId()->getValue();
$address->city = $command->getCity();
$address->alias = $command->getAddressAlias();
$address->postcode = $command->getPostCode();
$address->address2 = $command->getAddress2();
$address->dni = $command->getDni();
$address->company = $command->getCompany();
$address->vat_number = $command->getVatNumber();
$address->id_state = $command->getStateId()->getValue();
$address->phone = $command->getHomePhone();
$address->phone_mobile = $command->getMobilePhone();
$address->other = $command->getOther();
return $address;
}
}

View File

@@ -0,0 +1,72 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\Address\CommandHandler;
use Address;
use PrestaShop\PrestaShop\Adapter\Address\AbstractAddressHandler;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\Address\Command\AddManufacturerAddressCommand;
use PrestaShop\PrestaShop\Core\Domain\Address\CommandHandler\AddManufacturerAddressHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\Address\Exception\AddressException;
use PrestaShop\PrestaShop\Core\Domain\Address\ValueObject\AddressId;
use PrestaShopException;
/**
* Adds manufacturer address using legacy object model
*/
#[AsCommandHandler]
final class AddManufacturerAddressHandler extends AbstractAddressHandler implements AddManufacturerAddressHandlerInterface
{
/**
* @param AddManufacturerAddressCommand $command
*
* @return AddressId
*
* @throws AddressException
*/
public function handle(AddManufacturerAddressCommand $command)
{
$address = $this->createAddressFromCommand($command);
try {
$this->validateAddress($address);
if (false === $address->add()) {
throw new AddressException(sprintf('Failed to add new address "%s"', $command->getAddress()));
}
} catch (PrestaShopException) {
throw new AddressException(sprintf('An error occurred when adding new address "%s"', $command->getAddress()));
}
return new AddressId((int) $address->id);
}
/**
* @param AddManufacturerAddressCommand $command
*
* @return Address
*/
private function createAddressFromCommand(AddManufacturerAddressCommand $command)
{
$address = new Address();
$address->id_manufacturer = $command->getManufacturerId();
$address->lastname = $command->getLastName();
$address->firstname = $command->getFirstName();
$address->address1 = $command->getAddress();
$address->address2 = $command->getAddress2();
$address->postcode = $command->getPostCode();
$address->id_country = $command->getCountryId();
$address->city = $command->getCity();
$address->id_state = $command->getStateId();
$address->phone = $command->getHomePhone();
$address->phone_mobile = $command->getMobilePhone();
$address->other = $command->getOther();
$address->dni = $command->getDni();
$address->alias = 'manufacturer';
return $address;
}
}

View File

@@ -0,0 +1,47 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\Address\CommandHandler;
use PrestaShop\PrestaShop\Adapter\Address\AbstractAddressHandler;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\Address\Command\BulkDeleteAddressCommand;
use PrestaShop\PrestaShop\Core\Domain\Address\CommandHandler\BulkDeleteAddressHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\Address\Exception\AddressException;
use PrestaShop\PrestaShop\Core\Domain\Address\Exception\BulkDeleteAddressException;
/**
* Handles command which deletes addresses in bulk action
*/
#[AsCommandHandler]
final class BulkDeleteAddressHandler extends AbstractAddressHandler implements BulkDeleteAddressHandlerInterface
{
/**
* {@inheritdoc}
*
* @throws BulkDeleteAddressException
*/
public function handle(BulkDeleteAddressCommand $command)
{
$errors = [];
foreach ($command->getAdressIds() as $addressId) {
try {
$address = $this->getAddress($addressId);
if (!$this->deleteAddress($address)) {
$errors[] = $address->id;
}
} catch (AddressException) {
$errors[] = $addressId->getValue();
}
}
if (!empty($errors)) {
throw new BulkDeleteAddressException($errors, 'Failed to delete all of selected addresses');
}
}
}

View File

@@ -0,0 +1,33 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\Address\CommandHandler;
use PrestaShop\PrestaShop\Adapter\Address\AbstractAddressHandler;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\Address\Command\DeleteAddressCommand;
use PrestaShop\PrestaShop\Core\Domain\Address\CommandHandler\DeleteAddressHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\Address\Exception\DeleteAddressException;
/**
* Handles command which deletes address
*/
#[AsCommandHandler]
final class DeleteAddressHandler extends AbstractAddressHandler implements DeleteAddressHandlerInterface
{
/**
* {@inheritdoc}
*/
public function handle(DeleteAddressCommand $command)
{
$addressId = $command->getAddressId();
$address = $this->getAddress($addressId);
if (!$this->deleteAddress($address)) {
throw new DeleteAddressException(sprintf('Cannot delete Address object with id "%s".', $addressId->getValue()), DeleteAddressException::FAILED_DELETE);
}
}
}

View File

@@ -0,0 +1,154 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
declare(strict_types=1);
namespace PrestaShop\PrestaShop\Adapter\Address\CommandHandler;
use Cart;
use PrestaShop\PrestaShop\Adapter\Validate;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\Address\Command\EditCartAddressCommand;
use PrestaShop\PrestaShop\Core\Domain\Address\Command\EditCustomerAddressCommand;
use PrestaShop\PrestaShop\Core\Domain\Address\CommandHandler\EditCartAddressHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\Address\CommandHandler\EditCustomerAddressHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\Address\Exception\AddressConstraintException;
use PrestaShop\PrestaShop\Core\Domain\Address\Exception\CannotUpdateCartAddressException;
use PrestaShop\PrestaShop\Core\Domain\Address\ValueObject\AddressId;
use PrestaShop\PrestaShop\Core\Domain\Cart\CartAddressType;
use PrestaShop\PrestaShop\Core\Domain\Cart\Exception\CartNotFoundException;
use PrestaShop\PrestaShop\Core\Domain\Country\Exception\CountryConstraintException;
use PrestaShop\PrestaShop\Core\Domain\State\Exception\StateConstraintException;
use PrestaShopException;
/**
* EditCartAddressHandler manages an address update, it then updates cart
* relation to the newly created address.
*/
#[AsCommandHandler]
class EditCartAddressHandler implements EditCartAddressHandlerInterface
{
/**
* @var EditCustomerAddressHandlerInterface
*/
private $addressHandler;
/**
* @param EditCustomerAddressHandlerInterface $addressHandler
*/
public function __construct(EditCustomerAddressHandlerInterface $addressHandler)
{
$this->addressHandler = $addressHandler;
}
/**
* {@inheritdoc}
*
* @throws AddressConstraintException
* @throws CannotUpdateCartAddressException
* @throws CountryConstraintException
* @throws StateConstraintException
*/
public function handle(EditCartAddressCommand $command): AddressId
{
try {
$cart = new Cart($command->getCartId()->getValue());
if (!Validate::isLoadedObject($cart) || $command->getCartId()->getValue() !== (int) $cart->id) {
throw new CartNotFoundException(sprintf('Cart with id "%d" was not found', $command->getCartId()->getValue()));
}
$addressCommand = $this->createEditAddressCommand($command, $cart);
/** @var AddressId $addressId */
$addressId = $this->addressHandler->handle($addressCommand);
switch ($command->getAddressType()) {
case CartAddressType::DELIVERY_ADDRESS_TYPE:
$cart->id_address_delivery = $addressId->getValue();
break;
case CartAddressType::INVOICE_ADDRESS_TYPE:
$cart->id_address_invoice = $addressId->getValue();
break;
}
if (!$cart->update()) {
throw new CannotUpdateCartAddressException(sprintf('An error occurred when updating address for cart "%d"', $command->getCartId()->getValue()));
}
} catch (PrestaShopException) {
throw new CannotUpdateCartAddressException(sprintf('An error occurred when updating address for cart "%d"', $command->getCartId()->getValue()));
}
return $addressId;
}
/**
* @param EditCartAddressCommand $cartCommand
*
* @return EditCustomerAddressCommand
*
* @throws AddressConstraintException
* @throws CountryConstraintException
* @throws StateConstraintException
* @throws PrestaShopException
*/
private function createEditAddressCommand(EditCartAddressCommand $cartCommand, Cart $cart): EditCustomerAddressCommand
{
$addressId = null;
switch ($cartCommand->getAddressType()) {
case CartAddressType::DELIVERY_ADDRESS_TYPE:
$addressId = (int) $cart->id_address_delivery;
break;
case CartAddressType::INVOICE_ADDRESS_TYPE:
$addressId = (int) $cart->id_address_invoice;
break;
}
$addressCommand = new EditCustomerAddressCommand($addressId);
if (null !== $cartCommand->getAddressAlias()) {
$addressCommand->setAddressAlias($cartCommand->getAddressAlias());
}
if (null !== $cartCommand->getFirstName()) {
$addressCommand->setFirstName($cartCommand->getFirstName());
}
if (null !== $cartCommand->getLastName()) {
$addressCommand->setLastName($cartCommand->getLastName());
}
if (null !== $cartCommand->getAddress()) {
$addressCommand->setAddress($cartCommand->getAddress());
}
if (null !== $cartCommand->getCity()) {
$addressCommand->setCity($cartCommand->getCity());
}
if (null !== $cartCommand->getPostCode()) {
$addressCommand->setPostCode($cartCommand->getPostCode());
}
if (null !== $cartCommand->getCountryId()) {
$addressCommand->setCountryId($cartCommand->getCountryId()->getValue());
}
if (null !== $cartCommand->getDni()) {
$addressCommand->setDni($cartCommand->getDni());
}
if (null !== $cartCommand->getCompany()) {
$addressCommand->setCompany($cartCommand->getCompany());
}
if (null !== $cartCommand->getVatNumber()) {
$addressCommand->setVatNumber($cartCommand->getVatNumber());
}
if (null !== $cartCommand->getAddress2()) {
$addressCommand->setAddress2($cartCommand->getAddress2());
}
if (null !== $cartCommand->getStateId()) {
$addressCommand->setStateId($cartCommand->getStateId()->getValue());
}
if (null !== $cartCommand->getHomePhone()) {
$addressCommand->setHomePhone($cartCommand->getHomePhone());
}
if (null !== $cartCommand->getMobilePhone()) {
$addressCommand->setMobilePhone($cartCommand->getMobilePhone());
}
if (null !== $cartCommand->getOther()) {
$addressCommand->setOther($cartCommand->getOther());
}
return $addressCommand;
}
}

View File

@@ -0,0 +1,151 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\Address\CommandHandler;
use Address;
use Country;
use PrestaShop\PrestaShop\Adapter\Address\AbstractAddressHandler;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\Address\Command\EditCustomerAddressCommand;
use PrestaShop\PrestaShop\Core\Domain\Address\CommandHandler\EditCustomerAddressHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\Address\Exception\AddressConstraintException;
use PrestaShop\PrestaShop\Core\Domain\Address\Exception\AddressException;
use PrestaShop\PrestaShop\Core\Domain\Address\Exception\AddressNotFoundException;
use PrestaShop\PrestaShop\Core\Domain\Address\Exception\CannotAddAddressException;
use PrestaShop\PrestaShop\Core\Domain\Address\Exception\CannotUpdateAddressException;
use PrestaShop\PrestaShop\Core\Domain\Address\Exception\DeleteAddressException;
use PrestaShop\PrestaShop\Core\Domain\Address\ValueObject\AddressId;
use PrestaShopException;
/**
* Handles update of customer address
*/
#[AsCommandHandler]
final class EditCustomerAddressHandler extends AbstractAddressHandler implements EditCustomerAddressHandlerInterface
{
/**
* {@inheritdoc}
*
* @throws AddressException
* @throws AddressConstraintException
* @throws CannotUpdateAddressException
*/
public function handle(EditCustomerAddressCommand $command): AddressId
{
try {
$editedAddress = $this->getAddressFromCommand($command);
$this->validateAddress($editedAddress);
// The address is used by an order so it is not edited directly, instead a copy is created and
if ($editedAddress->isUsed()) {
// Get a copy of current address
$copyAddress = new Address($editedAddress->id);
// Reset ID to force recreating a new address
$editedAddress->id = $editedAddress->id_address = null;
// We consider this address as necessarily NOT deleted, in case you were editing a deleted address
// from an order then the newly edited address should not be deleted, so that you can select it
$editedAddress->deleted = false;
if (false === $editedAddress->save()) {
throw new CannotAddAddressException(sprintf('Failed to add new address "%s"', $command->getAddress()));
}
// Soft delete the former address
if (false === $copyAddress->delete()) {
throw new DeleteAddressException(sprintf('Cannot delete Address object with id "%s".', $copyAddress->id), DeleteAddressException::FAILED_DELETE);
}
} elseif (false === $editedAddress->update()) {
throw new CannotUpdateAddressException(sprintf('Failed to update address "%s"', $editedAddress->id));
}
} catch (PrestaShopException) {
throw new AddressException(sprintf('An error occurred when updating address "%s"', $command->getAddressId()->getValue()));
}
return new AddressId((int) $editedAddress->id);
}
/**
* @param EditCustomerAddressCommand $command
*
* @return Address
*
* @throws AddressException
* @throws AddressNotFoundException
*/
private function getAddressFromCommand(EditCustomerAddressCommand $command): Address
{
$address = $this->getAddress($command->getAddressId());
if (null !== $command->getLastName()) {
$address->lastname = $command->getLastName();
}
if (null !== $command->getFirstName()) {
$address->firstname = $command->getFirstName();
}
if (null !== $command->getAddress()) {
$address->address1 = $command->getAddress();
}
if (null !== $command->getPostCode()) {
$address->postcode = $command->getPostCode();
}
if (null !== $command->getCountryId()) {
$address->id_country = $command->getCountryId()->getValue();
}
if (null !== $command->getStateId()) {
$address->id_state = $command->getStateId()->getValue();
} elseif (null !== $command->getCountryId()) {
// If country was changed but not state we check if state value needs to be reset
$country = new Country($command->getCountryId()->getValue());
if (!$country->contains_states) {
$address->id_state = 0;
}
}
if (null !== $command->getCity()) {
$address->city = $command->getCity();
}
if (null !== $command->getAddressAlias()) {
$address->alias = $command->getAddressAlias();
}
if (null !== $command->getAddress2()) {
$address->address2 = $command->getAddress2();
}
if (null !== $command->getDni()) {
$address->dni = $command->getDni();
}
if (null !== $command->getCompany()) {
$address->company = $command->getCompany();
}
if (null !== $command->getVatNumber()) {
$address->vat_number = $command->getVatNumber();
}
if (null !== $command->getHomePhone()) {
$address->phone = $command->getHomePhone();
}
if (null !== $command->getMobilePhone()) {
$address->phone_mobile = $command->getMobilePhone();
}
if (null !== $command->getOther()) {
$address->other = $command->getOther();
}
return $address;
}
}

View File

@@ -0,0 +1,97 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\Address\CommandHandler;
use Address;
use Country;
use PrestaShop\PrestaShop\Adapter\Address\AbstractAddressHandler;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\Address\Command\EditManufacturerAddressCommand;
use PrestaShop\PrestaShop\Core\Domain\Address\CommandHandler\EditManufacturerAddressHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\Address\Exception\AddressException;
use PrestaShopException;
/**
* Handles command which edits manufacturer address
*/
#[AsCommandHandler]
final class EditManufacturerAddressHandler extends AbstractAddressHandler implements EditManufacturerAddressHandlerInterface
{
/**
* {@inheritdoc}
*/
public function handle(EditManufacturerAddressCommand $command)
{
$addressId = $command->getAddressId();
$address = $this->getAddress($addressId);
$this->populateAddressWithData($address, $command);
try {
$this->validateAddress($address);
if (!$address->update()) {
throw new AddressException(sprintf('Cannot update address with id "%s"', $address->id));
}
} catch (PrestaShopException) {
throw new AddressException(sprintf('Cannot update address with id "%s"', $address->id));
}
}
/**
* Populates Address object with given data
*
* @param Address $address
* @param EditManufacturerAddressCommand $command
*/
private function populateAddressWithData(Address $address, $command)
{
if (null !== $command->getManufacturerId()) {
$address->id_manufacturer = $command->getManufacturerId();
}
if (null !== $command->getLastName()) {
$address->lastname = $command->getLastName();
}
if (null !== $command->getFirstName()) {
$address->firstname = $command->getFirstName();
}
if (null !== $command->getAddress()) {
$address->address1 = $command->getAddress();
}
if (null !== $command->getAddress2()) {
$address->address2 = $command->getAddress2();
}
if (null !== $command->getPostCode()) {
$address->postcode = $command->getPostCode();
}
if (null !== $command->getCity()) {
$address->city = $command->getCity();
}
if (null !== $command->getCountryId()) {
$address->id_country = $command->getCountryId();
}
if (null !== $command->getStateId()) {
$address->id_state = $command->getStateId();
} elseif (null !== $command->getCountryId()) {
// If country was changed but not state we check if state value needs to be reset
$country = new Country($command->getCountryId());
if (!$country->contains_states) {
$address->id_state = 0;
}
}
if (null !== $command->getHomePhone()) {
$address->phone = $command->getHomePhone();
}
if (null !== $command->getMobilePhone()) {
$address->phone_mobile = $command->getMobilePhone();
}
if (null !== $command->getOther()) {
$address->other = $command->getOther();
}
if (null !== $command->getDni()) {
$address->dni = $command->getDni();
}
}
}

View File

@@ -0,0 +1,170 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
declare(strict_types=1);
namespace PrestaShop\PrestaShop\Adapter\Address\CommandHandler;
use Order;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\Address\Command\EditCustomerAddressCommand;
use PrestaShop\PrestaShop\Core\Domain\Address\Command\EditOrderAddressCommand;
use PrestaShop\PrestaShop\Core\Domain\Address\CommandHandler\EditCustomerAddressHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\Address\CommandHandler\EditOrderAddressHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\Address\Exception\AddressConstraintException;
use PrestaShop\PrestaShop\Core\Domain\Address\Exception\CannotUpdateOrderAddressException;
use PrestaShop\PrestaShop\Core\Domain\Address\ValueObject\AddressId;
use PrestaShop\PrestaShop\Core\Domain\Country\Exception\CountryConstraintException;
use PrestaShop\PrestaShop\Core\Domain\Order\Command\ChangeOrderDeliveryAddressCommand;
use PrestaShop\PrestaShop\Core\Domain\Order\Command\ChangeOrderInvoiceAddressCommand;
use PrestaShop\PrestaShop\Core\Domain\Order\CommandHandler\ChangeOrderDeliveryAddressHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\Order\CommandHandler\ChangeOrderInvoiceAddressHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\Order\OrderAddressType;
use PrestaShop\PrestaShop\Core\Domain\State\Exception\StateConstraintException;
use PrestaShopException;
/**
* EditOrderAddressCommandHandler manages an address update, it then updates order and cart
* relation to the newly created address.
*/
#[AsCommandHandler]
class EditOrderAddressHandler implements EditOrderAddressHandlerInterface
{
/**
* @var EditCustomerAddressHandlerInterface
*/
private $addressHandler;
/**
* @var ChangeOrderDeliveryAddressHandlerInterface
*/
private $deliveryAddressHandler;
/**
* @var ChangeOrderInvoiceAddressHandlerInterface
*/
private $invoiceAddressHandler;
/**
* @param EditCustomerAddressHandlerInterface $addressHandler
* @param ChangeOrderDeliveryAddressHandlerInterface $deliveryAddressHandler
* @param ChangeOrderInvoiceAddressHandlerInterface $invoiceAddressHandler
*/
public function __construct(
EditCustomerAddressHandlerInterface $addressHandler,
ChangeOrderDeliveryAddressHandlerInterface $deliveryAddressHandler,
ChangeOrderInvoiceAddressHandlerInterface $invoiceAddressHandler
) {
$this->addressHandler = $addressHandler;
$this->deliveryAddressHandler = $deliveryAddressHandler;
$this->invoiceAddressHandler = $invoiceAddressHandler;
}
/**
* {@inheritdoc}
*
* @throws AddressConstraintException
* @throws CannotUpdateOrderAddressException
* @throws CountryConstraintException
* @throws StateConstraintException
*/
public function handle(EditOrderAddressCommand $command): AddressId
{
try {
$addressCommand = $this->createEditAddressCommand($command);
/** @var AddressId $addressId */
$addressId = $this->addressHandler->handle($addressCommand);
switch ($command->getAddressType()) {
case OrderAddressType::DELIVERY_ADDRESS_TYPE:
$this->deliveryAddressHandler->handle(new ChangeOrderDeliveryAddressCommand(
$command->getOrderId()->getValue(), $addressId->getValue()
));
break;
case OrderAddressType::INVOICE_ADDRESS_TYPE:
$this->invoiceAddressHandler->handle(new ChangeOrderInvoiceAddressCommand(
$command->getOrderId()->getValue(), $addressId->getValue()
));
break;
}
} catch (PrestaShopException) {
throw new CannotUpdateOrderAddressException(sprintf('An error occurred when updating address for order "%s"', $command->getOrderId()->getValue()));
}
return $addressId;
}
/**
* @param EditOrderAddressCommand $orderCommand
*
* @return EditCustomerAddressCommand
*
* @throws AddressConstraintException
* @throws CountryConstraintException
* @throws StateConstraintException
* @throws PrestaShopException
*/
private function createEditAddressCommand(EditOrderAddressCommand $orderCommand): EditCustomerAddressCommand
{
$order = new Order($orderCommand->getOrderId()->getValue());
$addressId = null;
switch ($orderCommand->getAddressType()) {
case OrderAddressType::DELIVERY_ADDRESS_TYPE:
$addressId = (int) $order->id_address_delivery;
break;
case OrderAddressType::INVOICE_ADDRESS_TYPE:
$addressId = (int) $order->id_address_invoice;
break;
}
$addressCommand = new EditCustomerAddressCommand($addressId);
if (null !== $orderCommand->getAddressAlias()) {
$addressCommand->setAddressAlias($orderCommand->getAddressAlias());
}
if (null !== $orderCommand->getFirstName()) {
$addressCommand->setFirstName($orderCommand->getFirstName());
}
if (null !== $orderCommand->getLastName()) {
$addressCommand->setLastName($orderCommand->getLastName());
}
if (null !== $orderCommand->getAddress()) {
$addressCommand->setAddress($orderCommand->getAddress());
}
if (null !== $orderCommand->getCity()) {
$addressCommand->setCity($orderCommand->getCity());
}
if (null !== $orderCommand->getPostCode()) {
$addressCommand->setPostCode($orderCommand->getPostCode());
}
if (null !== $orderCommand->getCountryId()) {
$addressCommand->setCountryId($orderCommand->getCountryId()->getValue());
}
if (null !== $orderCommand->getDni()) {
$addressCommand->setDni($orderCommand->getDni());
}
if (null !== $orderCommand->getCompany()) {
$addressCommand->setCompany($orderCommand->getCompany());
}
if (null !== $orderCommand->getVatNumber()) {
$addressCommand->setVatNumber($orderCommand->getVatNumber());
}
if (null !== $orderCommand->getAddress2()) {
$addressCommand->setAddress2($orderCommand->getAddress2());
}
if (null !== $orderCommand->getStateId()) {
$addressCommand->setStateId($orderCommand->getStateId()->getValue());
}
if (null !== $orderCommand->getHomePhone()) {
$addressCommand->setHomePhone($orderCommand->getHomePhone());
}
if (null !== $orderCommand->getMobilePhone()) {
$addressCommand->setMobilePhone($orderCommand->getMobilePhone());
}
if (null !== $orderCommand->getOther()) {
$addressCommand->setOther($orderCommand->getOther());
}
return $addressCommand;
}
}

View File

@@ -0,0 +1,42 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\Address\CommandHandler;
use CustomerAddress;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\Address\Command\SetRequiredFieldsForAddressCommand;
use PrestaShop\PrestaShop\Core\Domain\Address\CommandHandler\SetRequiredFieldsForAddressHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\Address\Exception\CannotSetRequiredFieldsForAddressException;
use PrestaShopDatabaseException;
/**
* Handles command which sets required fields for address.
*
* @internal
*/
#[AsCommandHandler]
final class SetRequiredFieldsForAddressHandler implements SetRequiredFieldsForAddressHandlerInterface
{
/**
* {@inheritdoc}
*
* @throws CannotSetRequiredFieldsForAddressException
*/
public function handle(SetRequiredFieldsForAddressCommand $command)
{
$address = new CustomerAddress();
try {
if ($address->addFieldsRequiredDatabase($command->getRequiredFields())) {
return;
}
} catch (PrestaShopDatabaseException) {
}
throw new CannotSetRequiredFieldsForAddressException(sprintf('Cannot set "%s" required fields for customer', implode(',', $command->getRequiredFields())));
}
}

View File

@@ -0,0 +1,83 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\Address\QueryHandler;
use Customer;
use PrestaShop\PrestaShop\Adapter\Address\AbstractCustomerAddressHandler;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsQueryHandler;
use PrestaShop\PrestaShop\Core\Domain\Address\Exception\AddressException;
use PrestaShop\PrestaShop\Core\Domain\Address\Exception\AddressNotFoundException;
use PrestaShop\PrestaShop\Core\Domain\Address\Query\GetCustomerAddressForEditing;
use PrestaShop\PrestaShop\Core\Domain\Address\QueryHandler\GetCustomerAddressForEditingHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\Address\QueryResult\EditableCustomerAddress;
use PrestaShop\PrestaShop\Core\Domain\Country\Exception\CountryConstraintException;
use PrestaShop\PrestaShop\Core\Domain\Country\ValueObject\CountryId;
use PrestaShop\PrestaShop\Core\Domain\Customer\Exception\CustomerException;
use PrestaShop\PrestaShop\Core\Domain\Customer\Exception\CustomerNotFoundException;
use PrestaShop\PrestaShop\Core\Domain\Customer\ValueObject\CustomerId;
use PrestaShop\PrestaShop\Core\Domain\State\Exception\StateConstraintException;
use PrestaShop\PrestaShop\Core\Domain\State\ValueObject\NoStateId;
use PrestaShop\PrestaShop\Core\Domain\State\ValueObject\StateId;
use PrestaShopException;
/**
* Handles query which gets customer address for editing
*/
#[AsQueryHandler]
final class GetCustomerAddressForEditingHandler extends AbstractCustomerAddressHandler implements GetCustomerAddressForEditingHandlerInterface
{
/**
* {@inheritdoc}
*
* @throws AddressException
* @throws AddressNotFoundException
* @throws CustomerException
* @throws CustomerNotFoundException
* @throws CountryConstraintException
* @throws StateConstraintException
*/
public function handle(GetCustomerAddressForEditing $query): EditableCustomerAddress
{
$addressId = $query->getAddressId();
$address = $this->getAddress($addressId);
try {
$customerId = new CustomerId((int) $address->id_customer);
$customer = new Customer($customerId->getValue());
} catch (PrestaShopException $e) {
throw new CustomerException('Failed to get customer', 0, $e);
}
if ($customer->id !== $customerId->getValue()) {
throw new CustomerNotFoundException(sprintf('Customer with id "%d" was not found.', $customerId->getValue()));
}
$editableCustomerAddress = new EditableCustomerAddress(
$addressId,
$customerId,
$customer->email,
$address->alias,
$address->firstname,
$address->lastname,
$address->address1,
$address->city,
new CountryId((int) $address->id_country),
$address->postcode,
$address->dni,
$address->company,
$address->vat_number,
$address->address2,
(int) $address->id_state !== NoStateId::NO_STATE_ID_VALUE ? new StateId($address->id_state) : new NoStateId(),
$address->phone,
$address->phone_mobile,
$address->other,
$this->getRequiredFields()
);
return $editableCustomerAddress;
}
}

View File

@@ -0,0 +1,47 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\Address\QueryHandler;
use PrestaShop\PrestaShop\Adapter\Address\AbstractAddressHandler;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsQueryHandler;
use PrestaShop\PrestaShop\Core\Domain\Address\Query\GetManufacturerAddressForEditing;
use PrestaShop\PrestaShop\Core\Domain\Address\QueryHandler\GetManufacturerAddressForEditingHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\Address\QueryResult\EditableManufacturerAddress;
/**
* Handles query which gets manufacturer address for editing
*/
#[AsQueryHandler]
final class GetManufacturerAddressForEditingHandler extends AbstractAddressHandler implements GetManufacturerAddressForEditingHandlerInterface
{
/**
* {@inheritdoc}
*/
public function handle(GetManufacturerAddressForEditing $query)
{
$addressId = $query->getAddressId();
$address = $this->getAddress($addressId);
return new EditableManufacturerAddress(
$addressId,
$address->lastname,
$address->firstname,
$address->address1,
$address->city,
(int) $address->id_manufacturer,
(int) $address->id_country,
$address->address2,
$address->postcode,
(int) $address->id_state,
$address->phone,
$address->phone_mobile,
$address->other,
$address->dni
);
}
}

View File

@@ -0,0 +1,32 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\Address\QueryHandler;
use PrestaShop\PrestaShop\Adapter\Address\AbstractCustomerAddressHandler;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsQueryHandler;
use PrestaShop\PrestaShop\Core\Domain\Address\Exception\AddressException;
use PrestaShop\PrestaShop\Core\Domain\Address\Query\GetRequiredFieldsForAddress;
use PrestaShop\PrestaShop\Core\Domain\Address\QueryHandler\GetRequiredFieldsForAddressHandlerInterface;
/**
* Handles query which gets required fields for address
*
* @internal
*/
#[AsQueryHandler]
final class GetRequiredFieldsForAddressHandler extends AbstractCustomerAddressHandler implements GetRequiredFieldsForAddressHandlerInterface
{
/**
* {@inheritdoc}
*
* @throws AddressException
*/
public function handle(GetRequiredFieldsForAddress $query): array
{
return $this->getRequiredFields();
}
}

View File

@@ -0,0 +1,42 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
declare(strict_types=1);
namespace PrestaShop\PrestaShop\Adapter\Address\Repository;
use Address;
use PrestaShop\PrestaShop\Core\Domain\Address\Exception\AddressNotFoundException;
use PrestaShop\PrestaShop\Core\Domain\Address\ValueObject\AddressId;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Attribute\Exception\AttributeNotFoundException;
use PrestaShop\PrestaShop\Core\Exception\CoreException;
use PrestaShop\PrestaShop\Core\Repository\AbstractMultiShopObjectModelRepository;
/**
* Provides access to address data source
*/
class AddressRepository extends AbstractMultiShopObjectModelRepository
{
/**
* @param AddressId $addressId
*
* @return Address
*
* @throws AttributeNotFoundException
* @throws CoreException
*/
public function get(AddressId $addressId): Address
{
/** @var Address $address */
$address = $this->getObjectModel(
$addressId->getValue(),
Address::class,
AddressNotFoundException::class
);
return $address;
}
}

View File

@@ -0,0 +1,44 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter;
use Address;
/**
* Class responsible for creating Address ObjectModel.
*/
class AddressFactory
{
/**
* Initialize an address corresponding to the specified id address or if empty to the
* default shop configuration.
*
* @param int|null $id_address
* @param bool $with_geoloc
*
* @return Address
*/
public function findOrCreate($id_address = null, $with_geoloc = false)
{
$func_args = func_get_args();
return call_user_func_array(['\\Address', 'initialize'], $func_args);
}
/**
* Check if an address exists depending on given $id_address.
*
* @param int $id_address
* @param bool $useCache [default=false] If true, use Cache for optimizing queries
*
* @return bool
*/
public function addressExists($id_address, bool $useCache = false)
{
return Address::addressExists($id_address, $useCache);
}
}

View File

@@ -0,0 +1,87 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\Admin;
use PrestaShop\PrestaShop\Adapter\Configuration;
use PrestaShop\PrestaShop\Core\Configuration\DataConfigurationInterface;
/**
* Manages the configuration data about image.
*/
class ImageConfiguration implements DataConfigurationInterface
{
/**
* @var Configuration
*/
private $configuration;
public function __construct(Configuration $configuration)
{
$this->configuration = $configuration;
}
/**
* {@inheritdoc}
*/
public function getConfiguration()
{
return [
'formats' => $this->configuration->get('PS_IMAGE_FORMAT'),
'base-format' => $this->configuration->get('PS_IMAGE_QUALITY'),
'avif-quality' => (int) $this->configuration->get('PS_AVIF_QUALITY'),
'jpeg-quality' => (int) $this->configuration->get('PS_JPEG_QUALITY'),
'png-quality' => (int) $this->configuration->get('PS_PNG_QUALITY'),
'webp-quality' => (int) $this->configuration->get('PS_WEBP_QUALITY'),
'generation-method' => (int) $this->configuration->get('PS_IMAGE_GENERATION_METHOD'),
'picture-max-size' => (int) $this->configuration->get('PS_PRODUCT_PICTURE_MAX_SIZE'),
'picture-max-width' => (int) $this->configuration->get('PS_PRODUCT_PICTURE_WIDTH'),
'picture-max-height' => (int) $this->configuration->get('PS_PRODUCT_PICTURE_HEIGHT'),
];
}
/**
* {@inheritdoc}
*/
public function updateConfiguration(array $configuration)
{
$errors = [];
if ($this->validateConfiguration($configuration)) {
$this->configuration->set('PS_IMAGE_FORMAT', $configuration['formats']);
$this->configuration->set('PS_IMAGE_QUALITY', $configuration['base-format']);
$this->configuration->set('PS_AVIF_QUALITY', (int) $configuration['avif-quality']);
$this->configuration->set('PS_JPEG_QUALITY', (int) $configuration['jpeg-quality']);
$this->configuration->set('PS_PNG_QUALITY', (int) $configuration['png-quality']);
$this->configuration->set('PS_WEBP_QUALITY', (int) $configuration['webp-quality']);
$this->configuration->set('PS_IMAGE_GENERATION_METHOD', (int) $configuration['generation-method']);
$this->configuration->set('PS_PRODUCT_PICTURE_MAX_SIZE', (int) $configuration['picture-max-size']);
$this->configuration->set('PS_PRODUCT_PICTURE_WIDTH', (int) $configuration['picture-max-width']);
$this->configuration->set('PS_PRODUCT_PICTURE_HEIGHT', (int) $configuration['picture-max-height']);
}
return $errors;
}
/**
* {@inheritdoc}
*/
public function validateConfiguration(array $configuration)
{
return isset(
$configuration['formats'],
$configuration['base-format'],
$configuration['avif-quality'],
$configuration['jpeg-quality'],
$configuration['png-quality'],
$configuration['webp-quality'],
$configuration['generation-method'],
$configuration['picture-max-size'],
$configuration['picture-max-width'],
$configuration['picture-max-height'],
);
}
}

View File

@@ -0,0 +1,49 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\Admin;
use Exception;
use PrestaShopBundle\Service\Hook\RenderingHookEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Adds listeners to renderhook Twig function, to let adding legacy helpers like Kpi, etc...
*/
class LegacyBlockHelperSubscriber implements EventSubscriberInterface
{
/**
* Returns an array of event names this subscriber wants to listen to.
*
* @return array The listeners array
*/
public static function getSubscribedEvents()
{
return [
'legacy_block_kpi' => ['renderKpi', 0],
];
}
/**
* Renders a Kpi block for a given legacy controller name.
*
* @param RenderingHookEvent $event
*
* @throws Exception
*/
public function renderKpi(RenderingHookEvent $event)
{
if (!array_key_exists('kpi_controller', $event->getHookParameters())) {
throw new Exception('The legacy_kpi hook need a kpi_controller parameter (legacy controller full class name).');
}
$controller = $event->getHookParameters()['kpi_controller'];
$controller = new $controller('new-theme');
$renderKpis = $controller->renderKpis() !== null ? $controller->renderKpis() : [];
$event->setContent($renderKpis);
}
}

View File

@@ -0,0 +1,66 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\Admin;
use PrestaShop\PrestaShop\Adapter\Configuration;
use PrestaShop\PrestaShop\Core\Configuration\DataConfigurationInterface;
/**
* Manages the configuration data about notifications options.
*/
class NotificationsConfiguration implements DataConfigurationInterface
{
/**
* @var Configuration
*/
private $configuration;
public function __construct(Configuration $configuration)
{
$this->configuration = $configuration;
}
/**
* {@inheritdoc}
*/
public function getConfiguration()
{
return [
'show_notifs_new_orders' => $this->configuration->getBoolean('PS_SHOW_NEW_ORDERS'),
'show_notifs_new_customers' => $this->configuration->getBoolean('PS_SHOW_NEW_CUSTOMERS'),
'show_notifs_new_messages' => $this->configuration->getBoolean('PS_SHOW_NEW_MESSAGES'),
];
}
/**
* {@inheritdoc}
*/
public function updateConfiguration(array $configuration)
{
$errors = [];
if ($this->validateConfiguration($configuration)) {
$this->configuration->set('PS_SHOW_NEW_ORDERS', (bool) $configuration['show_notifs_new_orders']);
$this->configuration->set('PS_SHOW_NEW_CUSTOMERS', (bool) $configuration['show_notifs_new_customers']);
$this->configuration->set('PS_SHOW_NEW_MESSAGES', (bool) $configuration['show_notifs_new_messages']);
}
return $errors;
}
/**
* {@inheritdoc}
*/
public function validateConfiguration(array $configuration)
{
return isset(
$configuration['show_notifs_new_orders'],
$configuration['show_notifs_new_customers'],
$configuration['show_notifs_new_messages']
);
}
}

View File

@@ -0,0 +1,114 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\Admin;
use PrestaShop\PrestaShop\Adapter\LegacyContext;
use ReflectionClass;
use Symfony\Component\Process\Exception\LogicException;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Routing\RequestContext;
use Symfony\Component\Routing\Router;
/**
* This UrlGeneratorInterface implementation (in a Sf service) will provides Legacy URLs.
*
* To be used by Symfony controllers, to generate a link to a Legacy page.
* Call an instance of it through the Symfony container:
* $container->get('prestashop.core.admin.url_generator_legacy');
* Or via the UrlGeneratorFactory (as Sf service):
* $container->get('prestashop.core.admin.url_generator_factory')->forLegacy();
*/
class UrlGenerator implements UrlGeneratorInterface
{
/**
* @var LegacyContext
*/
private $legacyContext;
/**
* @var Router
*/
private $router;
/**
* Constructor.
*
* @param LegacyContext $legacyContext
* @param Router $router
*/
public function __construct(LegacyContext $legacyContext, Router $router)
{
$this->legacyContext = $legacyContext;
$this->router = $router;
}
/**
* @param string $name
* @param array $parameters
* @param int $referenceType
*
* @return string
*/
public function generate($name, $parameters = [], $referenceType = self::ABSOLUTE_PATH): string
{
// By default, consider given parameters in legacy format (no mapping if route not found).
$legacyController = $name;
$legacyParameters = $parameters;
// resolve route & legacy mapping
[$legacyController, $legacyParameters] = $this->getLegacyOptions($name, $parameters);
return $this->legacyContext->getAdminLink($legacyController, true, $legacyParameters);
}
/**
* Try to get controller & parameters with mapping options.
*
* If failed to find options, then return the input values.
*
* @param string $routeName
* @param string[] $parameters The route parameters to convert
*
* @return array{0: string, 1: array<string>} An array with: the legacy controller name, then the parameters array
*/
final public function getLegacyOptions($routeName, $parameters = [])
{
$legacyController = $routeName;
$legacyParameters = $parameters;
$route = $this->router->getRouteCollection()->get($routeName);
if ($route) {
if ($route->hasDefault('_legacy_controller')) {
$legacyController = $route->getDefault('_legacy_controller');
if ($route->hasDefault('_legacy_param_mapper_class') && $route->hasDefault('_legacy_param_mapper_method')) {
$class = $route->getDefault('_legacy_param_mapper_class');
$method = $route->getDefault('_legacy_param_mapper_method');
$method = (new ReflectionClass('\\' . $class))->getMethod($method);
$legacyParameters = $method->invoke(($method->isStatic()) ? null : $method->getDeclaringClass()->newInstance(), $parameters);
}
}
}
return [$legacyController, $legacyParameters];
}
/**
* {@inheritdoc}
*/
public function setContext(RequestContext $context)
{
throw new LogicException('Cannot use this UrlGeneratorInterface implementation with a Symfony context. Please call AdminUrlGeneratorFactory::forLegacy() to reach the right instance.');
}
/**
* {@inheritdoc}
*/
public function getContext(): RequestContext
{
throw new LogicException('Cannot use this UrlGeneratorInterface implementation with a Symfony context. Please call AdminUrlGeneratorFactory::forLegacy() to reach the right instance.');
}
}

View File

@@ -0,0 +1,67 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
declare(strict_types=1);
namespace PrestaShop\PrestaShop\Adapter\AdminAPI;
use PrestaShop\PrestaShop\Adapter\Cache\Clearer\SymfonyCacheClearer;
use PrestaShop\PrestaShop\Adapter\Configuration;
use PrestaShop\PrestaShop\Core\Configuration\DataConfigurationInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class AdminAPIConfiguration implements DataConfigurationInterface
{
public function __construct(
private readonly Configuration $configuration,
private readonly TranslatorInterface $translator,
private readonly SymfonyCacheClearer $cacheClearer,
) {
}
public function getConfiguration()
{
return [
'enable_admin_api' => $this->configuration->getBoolean('PS_ENABLE_ADMIN_API'),
'force_debug_secured' => $this->configuration->getBoolean('PS_ADMIN_API_FORCE_DEBUG_SECURED'),
];
}
public function updateConfiguration(array $configuration)
{
$errors = $this->getConfigurationErrors($configuration);
if (!empty($errors)) {
return $errors;
}
$this->configuration->set('PS_ENABLE_ADMIN_API', $configuration['enable_admin_api']);
$this->configuration->set('PS_ADMIN_API_FORCE_DEBUG_SECURED', $configuration['force_debug_secured']);
// Clear cache so that Swagger and roles extraction are refreshed
$this->cacheClearer->clear();
return [];
}
public function validateConfiguration(array $configuration)
{
return empty($this->getConfigurationErrors($configuration));
}
private function getConfigurationErrors(array $configuration): array
{
$errors = [];
if (!is_bool($configuration['enable_admin_api'])) {
$errors[] = $this->translator->trans(
'The %s field is invalid.',
[$this->translator->trans('Admin API', [], 'Admin.Advparameters.Feature')],
'Admin.Notifications.Error'
);
}
return $errors;
}
}

View File

@@ -0,0 +1,43 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
declare(strict_types=1);
namespace PrestaShop\PrestaShop\Adapter\Alias\CommandHandler;
use PrestaShop\PrestaShop\Adapter\Alias\Repository\AliasRepository;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\Alias\Command\AddSearchTermAliasesCommand;
use PrestaShop\PrestaShop\Core\Domain\Alias\CommandHandler\AddSearchTermAliasesHandlerInterface;
#[AsCommandHandler]
class AddSearchTermAliasesHandler implements AddSearchTermAliasesHandlerInterface
{
/**
* @var AliasRepository
*/
private $aliasRepository;
/**
* @param AliasRepository $aliasRepository
*/
public function __construct(
AliasRepository $aliasRepository
) {
$this->aliasRepository = $aliasRepository;
}
/**
* {@inheritdoc}
*/
public function handle(AddSearchTermAliasesCommand $command): array
{
return $this->aliasRepository->addAliases(
$command->getSearchTerm(),
$command->getAliases()
);
}
}

View File

@@ -0,0 +1,68 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
declare(strict_types=1);
namespace PrestaShop\PrestaShop\Adapter\Alias\CommandHandler;
use PrestaShop\PrestaShop\Adapter\Alias\Repository\AliasRepository;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\AbstractBulkCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\Alias\Command\BulkDeleteSearchTermsAliasesCommand;
use PrestaShop\PrestaShop\Core\Domain\Alias\CommandHandler\BulkDeleteSearchTermsAliasesHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\Alias\Exception\AliasException;
use PrestaShop\PrestaShop\Core\Domain\Alias\Exception\BulkAliasException;
use PrestaShop\PrestaShop\Core\Domain\Alias\ValueObject\SearchTerm;
use PrestaShop\PrestaShop\Core\Domain\Exception\BulkCommandExceptionInterface;
/**
* Handles command which deletes aliases related to search term in bulk action
*/
#[AsCommandHandler]
class BulkDeleteSearchTermsAliasesHandler extends AbstractBulkCommandHandler implements BulkDeleteSearchTermsAliasesHandlerInterface
{
public function __construct(protected AliasRepository $aliasRepository)
{
}
/**
* {@inheritdoc}
*/
public function handle(BulkDeleteSearchTermsAliasesCommand $command): void
{
$this->handleBulkAction($command->getSearchTerms(), AliasException::class);
}
/**
* @param SearchTerm $term
* @param mixed $command
*
* @return void
*/
protected function handleSingleAction(mixed $term, mixed $command): void
{
$this->aliasRepository->deleteAliasesBySearchTerm($term);
}
/**
* {@inheritDoc}
*/
protected function buildBulkException(array $caughtExceptions): BulkCommandExceptionInterface
{
return new BulkAliasException(
$caughtExceptions,
'Errors occurred during Alias bulk delete action',
);
}
/**
* {@inheritDoc}
*/
protected function supports(mixed $term): bool
{
return $term instanceof SearchTerm;
}
}

View File

@@ -0,0 +1,30 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
declare(strict_types=1);
namespace PrestaShop\PrestaShop\Adapter\Alias\CommandHandler;
use PrestaShop\PrestaShop\Adapter\Alias\Repository\AliasRepository;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\Alias\Command\DeleteSearchTermAliasesCommand;
use PrestaShop\PrestaShop\Core\Domain\Alias\CommandHandler\DeleteSearchTermAliasesHandlerInterface;
#[AsCommandHandler]
class DeleteSearchTermAliasesHandler implements DeleteSearchTermAliasesHandlerInterface
{
public function __construct(private readonly AliasRepository $aliasRepository)
{
}
/**
* {@inheritdoc}
*/
public function handle(DeleteSearchTermAliasesCommand $command): void
{
$this->aliasRepository->deleteAliasesBySearchTerm($command->getSearchTerm());
}
}

View File

@@ -0,0 +1,29 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
declare(strict_types=1);
namespace PrestaShop\PrestaShop\Adapter\Alias\CommandHandler;
use PrestaShop\PrestaShop\Adapter\Alias\Repository\AliasRepository;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\Alias\Command\UpdateSearchTermAliasesCommand;
use PrestaShop\PrestaShop\Core\Domain\Alias\CommandHandler\UpdateSearchTermAliasesHandlerInterface;
#[AsCommandHandler]
class UpdateSearchTermAliasesHandler implements UpdateSearchTermAliasesHandlerInterface
{
public function __construct(
protected AliasRepository $aliasRepository
) {
}
public function handle(UpdateSearchTermAliasesCommand $command): void
{
$this->aliasRepository->deleteAliasesBySearchTerm($command->getOldSearchTerm());
$this->aliasRepository->addAliases($command->getNewSearchTerm()->getValue(), $command->getAliases());
}
}

View File

@@ -0,0 +1,43 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
declare(strict_types=1);
namespace PrestaShop\PrestaShop\Adapter\Alias\QueryHandler;
use PrestaShop\PrestaShop\Adapter\Alias\Repository\AliasRepository;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsQueryHandler;
use PrestaShop\PrestaShop\Core\Domain\Alias\Query\GetAliasForEditing;
use PrestaShop\PrestaShop\Core\Domain\Alias\QueryHandler\GetAliasForEditingHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\Alias\QueryResult\AliasForEditing;
/**
* Handles the query @see GetAliasForEditing using legacy ObjectModel
*/
#[AsQueryHandler]
class GetAliasForEditingHandler implements GetAliasForEditingHandlerInterface
{
/**
* @var AliasRepository
*/
private $aliasRepository;
public function __construct(
AliasRepository $aliasRepository
) {
$this->aliasRepository = $aliasRepository;
}
public function handle(GetAliasForEditing $query): AliasForEditing
{
$searchTerm = $this->aliasRepository->get($query->getAliasId())->search;
return new AliasForEditing(
$this->aliasRepository->getAliasesBySearchTerm($searchTerm),
$searchTerm
);
}
}

View File

@@ -0,0 +1,32 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\Alias\QueryHandler;
use PrestaShop\PrestaShop\Adapter\Alias\Repository\AliasRepository;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsQueryHandler;
use PrestaShop\PrestaShop\Core\Domain\Alias\Query\GetAliasesBySearchTermForEditing;
use PrestaShop\PrestaShop\Core\Domain\Alias\QueryHandler\GetAliasesBySearchTermForEditingHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\Alias\QueryResult\AliasForEditing;
/**
* Handle the query @see GetAliasesBySearchTermForEditing using legacy ObjectModel
*/
#[AsQueryHandler]
class GetAliasesBySearchTermForEditingHandler implements GetAliasesBySearchTermForEditingHandlerInterface
{
public function __construct(
private readonly AliasRepository $aliasRepository
) {
}
public function handle(GetAliasesBySearchTermForEditing $query): AliasForEditing
{
$aliases = $this->aliasRepository->getAliasesBySearchTerm($query->getSearchTerm());
return new AliasForEditing($aliases, $query->getSearchTerm());
}
}

View File

@@ -0,0 +1,35 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
declare(strict_types=1);
namespace PrestaShop\PrestaShop\Adapter\Alias\QueryHandler;
use PrestaShop\PrestaShop\Adapter\Alias\Repository\AliasRepository;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsQueryHandler;
use PrestaShop\PrestaShop\Core\Domain\Alias\Query\SearchForSearchTerm;
use PrestaShop\PrestaShop\Core\Domain\Alias\QueryHandler\SearchForSearchTermHandlerInterface;
#[AsQueryHandler]
class SearchForSearchTermHandler implements SearchForSearchTermHandlerInterface
{
public function __construct(protected readonly AliasRepository $aliasRepository)
{
}
/**
* @param SearchForSearchTerm $query
*
* @return string[]
*/
public function handle(SearchForSearchTerm $query): array
{
return array_column(
$this->aliasRepository->searchSearchTerms($query->getSearchTerm(), $query->getLimit()),
'search'
);
}
}

View File

@@ -0,0 +1,283 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
declare(strict_types=1);
namespace PrestaShop\PrestaShop\Adapter\Alias\Repository;
use Alias;
use Doctrine\DBAL\ArrayParameterType;
use Doctrine\DBAL\Connection;
use PrestaShop\PrestaShop\Adapter\Alias\Validate\AliasValidator;
use PrestaShop\PrestaShop\Core\Domain\Alias\Exception\AliasConstraintException;
use PrestaShop\PrestaShop\Core\Domain\Alias\Exception\AliasNotFoundException;
use PrestaShop\PrestaShop\Core\Domain\Alias\Exception\BulkAliasException;
use PrestaShop\PrestaShop\Core\Domain\Alias\Exception\CannotAddAliasException;
use PrestaShop\PrestaShop\Core\Domain\Alias\Exception\CannotDeleteAliasException;
use PrestaShop\PrestaShop\Core\Domain\Alias\ValueObject\AliasId;
use PrestaShop\PrestaShop\Core\Domain\Alias\ValueObject\SearchTerm;
use PrestaShop\PrestaShop\Core\Domain\Feature\Exception\BulkFeatureException;
use PrestaShop\PrestaShop\Core\Exception\CoreException;
use PrestaShop\PrestaShop\Core\Repository\AbstractObjectModelRepository;
class AliasRepository extends AbstractObjectModelRepository
{
/**
* @param Connection $connection
* @param string $dbPrefix
* @param AliasValidator $aliasValidator
*/
public function __construct(
protected Connection $connection,
protected string $dbPrefix,
protected AliasValidator $aliasValidator
) {
}
/**
* Creates new Alias entity and saves to the database
*
* @param string $searchTerm
* @param array{
* array{
* alias: string,
* active: bool,
* }
* } $aliases
*
* @return AliasId[]
*
* @throws CoreException
*/
public function addAliases(string $searchTerm, array $aliases): array
{
// Get all aliases already in use for other search terms
$aliasesToAdd = array_column($aliases, 'alias');
$aliasesAlreadyUsed = $this->getAliasesAlreadyInUseNotForSearchTerm($searchTerm, $aliasesToAdd);
// If we have aliases already in use, we need to throw an exception
if (count($aliasesAlreadyUsed) > 0) {
throw new AliasConstraintException(
implode(', ', $aliasesAlreadyUsed),
AliasConstraintException::ALIAS_ALREADY_USED
);
}
$aliasIds = [];
foreach ($aliases as $searchAlias) {
// As search term is not a primary key, we need make sure that alias and search combination does not exist
// if alias exists for search term, we need to apply new active if needed
$aliasIfExists = $this->getAliasIfExists($searchAlias['alias'], $searchTerm);
if ($aliasIfExists) {
$aliasIfExists->active = $searchAlias['active'];
$this->partialUpdate($aliasIfExists, ['active'], CannotAddAliasException::class);
continue;
}
$alias = new Alias();
$alias->search = $searchTerm;
$alias->alias = $searchAlias['alias'];
$alias->active = $searchAlias['active'];
$this->aliasValidator->validate($alias);
$this->addObjectModel($alias, CannotAddAliasException::class);
$aliasIds[] = new AliasId((int) $alias->id);
}
return $aliasIds;
}
/**
* @param AliasId $aliasId
*
* @return Alias
*/
public function get(AliasId $aliasId): Alias
{
/** @var Alias $alias */
$alias = $this->getObjectModel(
$aliasId->getValue(),
Alias::class,
AliasNotFoundException::class
);
return $alias;
}
/**
* @param string $alias
* @param string $searchTerm
*
* @return Alias|null
*/
public function getAliasIfExists(string $alias, string $searchTerm): ?Alias
{
$qb = $this->connection->createQueryBuilder()
->select('a.id_alias')
->from($this->dbPrefix . 'alias', 'a')
->where('a.search = :search')
->andWhere('a.alias = :alias')
->setParameter('search', $searchTerm)
->setParameter('alias', $alias)
;
$alias = $qb->executeQuery()->fetchOne();
if ($alias) {
return $this->get(new AliasId((int) $alias));
}
return null;
}
/**
* @param string $searchTerm
*
* @return array{
* array{
* alias: string,
* active: bool,
* }
* }
*/
public function getAliasesBySearchTerm(string $searchTerm): array
{
$qb = $this->connection->createQueryBuilder()
->addSelect('a.alias, a.active')
->from($this->dbPrefix . 'alias', 'a')
->where('a.search = :search')
->setParameter('search', $searchTerm)
->addOrderBy('a.alias', 'ASC')
;
return $qb->executeQuery()->fetchAllAssociative();
}
public function delete(AliasId $aliasId): void
{
$this->deleteObjectModel($this->get($aliasId), CannotDeleteAliasException::class);
}
/**
* @param Alias $alias
* @param string[] $propertiesToUpdate
* @param string $exceptionClass
*
* @return void
*/
public function partialUpdate(Alias $alias, array $propertiesToUpdate, string $exceptionClass): void
{
$this->aliasValidator->validate($alias);
$this->partiallyUpdateObjectModel($alias, $propertiesToUpdate, $exceptionClass);
}
/**
* Deletes all related aliases
*
* @param SearchTerm $searchTerm
*/
public function deleteAliasesBySearchTerm(SearchTerm $searchTerm): void
{
$exceptions = [];
$aliasIds = $this->connection->createQueryBuilder()
->addSelect('a.id_alias')
->from($this->dbPrefix . 'alias', 'a')
->where('a.search = :searchTerm')
->setParameter('searchTerm', $searchTerm->getValue())
->executeQuery()
->fetchFirstColumn()
;
if (empty($aliasIds)) {
return;
}
foreach ($aliasIds as $currentAliasId) {
try {
$this->deleteObjectModel($this->get(new AliasId((int) $currentAliasId)), CannotDeleteAliasException::class);
} catch (CannotDeleteAliasException $e) {
$exceptions[] = $e;
}
}
if (!empty($exceptions)) {
throw new BulkAliasException(
$exceptions,
'Errors occurred during Alias bulk delete action',
BulkFeatureException::FAILED_BULK_DELETE
);
}
}
/**
* @param string $searchPhrase
* @param int|null $limit
*
* @return array<int, array<string, string>>
*/
public function searchSearchTerms(string $searchPhrase, ?int $limit = null): array
{
return $this->connection->createQueryBuilder()
->addSelect('a.search')
->from($this->dbPrefix . 'alias', 'a')
->addOrderBy('a.search', 'ASC')
->addGroupBy('a.search')
->setMaxResults($limit)
->where('a.search LIKE :searchPhrase')
->setParameter('searchPhrase', '%' . $searchPhrase . '%')
->executeQuery()
->fetchAllAssociative();
}
/**
* @param array $searchTerms
*
* @return array{
* array{
* id_alias: int,
* search: string,
* alias: string,
* active: bool,
* }
* }
*/
public function getAliasesBySearchTerms(array $searchTerms): array
{
$qb = $this->connection->createQueryBuilder()
->addSelect('a.id_alias, a.search, a.alias, a.active')
->from($this->dbPrefix . 'alias', 'a')
->where('a.search IN (:searchTerms)')
->setParameter('searchTerms', $searchTerms, ArrayParameterType::STRING)
->addOrderBy('a.search', 'ASC')
->addOrderBy('a.alias', 'ASC')
;
return $qb->executeQuery()->fetchAllAssociative();
}
/**
* @param string $searchTerm
* @param string[] $aliases
*
* @return string[]
*/
private function getAliasesAlreadyInUseNotForSearchTerm(string $searchTerm, array $aliases): array
{
$qb = $this->connection->createQueryBuilder()
->addSelect('DISTINCT a.alias')
->from($this->dbPrefix . 'alias', 'a')
->addOrderBy('a.alias', 'ASC')
->where('a.search != :searchTerm')
->andWhere('a.alias IN (:aliases)')
->setParameter('searchTerm', $searchTerm)
->setParameter('aliases', $aliases, ArrayParameterType::STRING)
;
return $qb->executeQuery()->fetchFirstColumn();
}
}

View File

@@ -0,0 +1,51 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
declare(strict_types=1);
namespace PrestaShop\PrestaShop\Adapter\Alias\Validate;
use Alias;
use PrestaShop\PrestaShop\Adapter\AbstractObjectModelValidator;
use PrestaShop\PrestaShop\Core\Domain\Alias\Exception\AliasConstraintException;
use PrestaShop\PrestaShop\Core\Exception\CoreException;
/**
* Validates alias field using legacy object model
*/
class AliasValidator extends AbstractObjectModelValidator
{
/**
* This method is specific for alias creation only.
*
* @param Alias $alias
*
* @throws CoreException
*/
public function validate(Alias $alias): void
{
$this->validateAliasProperty($alias, 'search', AliasConstraintException::INVALID_SEARCH);
$this->validateAliasProperty($alias, 'alias', AliasConstraintException::INVALID_ALIAS);
$this->validateAliasProperty($alias, 'active', AliasConstraintException::INVALID_VISIBILITY);
}
/**
* @param Alias $alias
* @param string $propertyName
* @param int $errorCode
*
* @throws AliasConstraintException
*/
private function validateAliasProperty(Alias $alias, string $propertyName, int $errorCode = 0): void
{
$this->validateObjectModelProperty(
$alias,
$propertyName,
AliasConstraintException::class,
$errorCode
);
}
}

View File

@@ -0,0 +1,65 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
declare(strict_types=1);
namespace PrestaShop\PrestaShop\Adapter\ApiClient\CommandHandler;
use Doctrine\ORM\Exception\ORMException;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\ApiClient\Command\AddApiClientCommand;
use PrestaShop\PrestaShop\Core\Domain\ApiClient\CommandHandler\AddApiClientCommandHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\ApiClient\Exception\ApiClientConstraintException;
use PrestaShop\PrestaShop\Core\Domain\ApiClient\Exception\CannotAddApiClientException;
use PrestaShop\PrestaShop\Core\Domain\ApiClient\ValueObject\CreatedApiClient;
use PrestaShop\PrestaShop\Core\Util\String\RandomString;
use PrestaShopBundle\Entity\ApiClient;
use PrestaShopBundle\Entity\Repository\ApiClientRepository;
use Symfony\Component\PasswordHasher\PasswordHasherInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
#[AsCommandHandler]
class AddApiClientHandler implements AddApiClientCommandHandlerInterface
{
public function __construct(
private readonly ApiClientRepository $repository,
private readonly ValidatorInterface $validator,
private readonly PasswordHasherInterface $passwordHasher
) {
}
public function handle(AddApiClientCommand $command): CreatedApiClient
{
$apiClient = new ApiClient();
$apiClient->setClientId($command->getClientId());
$apiClient->setClientName($command->getClientName());
$secret = RandomString::generate();
$apiClient->setClientSecret($this->passwordHasher->hash($secret));
$apiClient->setEnabled($command->isEnabled());
$apiClient->setDescription($command->getDescription());
$apiClient->setScopes($command->getScopes());
$apiClient->setLifetime($command->getLifetime());
$errors = $this->validator->validate($apiClient);
if (count($errors) > 0) {
throw ApiClientConstraintException::buildFromPropertyPath(
$errors->get(0)->getPropertyPath(),
$errors->get(0)->getMessage(),
$errors->get(0)->getMessageTemplate()
);
}
try {
$apiClientId = $this->repository->save($apiClient);
} catch (ORMException $e) {
throw new CannotAddApiClientException('Could not add Api client', 0, $e);
}
return new CreatedApiClient($apiClientId, $secret);
}
}

View File

@@ -0,0 +1,42 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
declare(strict_types=1);
namespace PrestaShop\PrestaShop\Adapter\ApiClient\CommandHandler;
use Doctrine\ORM\Exception\ORMException;
use Doctrine\ORM\NoResultException;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\ApiClient\Command\DeleteApiClientCommand;
use PrestaShop\PrestaShop\Core\Domain\ApiClient\CommandHandler\DeleteApiClientHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\ApiClient\Exception\ApiClientNotFoundException;
use PrestaShop\PrestaShop\Core\Domain\ApiClient\Exception\CannotDeleteApiClientException;
use PrestaShopBundle\Entity\Repository\ApiClientRepository;
#[AsCommandHandler]
class DeleteApiClientHandler implements DeleteApiClientHandlerInterface
{
public function __construct(
private readonly ApiClientRepository $repository,
) {
}
public function handle(DeleteApiClientCommand $command): void
{
try {
$apiClient = $this->repository->getById($command->getApiClientId()->getValue());
} catch (NoResultException $e) {
throw new ApiClientNotFoundException(sprintf('Could not find Api client with ID %s', $command->getApiClientId()->getValue()), 0, $e);
}
try {
$this->repository->delete($apiClient);
} catch (ORMException $e) {
throw new CannotDeleteApiClientException('Could not delete Api client', 0, $e);
}
}
}

View File

@@ -0,0 +1,80 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
declare(strict_types=1);
namespace PrestaShop\PrestaShop\Adapter\ApiClient\CommandHandler;
use Doctrine\ORM\Exception\ORMException;
use Doctrine\ORM\NoResultException;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\ApiClient\Command\EditApiClientCommand;
use PrestaShop\PrestaShop\Core\Domain\ApiClient\CommandHandler\EditApiClientCommandHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\ApiClient\Exception\ApiClientConstraintException;
use PrestaShop\PrestaShop\Core\Domain\ApiClient\Exception\ApiClientNotFoundException;
use PrestaShop\PrestaShop\Core\Domain\ApiClient\Exception\CannotUpdateApiClientException;
use PrestaShopBundle\Entity\Repository\ApiClientRepository;
use Symfony\Component\Validator\Validator\ValidatorInterface;
#[AsCommandHandler]
class EditApiClientHandler implements EditApiClientCommandHandlerInterface
{
public function __construct(
private readonly ApiClientRepository $repository,
private readonly ValidatorInterface $validator
) {
}
public function handle(EditApiClientCommand $command): void
{
try {
$apiClient = $this->repository->getById($command->getApiClientId()->getValue());
} catch (NoResultException $e) {
throw new ApiClientNotFoundException(sprintf('Could not find Api client %s', $command->getClientId()), 0, $e);
}
if (!is_null($command->getClientId())) {
$apiClient->setClientId($command->getClientId());
}
if (!is_null($command->getClientName())) {
$apiClient->setClientName($command->getClientName());
}
if (!is_null($command->isEnabled())) {
$apiClient->setEnabled($command->isEnabled());
}
if (!is_null($command->getDescription())) {
$apiClient->setDescription($command->getDescription());
}
if (!is_null($command->getScopes())) {
$apiClient->setScopes($command->getScopes());
}
if (!is_null($command->getLifetime())) {
$apiClient->setLifetime($command->getLifetime());
}
$errors = $this->validator->validate($apiClient);
if (count($errors) > 0) {
throw ApiClientConstraintException::buildFromPropertyPath(
$errors->get(0)->getPropertyPath(),
$errors->get(0)->getMessage(),
$errors->get(0)->getMessageTemplate()
);
}
try {
$this->repository->save($apiClient);
} catch (ORMException $e) {
throw new CannotUpdateApiClientException('Could not update Api client', 0, $e);
}
}
}

View File

@@ -0,0 +1,43 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\ApiClient\CommandHandler;
use Doctrine\ORM\Exception\ORMException;
use Doctrine\ORM\NoResultException;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\ApiClient\Command\ForceApiClientSecretCommand;
use PrestaShop\PrestaShop\Core\Domain\ApiClient\CommandHandler\ForceApiClientSecretHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\ApiClient\Exception\ApiClientNotFoundException;
use PrestaShop\PrestaShop\Core\Domain\ApiClient\Exception\CannotGenerateApiClientSecretException;
use PrestaShopBundle\Entity\Repository\ApiClientRepository;
use Symfony\Component\PasswordHasher\PasswordHasherInterface;
#[AsCommandHandler]
class ForceApiClientSecretHandler implements ForceApiClientSecretHandlerInterface
{
public function __construct(
private readonly ApiClientRepository $repository,
private readonly PasswordHasherInterface $passwordHasher
) {
}
public function handle(ForceApiClientSecretCommand $command): void
{
try {
$apiClient = $this->repository->getById($command->getApiClientId()->getValue());
} catch (NoResultException $e) {
throw new ApiClientNotFoundException(sprintf('Could not find Api client with ID %s', $command->getApiClientId()->getValue()), 0, $e);
}
try {
$apiClient->setClientSecret($this->passwordHasher->hash($command->getSecret()->getValue()));
$this->repository->save($apiClient);
} catch (ORMException $e) {
throw new CannotGenerateApiClientSecretException('Could not generate new token Api client', 0, $e);
}
}
}

View File

@@ -0,0 +1,49 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
declare(strict_types=1);
namespace PrestaShop\PrestaShop\Adapter\ApiClient\CommandHandler;
use Doctrine\ORM\Exception\ORMException;
use Doctrine\ORM\NoResultException;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\ApiClient\Command\GenerateApiClientSecretCommand;
use PrestaShop\PrestaShop\Core\Domain\ApiClient\CommandHandler\GenerateApiClientSecretHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\ApiClient\Exception\ApiClientNotFoundException;
use PrestaShop\PrestaShop\Core\Domain\ApiClient\Exception\CannotGenerateApiClientSecretException;
use PrestaShop\PrestaShop\Core\Util\String\RandomString;
use PrestaShopBundle\Entity\Repository\ApiClientRepository;
use Symfony\Component\PasswordHasher\PasswordHasherInterface;
#[AsCommandHandler]
class GenerateApiClientSecretHandler implements GenerateApiClientSecretHandlerInterface
{
public function __construct(
private readonly ApiClientRepository $repository,
private readonly PasswordHasherInterface $passwordHasher
) {
}
public function handle(GenerateApiClientSecretCommand $command): string
{
try {
$apiClient = $this->repository->getById($command->getApiClientId()->getValue());
} catch (NoResultException $e) {
throw new ApiClientNotFoundException(sprintf('Could not find Api client with ID %s', $command->getApiClientId()->getValue()), 0, $e);
}
try {
$secret = RandomString::generate();
$apiClient->setClientSecret($this->passwordHasher->hash($secret));
$this->repository->save($apiClient);
return $secret;
} catch (ORMException $e) {
throw new CannotGenerateApiClientSecretException('Could not generate new token Api client', 0, $e);
}
}
}

View File

@@ -0,0 +1,46 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
declare(strict_types=1);
namespace PrestaShop\PrestaShop\Adapter\ApiClient\QueryHandler;
use Doctrine\ORM\NoResultException;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsQueryHandler;
use PrestaShop\PrestaShop\Core\Domain\ApiClient\Exception\ApiClientNotFoundException;
use PrestaShop\PrestaShop\Core\Domain\ApiClient\Query\GetApiClientForEditing;
use PrestaShop\PrestaShop\Core\Domain\ApiClient\QueryHandler\GetApiClientForEditingHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\ApiClient\QueryResult\EditableApiClient;
use PrestaShopBundle\Entity\Repository\ApiClientRepository;
#[AsQueryHandler]
class GetApiClientForEditingHandler implements GetApiClientForEditingHandlerInterface
{
public function __construct(private readonly ApiClientRepository $repository)
{
}
public function handle(GetApiClientForEditing $query): EditableApiClient
{
try {
$apiClient = $this->repository->getById($query->getApiClientId()->getValue());
} catch (NoResultException $e) {
throw new ApiClientNotFoundException(sprintf('Could not find Api client %s', $query->getApiClientId()->getValue()), 0, $e);
}
return new EditableApiClient(
$apiClient->getId(),
$apiClient->getClientId(),
$apiClient->getClientName(),
$apiClient->isEnabled(),
$apiClient->getDescription(),
$apiClient->getScopes(),
$apiClient->getLifetime(),
$apiClient->getExternalIssuer(),
);
}
}

View File

@@ -0,0 +1,57 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\Assets;
use Tools as ToolsLegacy;
trait AssetUrlGeneratorTrait
{
/**
* @var string
*/
protected $fqdn;
/**
* @param string $fullPath
*
* @return string
*/
protected function getUriFromPath($fullPath)
{
return str_replace($this->configuration->get('_PS_ROOT_DIR_'), rtrim($this->configuration->get('__PS_BASE_URI__'), '/'), $fullPath);
}
/**
* @param string $fullUri
*
* @return string
*/
protected function getPathFromUri($fullUri)
{
if ('' !== ($trimmedUri = rtrim($this->configuration->get('__PS_BASE_URI__'), '/'))) {
return $this->configuration->get('_PS_ROOT_DIR_') . preg_replace('#' . preg_quote($trimmedUri) . '#', '', $fullUri, 1);
}
return $this->configuration->get('_PS_ROOT_DIR_') . $fullUri;
}
/**
* @return string
*/
protected function getFQDN()
{
if (null === $this->fqdn) {
if ($this->configuration->get('PS_SSL_ENABLED') && ToolsLegacy::usingSecureMode()) {
$this->fqdn = $this->configuration->get('_PS_BASE_URL_SSL_');
} else {
$this->fqdn = $this->configuration->get('_PS_BASE_URL_');
}
}
return $this->fqdn;
}
}

View File

@@ -0,0 +1,130 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\Attachment;
use Attachment;
use PrestaShop\PrestaShop\Core\ConstraintValidator\Constraints\CleanHtml;
use PrestaShop\PrestaShop\Core\ConstraintValidator\Constraints\DefaultLanguage;
use PrestaShop\PrestaShop\Core\Domain\Attachment\Exception\AttachmentConstraintException;
use PrestaShop\PrestaShop\Core\Domain\Attachment\Exception\AttachmentNotFoundException;
use PrestaShop\PrestaShop\Core\Domain\Attachment\Exception\DeleteAttachmentException;
use PrestaShop\PrestaShop\Core\Domain\Attachment\ValueObject\AttachmentId;
use PrestaShopException;
use Symfony\Component\Validator\Validator\ValidatorInterface;
/**
* Abstract attachment handler
*/
abstract class AbstractAttachmentHandler
{
/**
* @var ValidatorInterface
*/
private $validator;
/**
* @param ValidatorInterface $validator
*/
public function __construct(ValidatorInterface $validator)
{
$this->validator = $validator;
}
/**
* @param array $localizedTexts
*
* @throws AttachmentConstraintException
*/
protected function assertHasDefaultLanguage(array $localizedTexts)
{
$errors = $this->validator->validate($localizedTexts, new DefaultLanguage());
if (0 !== count($errors)) {
throw new AttachmentConstraintException('Missing name in default language', AttachmentConstraintException::MISSING_NAME_IN_DEFAULT_LANGUAGE);
}
}
/**
* @param array $localizedDescription
*
* @throws AttachmentConstraintException
*/
protected function assertDescriptionContainsCleanHtml(array $localizedDescription)
{
foreach ($localizedDescription as $description) {
$errors = $this->validator->validate($description, new CleanHtml());
if (0 !== count($errors)) {
throw new AttachmentConstraintException(sprintf('Given description "%s" contains javascript events or script tags', $description), AttachmentConstraintException::INVALID_DESCRIPTION);
}
}
}
/**
* @return string
*/
protected function getUniqueFileName(): string
{
$uniqueFileName = sha1(uniqid());
return $uniqueFileName;
}
/**
* @param Attachment $attachment
*
* @throws AttachmentConstraintException
* @throws PrestaShopException
*/
protected function assertValidFields(Attachment $attachment)
{
if (!$attachment->validateFields(false) && !$attachment->validateFieldsLang(false)) {
throw new AttachmentConstraintException('Attachment contains invalid field values', AttachmentConstraintException::INVALID_FIELDS);
}
}
/**
* @param AttachmentId $attachmentId
*
* @return Attachment
*
* @throws AttachmentNotFoundException
*/
protected function getAttachment(AttachmentId $attachmentId): Attachment
{
$attachmentIdValue = $attachmentId->getValue();
try {
$attachment = new Attachment($attachmentIdValue);
} catch (PrestaShopException) {
throw new AttachmentNotFoundException(sprintf('Attachment with id "%s" was not found.', $attachmentId->getValue()));
}
if ($attachment->id !== $attachmentId->getValue()) {
throw new AttachmentNotFoundException(sprintf('Attachment with id "%s" was not found.', $attachmentId->getValue()));
}
return $attachment;
}
/**
* Deletes legacy Attachment
*
* @param Attachment $attachment
*
* @return bool
*
* @throws DeleteAttachmentException
*/
protected function deleteAttachment(Attachment $attachment): bool
{
try {
return $attachment->delete();
} catch (PrestaShopException) {
throw new DeleteAttachmentException(sprintf('An error occurred when deleting Attachment object with id "%s".', $attachment->id));
}
}
}

View File

@@ -0,0 +1,193 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
declare(strict_types=1);
namespace PrestaShop\PrestaShop\Adapter\Attachment;
use Attachment;
use Doctrine\DBAL\Connection;
use PrestaShop\PrestaShop\Core\Domain\Attachment\Exception\AttachmentNotFoundException;
use PrestaShop\PrestaShop\Core\Domain\Attachment\ValueObject\AttachmentId;
use PrestaShop\PrestaShop\Core\Domain\Product\ValueObject\ProductId;
use PrestaShop\PrestaShop\Core\Exception\CoreException;
use PrestaShop\PrestaShop\Core\Repository\AbstractObjectModelRepository;
/**
* Methods to access Attachment data source
*/
class AttachmentRepository extends AbstractObjectModelRepository
{
/**
* @var Connection
*/
private $connection;
/**
* @var string
*/
private $dbPrefix;
/**
* @param Connection $connection
* @param string $dbPrefix
*/
public function __construct(
Connection $connection,
string $dbPrefix
) {
$this->connection = $connection;
$this->dbPrefix = $dbPrefix;
}
/**
* @param AttachmentId $attachmentId
*
* @return Attachment
*
* @throws CoreException
* @throws AttachmentNotFoundException
*/
public function get(AttachmentId $attachmentId): Attachment
{
/** @var Attachment $attachment */
$attachment = $this->getObjectModel(
$attachmentId->getValue(),
Attachment::class,
AttachmentNotFoundException::class
);
return $attachment;
}
/**
* @param ProductId $productId
*
* @return array<int, array<string, string|array<int, string>>>
*/
public function getProductAttachments(ProductId $productId): array
{
$qb = $this->connection->createQueryBuilder();
$qb->select('a.*')
->from($this->dbPrefix . 'attachment', 'a')
->leftJoin(
'a',
$this->dbPrefix . 'product_attachment',
'pa',
'a.id_attachment = pa.id_attachment'
)
->where('pa.id_product = :productId')
->setParameter('productId', $productId->getValue())
;
$results = $qb->executeQuery()->fetchAllAssociative();
if (empty($results)) {
return [];
}
return $this->addLocalizedValues($results);
}
/**
* @param AttachmentId $attachmentId
*
* @throws CoreException
*/
public function assertAttachmentExists(AttachmentId $attachmentId): void
{
$this->assertObjectModelExists($attachmentId->getValue(), 'attachment', AttachmentNotFoundException::class);
}
public function search(string $searchPhrase): array
{
$searchPhrase = sprintf('%%%s%%', strtolower($searchPhrase));
$qb = $this->connection->createQueryBuilder();
$qb->select('a.*')
->from($this->dbPrefix . 'attachment', 'a')
->leftJoin(
'a',
$this->dbPrefix . 'product_attachment',
'pa',
'a.id_attachment = pa.id_attachment'
)
->leftJoin(
'a',
$this->dbPrefix . 'attachment_lang',
'al',
'al.id_attachment = a.id_attachment'
)
->andWhere(
$qb->expr()->or(
$qb->expr()->like('LOWER(a.file_name)', ':searchPhrase'),
$qb->expr()->like('LOWER(al.name)', ':searchPhrase'),
$qb->expr()->like('LOWER(al.description)', ':searchPhrase')
)
)
->setParameter('searchPhrase', $searchPhrase)
->addGroupBy('a.id_attachment')
;
$results = $qb->executeQuery()->fetchAllAssociative();
if (empty($results)) {
return [];
}
return $this->addLocalizedValues($results);
}
/**
* @param array $results
*
* @return array
*/
private function addLocalizedValues(array $results): array
{
$attachmentIds = array_map(function (array $result) {
return (int) $result['id_attachment'];
}, $results);
$localizedValuesByAttachmentIds = $this->getAttachmentsLocalizedValues($attachmentIds);
$fullAttachments = [];
foreach ($results as $result) {
foreach ($localizedValuesByAttachmentIds as $attachmentId => $localizedValues) {
if ($attachmentId !== (int) $result['id_attachment']) {
continue;
}
$fullAttachments[] = array_merge($result, $localizedValues);
}
}
return $fullAttachments;
}
/**
* @param int[] $attachmentIds
*
* @return array<int, array<string, array<int, string>>>
*/
private function getAttachmentsLocalizedValues(array $attachmentIds): array
{
$qb = $this->connection->createQueryBuilder();
$qb->select('al.*')
->from($this->dbPrefix . 'attachment_lang', 'al')
->where($qb->expr()->in('id_attachment', ':attachmentIds'))
->setParameter('attachmentIds', $attachmentIds, Connection::PARAM_INT_ARRAY)
;
$results = $qb->executeQuery()->fetchAllAssociative();
$localizedAttachments = [];
foreach ($results as $result) {
$localizedAttachments[(int) $result['id_attachment']]['name'][(int) $result['id_lang']] = $result['name'];
$localizedAttachments[(int) $result['id_attachment']]['description'][(int) $result['id_lang']] = $result['description'];
}
return $localizedAttachments;
}
}

View File

@@ -0,0 +1,87 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\Attachment\CommandHandler;
use Attachment;
use PrestaShop\PrestaShop\Adapter\Attachment\AbstractAttachmentHandler;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\Attachment\AttachmentFileUploaderInterface;
use PrestaShop\PrestaShop\Core\Domain\Attachment\Command\AddAttachmentCommand;
use PrestaShop\PrestaShop\Core\Domain\Attachment\CommandHandler\AddAttachmentHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\Attachment\Exception\AttachmentConstraintException;
use PrestaShop\PrestaShop\Core\Domain\Attachment\Exception\AttachmentException;
use PrestaShop\PrestaShop\Core\Domain\Attachment\Exception\AttachmentNotFoundException;
use PrestaShop\PrestaShop\Core\Domain\Attachment\Exception\CannotAddAttachmentException;
use PrestaShop\PrestaShop\Core\Domain\Attachment\Exception\EmptyFileException;
use PrestaShop\PrestaShop\Core\Domain\Attachment\ValueObject\AttachmentId;
use PrestaShopException;
use Symfony\Component\Validator\Validator\ValidatorInterface;
/**
* Handles attachment creation and file uploading procedures
*/
#[AsCommandHandler]
final class AddAttachmentHandler extends AbstractAttachmentHandler implements AddAttachmentHandlerInterface
{
/**
* @var AttachmentFileUploaderInterface
*/
protected $fileUploader;
/**
* @param ValidatorInterface $validator
* @param AttachmentFileUploaderInterface $fileUploader
*/
public function __construct(ValidatorInterface $validator, AttachmentFileUploaderInterface $fileUploader)
{
parent::__construct($validator);
$this->fileUploader = $fileUploader;
}
/**
* {@inheritdoc}
*
* @throws AttachmentConstraintException
* @throws AttachmentException
* @throws AttachmentNotFoundException
*/
public function handle(AddAttachmentCommand $command): AttachmentId
{
try {
if ($command->getFilePathName() === null) {
throw new EmptyFileException('No file found to be uploaded');
}
$attachment = new Attachment();
$this->assertDescriptionContainsCleanHtml($command->getLocalizedDescriptions());
$this->assertHasDefaultLanguage($command->getLocalizedNames());
$uniqueFileName = $this->getUniqueFileName();
$attachment->description = $command->getLocalizedDescriptions();
$attachment->name = $command->getLocalizedNames();
$attachment->file_name = $command->getOriginalName();
$attachment->file = $uniqueFileName;
$attachment->mime = $command->getMimeType();
$attachment->file_size = $command->getFileSize();
$this->assertValidFields($attachment);
$this->fileUploader->upload($command->getFilePathName(), $uniqueFileName, $command->getFileSize());
if (false === $attachment->add()) {
throw new CannotAddAttachmentException('Failed to add attachment');
}
} catch (PrestaShopException $e) {
throw new AttachmentException('An unexpected error occurred when adding attachment', 0, $e);
}
return new AttachmentId($attachment->id);
}
}

View File

@@ -0,0 +1,47 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\Attachment\CommandHandler;
use PrestaShop\PrestaShop\Adapter\Attachment\AbstractAttachmentHandler;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\Attachment\Command\BulkDeleteAttachmentsCommand;
use PrestaShop\PrestaShop\Core\Domain\Attachment\CommandHandler\BulkDeleteAttachmentsHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\Attachment\Exception\AttachmentException;
use PrestaShop\PrestaShop\Core\Domain\Attachment\Exception\BulkDeleteAttachmentsException;
/**
* Bulk delete attachments handler
*/
#[AsCommandHandler]
final class BulkDeleteAttachmentsHandler extends AbstractAttachmentHandler implements BulkDeleteAttachmentsHandlerInterface
{
/**
* {@inheritdoc}
*
* @throws BulkDeleteAttachmentsException
*/
public function handle(BulkDeleteAttachmentsCommand $command)
{
$errors = [];
foreach ($command->getAttachmentIds() as $attachmentId) {
try {
$attachment = $this->getAttachment($attachmentId);
if (!$this->deleteAttachment($attachment)) {
$errors[] = $attachment->id;
}
} catch (AttachmentException) {
$errors[] = $attachmentId->getValue();
}
}
if (!empty($errors)) {
throw new BulkDeleteAttachmentsException($errors, 'Failed to delete all of selected attachments');
}
}
}

View File

@@ -0,0 +1,36 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\Attachment\CommandHandler;
use PrestaShop\PrestaShop\Adapter\Attachment\AbstractAttachmentHandler;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\Attachment\Command\DeleteAttachmentCommand;
use PrestaShop\PrestaShop\Core\Domain\Attachment\CommandHandler\DeleteAttachmentHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\Attachment\Exception\AttachmentNotFoundException;
use PrestaShop\PrestaShop\Core\Domain\Attachment\Exception\DeleteAttachmentException;
/**
* Delete attachment handler
*/
#[AsCommandHandler]
final class DeleteAttachmentHandler extends AbstractAttachmentHandler implements DeleteAttachmentHandlerInterface
{
/**
* {@inheritdoc}
*
* @throws DeleteAttachmentException
* @throws AttachmentNotFoundException
*/
public function handle(DeleteAttachmentCommand $command)
{
$attachment = $this->getAttachment($command->getAttachmentId());
if (!$this->deleteAttachment($attachment)) {
throw new DeleteAttachmentException(sprintf('Cannot delete Attachment object with id "%s".', $attachment->id));
}
}
}

View File

@@ -0,0 +1,117 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\Attachment\CommandHandler;
use Attachment;
use PrestaShop\PrestaShop\Adapter\Attachment\AbstractAttachmentHandler;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\Attachment\AttachmentFileUploaderInterface;
use PrestaShop\PrestaShop\Core\Domain\Attachment\Command\EditAttachmentCommand;
use PrestaShop\PrestaShop\Core\Domain\Attachment\CommandHandler\EditAttachmentHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\Attachment\Exception\AttachmentConstraintException;
use PrestaShop\PrestaShop\Core\Domain\Attachment\Exception\AttachmentException;
use PrestaShop\PrestaShop\Core\Domain\Attachment\Exception\AttachmentNotFoundException;
use PrestaShop\PrestaShop\Core\Domain\Attachment\Exception\CannotUpdateAttachmentException;
use PrestaShopException;
use Symfony\Component\Validator\Validator\ValidatorInterface;
/**
* Handles editing of attachment and file uploading procedures
*/
#[AsCommandHandler]
final class EditAttachmentHandler extends AbstractAttachmentHandler implements EditAttachmentHandlerInterface
{
/**
* @var AttachmentFileUploaderInterface
*/
protected $fileUploader;
/**
* @param ValidatorInterface $validator
* @param AttachmentFileUploaderInterface $fileUploader
*/
public function __construct(ValidatorInterface $validator, AttachmentFileUploaderInterface $fileUploader)
{
parent::__construct($validator);
$this->fileUploader = $fileUploader;
}
/**
* {@inheritdoc}
*
* @throws AttachmentConstraintException
* @throws AttachmentException
* @throws AttachmentNotFoundException
* @throws CannotUpdateAttachmentException
*/
public function handle(EditAttachmentCommand $command)
{
$attachmentIdValue = $command->getAttachmentId()->getValue();
try {
$attachment = new Attachment($attachmentIdValue);
} catch (PrestaShopException) {
throw new AttachmentNotFoundException(sprintf('Attachment with id "%s" was not found.', $attachmentIdValue));
}
if ($attachment->id !== $attachmentIdValue) {
throw new AttachmentNotFoundException(sprintf('Attachment with id "%s" was not found.', $attachmentIdValue));
}
$this->updateAttachmentFromCommandData($attachment, $command);
}
/**
* @param Attachment $attachment
* @param EditAttachmentCommand $command
*
* @throws AttachmentConstraintException
* @throws AttachmentException
* @throws AttachmentNotFoundException
* @throws CannotUpdateAttachmentException
*/
private function updateAttachmentFromCommandData(Attachment $attachment, EditAttachmentCommand $command)
{
try {
if (!$attachment->validateFields(false) && !$attachment->validateFieldsLang(false)) {
throw new AttachmentConstraintException('Attachment contains invalid field values', AttachmentConstraintException::INVALID_FIELDS);
}
$this->assertDescriptionContainsCleanHtml($command->getLocalizedDescriptions());
$this->assertHasDefaultLanguage($command->getLocalizedNames());
$attachment->description = $command->getLocalizedDescriptions();
$attachment->name = $command->getLocalizedNames();
$this->assertValidFields($attachment);
if (null !== $command->getPathName()) {
$uniqueFileName = $this->getUniqueFileName();
$attachment->file_name = $command->getOriginalFileName();
$attachment->file = $uniqueFileName;
$attachment->mime = $command->getMimeType();
$this->assertValidFields($attachment);
$this->fileUploader->upload(
$command->getPathName(),
$uniqueFileName,
$command->getFileSize(),
$command->getAttachmentId()->getValue()
);
}
if (false === $attachment->update()) {
throw new CannotUpdateAttachmentException('Failed to update attachment');
}
} catch (PrestaShopException $e) {
throw new AttachmentException('An unexpected error occurred when updating attachment', 0, $e);
}
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\Attachment\QueryHandler;
use Attachment;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsQueryHandler;
use PrestaShop\PrestaShop\Core\Domain\Attachment\Exception\AttachmentNotFoundException;
use PrestaShop\PrestaShop\Core\Domain\Attachment\Query\GetAttachmentForEditing;
use PrestaShop\PrestaShop\Core\Domain\Attachment\QueryHandler\GetAttachmentForEditingHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\Attachment\QueryResult\EditableAttachment;
use PrestaShopException;
use SplFileInfo;
/**
* Handles command that gets attachment for editing
*
* @internal
*/
#[AsQueryHandler]
final class GetAttachmentForEditingHandler implements GetAttachmentForEditingHandlerInterface
{
/**
* {@inheritdoc}
*
* @throws AttachmentNotFoundException
*/
public function handle(GetAttachmentForEditing $query): EditableAttachment
{
$attachmentIdValue = $query->getAttachmentId()->getValue();
try {
$attachment = new Attachment($attachmentIdValue);
} catch (PrestaShopException) {
throw new AttachmentNotFoundException(sprintf('Attachment with id "%s" was not found.', $attachmentIdValue));
}
if ($attachment->id !== $attachmentIdValue) {
throw new AttachmentNotFoundException(sprintf('Attachment with id "%s" was not found.', $attachmentIdValue));
}
$filePath = _PS_DOWNLOAD_DIR_ . $attachment->file;
$file = file_exists($filePath) ? new SplFileInfo($filePath) : null;
$editableAttachment = new EditableAttachment(
$attachment->file_name,
$attachment->name,
$attachment->description
);
if (null !== $file) {
$editableAttachment->setFile($file);
}
return $editableAttachment;
}
}

View File

@@ -0,0 +1,54 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\Attachment\QueryHandler;
use PrestaShop\PrestaShop\Adapter\Attachment\AbstractAttachmentHandler;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsQueryHandler;
use PrestaShop\PrestaShop\Core\Domain\Attachment\Exception\AttachmentNotFoundException;
use PrestaShop\PrestaShop\Core\Domain\Attachment\Query\GetAttachment;
use PrestaShop\PrestaShop\Core\Domain\Attachment\QueryHandler\GetAttachmentHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\Attachment\QueryResult\Attachment;
/**
* Provides path and original file name of attachment
*/
#[AsQueryHandler]
final class GetAttachmentHandler extends AbstractAttachmentHandler implements GetAttachmentHandlerInterface
{
/**
* @var string
*/
private $downloadDirectory;
/**
* @param string $downloadDirectory
*/
public function __construct(string $downloadDirectory)
{
$this->downloadDirectory = $downloadDirectory;
}
/**
* {@inheritdoc}
*
* @throws AttachmentNotFoundException
*/
public function handle(GetAttachment $query): Attachment
{
$attachment = $this->getAttachment($query->getAttachmentId());
$path = $this->downloadDirectory . $attachment->file;
if (!file_exists($path)) {
throw new AttachmentNotFoundException(sprintf('Attachment file was not found at %s', $path));
}
return new Attachment(
$path,
$attachment->file_name
);
}
}

View File

@@ -0,0 +1,49 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
declare(strict_types=1);
namespace PrestaShop\PrestaShop\Adapter\Attachment\QueryHandler;
use PrestaShop\PrestaShop\Adapter\Attachment\AttachmentRepository;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsQueryHandler;
use PrestaShop\PrestaShop\Core\Domain\Attachment\Query\GetAttachmentInformation;
use PrestaShop\PrestaShop\Core\Domain\Attachment\QueryHandler\GetAttachmentInformationHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\Attachment\QueryResult\AttachmentInformation;
/**
* Handles @see GetAttachmentInformation query using legacy object model
*/
#[AsQueryHandler]
class GetAttachmentInformationHandler implements GetAttachmentInformationHandlerInterface
{
/**
* @var AttachmentRepository
*/
private $attachmentRepository;
public function __construct(
AttachmentRepository $attachmentRepository
) {
$this->attachmentRepository = $attachmentRepository;
}
/**
* {@inheritdoc}
*/
public function handle(GetAttachmentInformation $query): AttachmentInformation
{
$attachment = $this->attachmentRepository->get($query->getAttachmentId());
return new AttachmentInformation(
(int) $attachment->id,
$attachment->name,
$attachment->description,
$attachment->file_name,
$attachment->mime,
(int) $attachment->file_size
);
}
}

View File

@@ -0,0 +1,65 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
declare(strict_types=1);
namespace PrestaShop\PrestaShop\Adapter\Attachment\QueryHandler;
use PrestaShop\PrestaShop\Adapter\Attachment\AttachmentRepository;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsQueryHandler;
use PrestaShop\PrestaShop\Core\Domain\Attachment\Exception\EmptySearchException;
use PrestaShop\PrestaShop\Core\Domain\Attachment\Query\SearchAttachment;
use PrestaShop\PrestaShop\Core\Domain\Attachment\QueryHandler\SearchAttachmentHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\Attachment\QueryResult\AttachmentInformation;
/**
* Handles @see SearchAttachment query using legacy object model
*/
#[AsQueryHandler]
class SearchAttachmentHandler implements SearchAttachmentHandlerInterface
{
/**
* @var AttachmentRepository
*/
private $repository;
/**
* @param AttachmentRepository $repository
*/
public function __construct(AttachmentRepository $repository)
{
$this->repository = $repository;
}
/**
* @param SearchAttachment $query
*
* @return array
*
* @throws EmptySearchException
*/
public function handle(SearchAttachment $query): array
{
$attachments = $this->repository->search($query->getSearchPhrase());
if (empty($attachments)) {
throw new EmptySearchException(sprintf('No attachments found with search "%s"', $query->getSearchPhrase()));
}
$attachmentInfos = [];
foreach ($attachments as $attachment) {
$attachmentInfos[] = new AttachmentInformation(
(int) $attachment['id_attachment'],
$attachment['name'],
$attachment['description'],
$attachment['file_name'],
$attachment['mime'],
(int) $attachment['file_size']
);
}
return $attachmentInfos;
}
}

View File

@@ -0,0 +1,59 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\Attribute;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Attribute\Exception\AttributeException;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Attribute\Exception\AttributeNotFoundException;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Attribute\ValueObject\AttributeId;
use PrestaShopException;
use ProductAttribute;
/**
* Provides common methods for attribute command/query handlers
*/
abstract class AbstractAttributeHandler
{
/**
* @param AttributeId $attributeId
*
* @return ProductAttribute
*
* @throws AttributeException
*/
protected function getAttributeById($attributeId)
{
$idValue = $attributeId->getValue();
try {
$attribute = new ProductAttribute($idValue);
if ($attribute->id !== $idValue) {
throw new AttributeNotFoundException(sprintf('Attribute with id "%s" was not found.', $idValue));
}
} catch (PrestaShopException) {
throw new AttributeException(sprintf('An error occurred when trying to get attribute with id %s', $idValue));
}
return $attribute;
}
/**
* @param ProductAttribute $attribute
*
* @return bool
*
* @throws AttributeException
*/
protected function deleteAttribute(ProductAttribute $attribute)
{
try {
return $attribute->delete();
} catch (PrestaShopException) {
throw new AttributeException(sprintf('An error occurred when trying to delete attribute with id %s', $attribute->id));
}
}
}

View File

@@ -0,0 +1,111 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\Attribute;
use Combination;
use Context;
use Db;
use Product;
use ProductAttribute;
use Shop;
/**
* This class will provide data from DB / ORM about Attributes.
*/
class AttributeDataProvider
{
/**
* Get all attributes for a given language.
*
* @param int $id_lang Language id
* @param bool $not_null Get only not null fields if true
*
* @return array Attributes
*/
public static function getAttributes($id_lang, $not_null = false)
{
return ProductAttribute::getAttributes($id_lang, $not_null);
}
/**
* Get all attributes ids for a given group.
*
* @param int $id_group Attribute group id
* @param bool $not_null Get only not null fields if true
*
* @return array Attributes
*/
public static function getAttributeIdsByGroup($id_group, $not_null = false)
{
if (!Combination::isFeatureActive()) {
return [];
}
$result = Db::getInstance()->executeS('
SELECT DISTINCT a.`id_attribute`
FROM `' . _DB_PREFIX_ . 'attribute_group` ag
LEFT JOIN `' . _DB_PREFIX_ . 'attribute` a
ON a.`id_attribute_group` = ag.`id_attribute_group`
' . Shop::addSqlAssociation('attribute_group', 'ag') . '
' . Shop::addSqlAssociation('attribute', 'a') . '
WHERE ag.`id_attribute_group` = ' . (int) $id_group . '
' . ($not_null ? 'AND a.`id_attribute` IS NOT NULL' : '') . '
ORDER BY a.`position` ASC
');
return array_map(function ($a) {
return $a['id_attribute'];
}, $result);
}
/**
* Get combination for a product.
*
* @param int $idProduct
*
* @return array|bool
*/
public function getProductCombinations($idProduct)
{
$context = Context::getContext();
// get product
$product = new Product((int) $idProduct, false);
if (!is_object($product) || empty($product->id)) {
return false;
}
$allCombinations = $product->getAttributeCombinations(1, false);
$allCombinationsIds = array_map(function ($o) {
return $o['id_product_attribute'];
}, $allCombinations);
$combinations = [];
foreach ($allCombinationsIds as $combinationId) {
$combinations[] = $product->getAttributeCombinationsById($combinationId, $context->employee->id_lang)[0];
}
return $combinations;
}
/**
* Get combination images ids.
*
* @param int $idAttribute
*
* @return array
*/
public function getImages($idAttribute)
{
return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
SELECT a.`id_image` as id
FROM `' . _DB_PREFIX_ . 'product_attribute_image` a
' . Shop::addSqlAssociation('product_attribute', 'a') . '
WHERE a.`id_product_attribute` = ' . (int) $idAttribute . '
');
}
}

View File

@@ -0,0 +1,62 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
declare(strict_types=1);
namespace PrestaShop\PrestaShop\Adapter\Attribute\CommandHandler;
use PrestaShop\PrestaShop\Adapter\Attribute\Repository\AttributeRepository;
use PrestaShop\PrestaShop\Adapter\Attribute\Validate\AttributeValidator;
use PrestaShop\PrestaShop\Adapter\File\Uploader\AttributeFileUploader;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Attribute\Command\AddAttributeCommand;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Attribute\CommandHandler\AddAttributeHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Attribute\ValueObject\AttributeId;
use ProductAttribute;
/**
* Handles adding of attribute value using legacy logic.
*/
#[AsCommandHandler]
class AddAttributeHandler implements AddAttributeHandlerInterface
{
public function __construct(
private readonly AttributeRepository $attributeRepository,
private readonly AttributeValidator $attributeValidator,
private readonly AttributeFileUploader $attributeFileUploader
) {
}
/**
* {@inheritdoc}
*/
public function handle(AddAttributeCommand $command): AttributeId
{
$attribute = new ProductAttribute();
$attribute->name = $command->getLocalizedNames();
$attribute->id_shop_list = $command->getAssociatedShopIds();
if (!empty($command->getColor())) {
$attribute->color = $command->getColor();
}
$attribute->id_attribute_group = $command->getAttributeGroupId()->getValue();
$this->attributeValidator->validate($attribute);
$id = $this->attributeRepository->add($attribute);
if (null !== $command->getTextureFilePath()) {
$this->attributeFileUploader->upload(
$command->getTextureFilePath(),
$id->getValue()
);
}
return $id;
}
}

View File

@@ -0,0 +1,37 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\Attribute\CommandHandler;
use PrestaShop\PrestaShop\Adapter\Attribute\AbstractAttributeHandler;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Attribute\Command\BulkDeleteAttributeCommand;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Attribute\CommandHandler\BulkDeleteAttributeHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Attribute\Exception\AttributeException;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Attribute\Exception\DeleteAttributeException;
/**
* Handles command which deletes attributes in bulk action using legacy object model
*/
#[AsCommandHandler]
final class BulkDeleteAttributeHandler extends AbstractAttributeHandler implements BulkDeleteAttributeHandlerInterface
{
/**
* @param BulkDeleteAttributeCommand $command
*
* @throws AttributeException
*/
public function handle(BulkDeleteAttributeCommand $command)
{
foreach ($command->getAttributeIds() as $attributeId) {
$attribute = $this->getAttributeById($attributeId);
if (false === $this->deleteAttribute($attribute)) {
throw new DeleteAttributeException(sprintf('Failed to delete attribute with id "%s"', $attribute->id), DeleteAttributeException::FAILED_BULK_DELETE);
}
}
}
}

View File

@@ -0,0 +1,35 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\Attribute\CommandHandler;
use PrestaShop\PrestaShop\Adapter\Attribute\AbstractAttributeHandler;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Attribute\Command\DeleteAttributeCommand;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Attribute\CommandHandler\DeleteAttributeHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Attribute\Exception\AttributeException;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Attribute\Exception\DeleteAttributeException;
/**
* Handles command which deletes the Attribute using legacy object model
*/
#[AsCommandHandler]
final class DeleteAttributeHandler extends AbstractAttributeHandler implements DeleteAttributeHandlerInterface
{
/**
* {@inheritdoc}
*
* @throws AttributeException
*/
public function handle(DeleteAttributeCommand $command)
{
$attribute = $this->getAttributeById($command->getAttributeId());
if (false === $this->deleteAttribute($attribute)) {
throw new DeleteAttributeException(sprintf('Failed to delete attribute with id "%s".', $attribute->id), DeleteAttributeException::FAILED_DELETE);
}
}
}

View File

@@ -0,0 +1,35 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\Attribute\CommandHandler;
use PrestaShop\PrestaShop\Adapter\Attribute\AbstractAttributeHandler;
use PrestaShop\PrestaShop\Adapter\File\Uploader\AttributeFileUploader;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Attribute\CommandHandler\DeleteAttributeTextureImageHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Command\DeleteAttributeTextureImageCommand;
/**
* Handles command which deletes the Attribute using legacy object model
*/
#[AsCommandHandler]
final class DeleteAttributeTextureImageHandler extends AbstractAttributeHandler implements DeleteAttributeTextureImageHandlerInterface
{
public function __construct(
private AttributeFileUploader $attributeFileUploader
) {
}
/**
* {@inheritdoc}
*/
public function handle(DeleteAttributeTextureImageCommand $command)
{
$attribute = $this->getAttributeById($command->getAttributeId());
$this->attributeFileUploader->deleteOldFile($attribute->id);
}
}

View File

@@ -0,0 +1,69 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
declare(strict_types=1);
namespace PrestaShop\PrestaShop\Adapter\Attribute\CommandHandler;
use PrestaShop\PrestaShop\Adapter\Attribute\Repository\AttributeRepository;
use PrestaShop\PrestaShop\Adapter\Attribute\Validate\AttributeValidator;
use PrestaShop\PrestaShop\Adapter\Domain\LocalizedObjectModelTrait;
use PrestaShop\PrestaShop\Adapter\File\Uploader\AttributeFileUploader;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Attribute\Command\EditAttributeCommand;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Attribute\CommandHandler\EditAttributeHandlerInterface;
/**
* Handles editing of attribute groups using legacy logic.
*/
#[AsCommandHandler]
class EditAttributeHandler implements EditAttributeHandlerInterface
{
use LocalizedObjectModelTrait;
public function __construct(
private AttributeRepository $attributeRepository,
private AttributeValidator $attributeValidator,
private AttributeFileUploader $attributeFileUploader
) {
}
/**
* {@inheritdoc}
*/
public function handle(EditAttributeCommand $command): void
{
$attribute = $this->attributeRepository->get($command->getAttributeId());
$propertiesToUpdate = [];
if (null !== $command->getLocalizedNames()) {
$this->fillLocalizedValues($attribute, 'name', $command->getLocalizedNames(), $propertiesToUpdate);
}
if (null !== $command->getColor()) {
$attribute->color = $command->getColor();
$propertiesToUpdate[] = 'color';
}
if (null !== $command->getAttributeGroupId()) {
$attribute->id_attribute_group = $command->getAttributeGroupId()->getValue();
$propertiesToUpdate[] = 'id_attribute_group';
}
if (null !== $command->getAssociatedShopIds()) {
$attribute->id_shop_list = $command->getAssociatedShopIds();
$propertiesToUpdate[] = 'id_shop_list';
}
if (null !== $command->getTextureFilePath()) {
$this->attributeFileUploader->deleteOldFile($command->getAttributeId()->getValue());
$this->attributeFileUploader->upload($command->getTextureFilePath(), $command->getAttributeId()->getValue());
}
$this->attributeValidator->validate($attribute);
$this->attributeRepository->partialUpdate($attribute, $propertiesToUpdate);
}
}

View File

@@ -0,0 +1,94 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\Attribute\QueryHandler;
use ImageManager;
use PrestaShop\PrestaShop\Adapter\Attribute\Repository\AttributeRepository;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsQueryHandler;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Attribute\Query\GetAttributeForEditing;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Attribute\QueryHandler\GetAttributeForEditingHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Attribute\QueryResult\EditableAttribute;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Attribute\ValueObject\AttributeId;
use PrestaShop\PrestaShop\Core\Image\Parser\ImageTagSourceParserInterface;
/**
* Handles query which gets attribute group for editing
*/
#[AsQueryHandler]
final class GetAttributeForEditingHandler implements GetAttributeForEditingHandlerInterface
{
/**
* @var AttributeRepository
*/
private $attributeRepository;
/**
* @var ImageTagSourceParserInterface
*/
private $imageTagSourceParser;
public function __construct(
AttributeRepository $attributeRepository,
ImageTagSourceParserInterface $imageTagSourceParser
) {
$this->attributeRepository = $attributeRepository;
$this->imageTagSourceParser = $imageTagSourceParser;
}
/**
* {@inheritdoc}
*/
public function handle(GetAttributeForEditing $query): EditableAttribute
{
$attributeId = $query->getAttributeId();
$attribute = $this->attributeRepository->get($attributeId);
return new EditableAttribute(
$attributeId->getValue(),
(int) $attribute->id_attribute_group,
$attribute->name,
$attribute->color,
$attribute->getAssociatedShops(),
$this->getTextureImage($attributeId),
);
}
/**
* @param AttributeId $attributeId
*
* @return array|null
*/
private function getTextureImage(AttributeId $attributeId)
{
$imageType = 'jpg';
$image = _PS_IMG_DIR_ . 'co/' . $attributeId->getValue() . '.' . $imageType;
if (!file_exists($image)) {
return null;
}
$imageTag = ImageManager::thumbnail(
$image,
'attribute_texture_' . $attributeId->getValue() . '_thumb.' . $imageType,
150,
$imageType,
true,
true
);
if (empty($imageTag)) {
return null;
}
$imageSize = filesize($image) / 1000;
return [
'size' => sprintf('%skB', $imageSize),
'path' => $this->imageTagSourceParser->parse($imageTag),
];
}
}

View File

@@ -0,0 +1,439 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
declare(strict_types=1);
namespace PrestaShop\PrestaShop\Adapter\Attribute\Repository;
use Attribute;
use Doctrine\DBAL\ArrayParameterType;
use Doctrine\DBAL\Connection;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Attribute\Exception\AttributeNotFoundException;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Attribute\Exception\CannotAddAttributeException;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Attribute\Exception\CannotUpdateAttributeException;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Attribute\ValueObject\AttributeId;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\ValueObject\AttributeGroupId;
use PrestaShop\PrestaShop\Core\Domain\Language\ValueObject\LanguageId;
use PrestaShop\PrestaShop\Core\Domain\Product\Combination\CombinationAttributeInformation;
use PrestaShop\PrestaShop\Core\Domain\Product\Combination\ValueObject\CombinationId;
use PrestaShop\PrestaShop\Core\Domain\Shop\Exception\ShopAssociationNotFound;
use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopConstraint;
use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopId;
use PrestaShop\PrestaShop\Core\Exception\CoreException;
use PrestaShop\PrestaShop\Core\Repository\AbstractMultiShopObjectModelRepository;
use ProductAttribute;
use RuntimeException;
/**
* Provides access to attribute data source
*/
class AttributeRepository extends AbstractMultiShopObjectModelRepository
{
private Connection $connection;
private string $dbPrefix;
public function __construct(
Connection $connection,
string $dbPrefix
) {
$this->connection = $connection;
$this->dbPrefix = $dbPrefix;
}
/**
* @param AttributeId $attributeId
*
* @return ProductAttribute
*
* @throws AttributeNotFoundException
* @throws CoreException
*/
public function get(AttributeId $attributeId): ProductAttribute
{
/** @var ProductAttribute $attribute */
$attribute = $this->getObjectModel(
$attributeId->getValue(),
ProductAttribute::class,
AttributeNotFoundException::class
);
return $attribute;
}
public function assertAttributeExists(AttributeId $attributeId): void
{
$this->assertObjectModelExists($attributeId->getValue(), 'attribute', AttributeNotFoundException::class);
}
/**
* @param ProductAttribute $attribute
*
* @return AttributeId
*
* @throws CoreException
*/
public function add(ProductAttribute $attribute): AttributeId
{
$attributeId = $this->addObjectModelToShops(
$attribute,
array_map(fn (int $shopId) => new ShopId((int) $shopId), $attribute->id_shop_list),
CannotAddAttributeException::class
);
return new AttributeId($attributeId);
}
public function partialUpdate(ProductAttribute $attribute, array $propertiesToUpdate, int $errorCode = 0): void
{
$this->partiallyUpdateObjectModel($attribute, $propertiesToUpdate, CannotUpdateAttributeException::class, $errorCode);
$this->updateObjectModelShopAssociations(
(int) $attribute->id,
ProductAttribute::class,
$attribute->id_shop_list
);
}
/**
* @param int[] $attributeIds
*/
public function assertAllAttributesExist(array $attributeIds): void
{
if (empty($attributeIds)) {
throw new RuntimeException('Empty list of attribute ids provided');
}
$qb = $this->connection->createQueryBuilder();
$qb->select('COUNT(id_attribute) AS total')
->from($this->dbPrefix . 'attribute')
->where('id_attribute IN (:idsList)')
->setParameter('idsList', $attributeIds, Connection::PARAM_INT_ARRAY)
;
$result = (int) $qb->executeQuery()->fetchAssociative()['total'];
if (count($attributeIds) !== $result) {
throw new AttributeNotFoundException('Some of provided attributes does not exist');
}
}
/**
* @param ShopConstraint $shopConstraint
* @param AttributeGroupId[] $attributeGroupIds
* @param AttributeId[] $attributeIds get only certain attributes (e.g. when need to get only certain combinations attributes)
*
* @return array<int, array<int, ProductAttribute>> arrays of product attributes indexed by product attribute groups
*/
public function getGroupedAttributes(ShopConstraint $shopConstraint, array $attributeGroupIds, array $attributeIds = []): array
{
if (empty($attributeGroupIds)) {
return [];
}
$attributeGroupIdValues = array_map(static function (AttributeGroupId $attributeGroupId): int {
return $attributeGroupId->getValue();
}, $attributeGroupIds);
$qb = $this->connection->createQueryBuilder();
$qb
->select('a.*, al.*')
->from($this->dbPrefix . 'attribute', 'a')
->innerJoin(
'a',
$this->dbPrefix . 'attribute_lang',
'al',
'a.id_attribute = al.id_attribute'
)
->andWhere($qb->expr()->in('a.id_attribute_group', ':attributeGroupIds'))
->setParameter('attributeGroupIds', $attributeGroupIdValues, Connection::PARAM_INT_ARRAY)
->addOrderBy('a.position', 'ASC')
;
if (!empty($attributeIds)) {
$attributeIdValues = array_map(static function (AttributeId $attributeId): int {
return $attributeId->getValue();
}, $attributeIds);
$qb->andWhere($qb->expr()->in('a.id_attribute', ':attributeIds'))
->setParameter('attributeIds', $attributeIdValues, Connection::PARAM_INT_ARRAY)
;
}
$shopIdValue = $shopConstraint->getShopId() ? $shopConstraint->getShopId()->getValue() : null;
if ($shopIdValue) {
$qb
->leftJoin(
'a',
$this->dbPrefix . 'attribute_shop',
'attr_shop',
'a.id_attribute = attr_shop.id_attribute'
)
->andWhere('attr_shop.id_shop = :shopId')
->setParameter('shopId', $shopIdValue)
;
}
$results = $qb->executeQuery()->fetchAllAssociative();
if (!$results) {
return [];
}
$attributes = [];
foreach ($results as $result) {
$attributeGroupId = (int) $result['id_attribute_group'];
$attributeId = (int) $result['id_attribute'];
$langId = (int) $result['id_lang'];
if (isset($attributes[$attributeGroupId][$attributeId])) {
$attribute = $attributes[$attributeGroupId][$attributeId];
} else {
$attribute = new ProductAttribute();
$attributes[$attributeGroupId][$attributeId] = $attribute;
}
$attribute->id = $attributeId;
$attribute->id_attribute_group = $attributeGroupId;
$attribute->color = (string) $result['color'];
$attribute->position = (int) $result['position'];
$attribute->name[$langId] = (string) $result['name'];
}
return $attributes;
}
/**
* @param CombinationId[] $combinationIds
* @param LanguageId $langId
*
* @return array<int, CombinationAttributeInformation[]>
*/
public function getAttributesInfoByCombinationIds(array $combinationIds, LanguageId $langId): array
{
$attributeCombinationAssociations = $this->getAttributeCombinationAssociations($combinationIds);
$attributeIds = array_unique(array_map(static function (array $attributeByCombination): int {
return (int) $attributeByCombination['id_attribute'];
}, $attributeCombinationAssociations));
$attributesInfoByAttributeId = $this->getAttributesInfoByAttributeIds($attributeIds, $langId->getValue());
return $this->buildCombinationAttributeInformationList(
$attributeCombinationAssociations,
$attributesInfoByAttributeId
);
}
/**
* Asserts that attribute exists in all the provided shops.
* If at least one of them is missing in any shop, it throws exception.
*
* @param AttributeId[] $attributeIds
* @param ShopId[] $shopIds
*
* @throws ShopAssociationNotFound
*/
public function assertExistsInEveryShop(array $attributeIds, array $shopIds): void
{
$attributeIdValues = array_map(static function (AttributeId $attributeId): int {
return $attributeId->getValue();
}, $attributeIds);
$shopIdValues = array_map(static function (ShopId $shopId): int {
return $shopId->getValue();
}, $shopIds);
$qb = $this->connection->createQueryBuilder();
$results = $qb
->select('a.id_attribute', 'attr_shop.id_shop')
->from($this->dbPrefix . 'attribute', 'a')
->innerJoin(
'a',
$this->dbPrefix . 'attribute_shop',
'attr_shop',
'a.id_attribute = attr_shop.id_attribute AND attr_shop.id_shop IN (:shopIds)'
)
->where($qb->expr()->in('a.id_attribute', ':attributeIds'))
->setParameter('shopIds', $shopIdValues, Connection::PARAM_INT_ARRAY)
->setParameter('attributeIds', $attributeIdValues, Connection::PARAM_INT_ARRAY)
->executeQuery()
->fetchAllAssociative()
;
$attributeShops = [];
foreach ($results as $result) {
$attributeShops[(int) $result['id_attribute']][] = (int) $result['id_shop'];
}
foreach ($attributeIdValues as $attributeIdValue) {
if (!isset($attributeShops[$attributeIdValue]) || $attributeShops[$attributeIdValue] !== $shopIdValues) {
throw new ShopAssociationNotFound('Provided attributes do not exist in every shop');
}
}
}
/**
* @param CombinationId[] $combinationIds
*
* @return array<int, array<string, mixed>>
*/
private function getAttributeCombinationAssociations(array $combinationIds): array
{
if (empty($combinationIds)) {
return [];
}
$combinationIds = array_map(function (CombinationId $id): int {
return $id->getValue();
}, $combinationIds);
$qb = $this->connection->createQueryBuilder();
$qb->select('pac.id_attribute')
->addSelect('pac.id_product_attribute')
->from($this->dbPrefix . 'product_attribute_combination', 'pac')
->where($qb->expr()->in('pac.id_product_attribute', ':combinationIds'))
->setParameter('combinationIds', $combinationIds, Connection::PARAM_INT_ARRAY)
;
return $qb->executeQuery()->fetchAllAssociative();
}
/**
* @param int[] $attributeIds
* @param int $langId
*
* @return array<int, array<string, mixed>>
*/
public function getAttributesInfoByAttributeIds(array $attributeIds, int $langId): array
{
$qb = $this->connection->createQueryBuilder();
$qb->select('a.id_attribute, a.position, a.color')
->addSelect('ag.id_attribute_group')
->addSelect('al.name AS attribute_name')
->addSelect('agl.name AS attribute_group_name')
->addSelect('agl.public_name AS attribute_group_public_name')
->from($this->dbPrefix . 'attribute', 'a')
->leftJoin(
'a',
$this->dbPrefix . 'attribute_lang',
'al',
'a.id_attribute = al.id_attribute AND al.id_lang = :langId'
)->leftJoin(
'a',
$this->dbPrefix . 'attribute_group',
'ag',
'a.id_attribute_group = ag.id_attribute_group'
)->leftJoin(
'ag',
$this->dbPrefix . 'attribute_group_lang',
'agl',
'agl.id_attribute_group = ag.id_attribute_group AND agl.id_lang = :langId'
)->where($qb->expr()->in('a.id_attribute', ':attributeIds'))
->addOrderBy('ag.position', 'ASC')
->addOrderBy('a.position', 'ASC')
->setParameter('attributeIds', $attributeIds, Connection::PARAM_INT_ARRAY)
->setParameter('langId', $langId)
;
$attributesInfo = $qb->executeQuery()->fetchAllAssociative();
$attributesInfoByAttributeId = [];
foreach ($attributesInfo as $attributeInfo) {
$attributesInfoByAttributeId[(int) $attributeInfo['id_attribute']] = $attributeInfo;
}
return $attributesInfoByAttributeId;
}
/**
* Retrieve attributes with values (id, name).
*
* @param int $languageId
* @param int[] $shopIds
*
* @return array<int, array{attribute_group_id: int, name: string, values: array<int, array{item_id: int, name: string}>}>
*/
public function getAttributesWithValues(int $languageId, array $shopIds = []): array
{
$qb = $this->connection->createQueryBuilder()
->select(
'DISTINCT a.id_attribute_group AS attribute_group_id',
'agl.name AS name',
'al.id_attribute AS value_id',
'al.name AS value_name'
)
->from($this->dbPrefix . 'attribute', 'a')
->leftJoin('a', $this->dbPrefix . 'attribute_lang', 'al',
'a.id_attribute = al.id_attribute AND al.id_lang = :language_id AND LENGTH(TRIM(al.name)) > 0'
)
->leftJoin('a', $this->dbPrefix . 'attribute_group', 'ag',
'ag.id_attribute_group = a.id_attribute_group'
)
->leftJoin('ag', $this->dbPrefix . 'attribute_group_lang', 'agl',
'ag.id_attribute_group = agl.id_attribute_group AND agl.id_lang = :language_id AND LENGTH(TRIM(agl.name)) > 0'
)
->orderBy('a.id_attribute_group')
->addOrderBy('al.id_attribute')
->setParameter('language_id', $languageId);
if (!empty($shopIds)) {
$qb
->join('a', $this->dbPrefix . 'attribute_shop', 'ats',
'ats.id_attribute = a.id_attribute AND ats.id_shop IN (:shop_ids)'
)
->join('a', $this->dbPrefix . 'attribute_group_shop', 'ags',
'ags.id_attribute_group = a.id_attribute_group AND ags.id_shop IN (:shop_ids)'
)
->setParameter('shop_ids', $shopIds, ArrayParameterType::INTEGER);
}
$rows = $qb->executeQuery()->fetchAllAssociative();
$attributeGroups = [];
foreach ($rows as $row) {
$groupId = (int) $row['attribute_group_id'];
if (!isset($attributeGroups[$groupId])) {
$attributeGroups[$groupId] = [
'attribute_group_id' => (int) $groupId,
'name' => (string) $row['name'],
'values' => [],
];
}
$attributeGroups[$groupId]['values'][] = [
'item_id' => (int) $row['value_id'],
'name' => (string) $row['value_name'],
];
}
return array_values($attributeGroups);
}
/**
* @param array<int, array<string, int>> $attributeCombinationAssociations
* @param array<int, array<string, mixed>> $attributesInfoByAttributeId
*
* @return array<int, CombinationAttributeInformation[]>
*/
private function buildCombinationAttributeInformationList(
array $attributeCombinationAssociations,
array $attributesInfoByAttributeId
): array {
$attributesInfoByCombinationId = [];
foreach ($attributeCombinationAssociations as $attributeCombinationAssociation) {
$combinationId = (int) $attributeCombinationAssociation['id_product_attribute'];
$attributeId = (int) $attributeCombinationAssociation['id_attribute'];
$attributesInfoByCombinationId[$combinationId][] = new CombinationAttributeInformation(
(int) $attributesInfoByAttributeId[$attributeId]['id_attribute_group'],
$attributesInfoByAttributeId[$attributeId]['attribute_group_name'],
(int) $attributesInfoByAttributeId[$attributeId]['id_attribute'],
$attributesInfoByAttributeId[$attributeId]['attribute_name']
);
}
return $attributesInfoByCombinationId;
}
}

View File

@@ -0,0 +1,47 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
declare(strict_types=1);
namespace PrestaShop\PrestaShop\Adapter\Attribute\Validate;
use PrestaShop\PrestaShop\Adapter\AbstractObjectModelValidator;
use PrestaShop\PrestaShop\Adapter\Shop\Repository\ShopRepository;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Attribute\Exception\AttributeConstraintException;
use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopId;
use PrestaShop\PrestaShop\Core\Exception\CoreException;
use ProductAttribute;
/**
* Validates attribute properties using legacy object model
*/
class AttributeValidator extends AbstractObjectModelValidator
{
public function __construct(
private ShopRepository $shopRepository
) {
}
/**
* @param ProductAttribute $attribute
*
* @throws CoreException
*/
public function validate(ProductAttribute $attribute): void
{
$this->validateObjectModelLocalizedProperty($attribute, 'name', AttributeConstraintException::class, AttributeConstraintException::INVALID_NAME);
$this->validateObjectModelProperty($attribute, 'color', AttributeConstraintException::class, AttributeConstraintException::INVALID_COLOR);
$this->validateObjectModelProperty($attribute, 'id_attribute_group', AttributeConstraintException::class, AttributeConstraintException::INVALID_ATTRIBUTE_GROUP_ID);
$this->validateShopsExists($attribute->id_shop_list);
}
private function validateShopsExists(array $shopIds): void
{
foreach ($shopIds as $shopId) {
$this->shopRepository->assertShopExists(new ShopId((int) $shopId));
}
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
declare(strict_types=1);
namespace PrestaShop\PrestaShop\Adapter\AttributeGroup;
use AttributeGroup;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Exception\AttributeGroupException;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Exception\AttributeGroupNotFoundException;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\ValueObject\AttributeGroupId;
use PrestaShopException;
/**
* Provides reusable methods for attribute group handlers
*/
abstract class AbstractAttributeGroupHandler
{
/**
* @param AttributeGroupId $attributeGroupId
*
* @return AttributeGroup
*
* @throws AttributeGroupException
*/
protected function getAttributeGroupById($attributeGroupId)
{
$idValue = $attributeGroupId->getValue();
try {
$attributeGroup = new AttributeGroup($idValue);
if ($attributeGroup->id !== $idValue) {
throw new AttributeGroupNotFoundException(sprintf('Attribute group with id "%s" was not found.', $idValue));
}
} catch (PrestaShopException) {
throw new AttributeGroupException(sprintf('An error occurred when trying to get attribute group with id %s', $idValue));
}
return $attributeGroup;
}
/**
* @param AttributeGroup $attributeGroup
*
* @return bool
*
* @throws AttributeGroupException
*/
protected function deleteAttributeGroup(AttributeGroup $attributeGroup)
{
try {
return $attributeGroup->delete();
} catch (PrestaShopException) {
throw new AttributeGroupException(sprintf('An error occurred when trying to delete attribute with id %s', $attributeGroup->id));
}
}
}

View File

@@ -0,0 +1,90 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
declare(strict_types=1);
namespace PrestaShop\PrestaShop\Adapter\AttributeGroup;
use AttributeGroup;
use PrestaShop\PrestaShop\Core\AttributeGroup\AttributeGroupViewDataProviderInterface;
use PrestaShop\PrestaShop\Core\ConfigurationInterface;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Exception\AttributeGroupException;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Exception\AttributeGroupNotFoundException;
use PrestaShopException;
/**
* Provides data required for attribute group view action using legacy object models
*/
final class AttributeGroupViewDataProvider implements AttributeGroupViewDataProviderInterface
{
/**
* @var int
*/
private $contextLangId;
/**
* @var ConfigurationInterface
*/
private $configuration;
/**
* @param int $contextLangId
* @param ConfigurationInterface $configuration
*/
public function __construct($contextLangId, ConfigurationInterface $configuration)
{
$this->contextLangId = $contextLangId;
$this->configuration = $configuration;
}
/**
* {@inheritdoc}
*/
public function isColorGroup($attributeGroupId)
{
$attributeGroup = $this->getAttributeGroupById($attributeGroupId);
return (bool) $attributeGroup->is_color_group;
}
/**
* {@inheritdoc}
*/
public function getAttributeGroupNameById($attributeGroupId)
{
$attributeGroup = $this->getAttributeGroupById($attributeGroupId);
if (!isset($attributeGroup->name[$this->contextLangId])) {
return $attributeGroup->name[$this->configuration->get('PS_LANG_DEFAULT')];
}
return $attributeGroup->name[$this->contextLangId];
}
/**
* Gets legacy AttributeGroup object by provided id
*
* @param int $attributeGroupId
*
* @return AttributeGroup
*
* @throws AttributeGroupException
* @throws AttributeGroupNotFoundException
*/
private function getAttributeGroupById($attributeGroupId)
{
try {
$attributeGroup = new AttributeGroup($attributeGroupId);
if ($attributeGroup->id !== $attributeGroupId) {
throw new AttributeGroupNotFoundException(sprintf('Attribute group with id "%s" was not found.', $attributeGroupId));
}
} catch (PrestaShopException) {
throw new AttributeGroupException(sprintf('An error occurred when trying to get attribute group with id %s', $attributeGroupId));
}
return $attributeGroup;
}
}

View File

@@ -0,0 +1,48 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
declare(strict_types=1);
namespace PrestaShop\PrestaShop\Adapter\AttributeGroup\CommandHandler;
use AttributeGroup;
use PrestaShop\PrestaShop\Adapter\AttributeGroup\Repository\AttributeGroupRepository;
use PrestaShop\PrestaShop\Adapter\AttributeGroup\Validate\AttributeGroupValidator;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Command\AddAttributeGroupCommand;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\CommandHandler\AddAttributeGroupHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\ValueObject\AttributeGroupId;
/**
* Handles adding of attribute groups using legacy logic.
*/
#[AsCommandHandler]
final class AddAttributeGroupHandler implements AddAttributeGroupHandlerInterface
{
public function __construct(
private AttributeGroupRepository $attributeGroupRepository,
private AttributeGroupValidator $validator
) {
}
/**
* {@inheritdoc}
*/
public function handle(AddAttributeGroupCommand $command): AttributeGroupId
{
$attributeGroup = new AttributeGroup();
$attributeGroup->name = $command->getLocalizedNames();
$attributeGroup->public_name = $command->getLocalizedPublicNames();
$attributeGroup->group_type = $command->getType()->getValue();
$attributeGroup->id_shop_list = $command->getAssociatedShopIds();
$this->validator->validate($attributeGroup);
$id = $this->attributeGroupRepository->add($attributeGroup);
return $id;
}
}

View File

@@ -0,0 +1,35 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
declare(strict_types=1);
namespace PrestaShop\PrestaShop\Adapter\AttributeGroup\CommandHandler;
use PrestaShop\PrestaShop\Adapter\AttributeGroup\AbstractAttributeGroupHandler;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Command\BulkDeleteAttributeGroupCommand;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\CommandHandler\BulkDeleteAttributeGroupHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Exception\DeleteAttributeGroupException;
/**
* Handles command which deletes multiple attribute groups using legacy object model
*/
#[AsCommandHandler]
final class BulkDeleteAttributeGroupHandler extends AbstractAttributeGroupHandler implements BulkDeleteAttributeGroupHandlerInterface
{
/**
* {@inheritdoc}
*/
public function handle(BulkDeleteAttributeGroupCommand $command)
{
foreach ($command->getAttributeGroupIds() as $attributeGroupId) {
$attributeGroup = $this->getAttributeGroupById($attributeGroupId);
if (false === $this->deleteAttributeGroup($attributeGroup)) {
throw new DeleteAttributeGroupException(sprintf('Failed to delete attribute group with id "%s"', $attributeGroupId->getValue()), DeleteAttributeGroupException::FAILED_BULK_DELETE);
}
}
}
}

View File

@@ -0,0 +1,37 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
declare(strict_types=1);
namespace PrestaShop\PrestaShop\Adapter\AttributeGroup\CommandHandler;
use PrestaShop\PrestaShop\Adapter\AttributeGroup\AbstractAttributeGroupHandler;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Command\DeleteAttributeGroupCommand;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\CommandHandler\DeleteAttributeGroupHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Exception\AttributeGroupException;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Exception\DeleteAttributeGroupException;
/**
* Handles command which deletes attribute group using legacy object model
*/
#[AsCommandHandler]
final class DeleteAttributeGroupHandler extends AbstractAttributeGroupHandler implements DeleteAttributeGroupHandlerInterface
{
/**
* {@inheritdoc}
*
* @throws AttributeGroupException
*/
public function handle(DeleteAttributeGroupCommand $command)
{
$attributeGroupId = $command->getAttributeGroupId();
$attributeGroup = $this->getAttributeGroupById($attributeGroupId);
if (false === $this->deleteAttributeGroup($attributeGroup)) {
throw new DeleteAttributeGroupException(sprintf('Failed deleting attribute group with id "%s"', $attributeGroupId->getValue()), DeleteAttributeGroupException::FAILED_DELETE);
}
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
declare(strict_types=1);
namespace PrestaShop\PrestaShop\Adapter\AttributeGroup\CommandHandler;
use PrestaShop\PrestaShop\Adapter\AttributeGroup\Repository\AttributeGroupRepository;
use PrestaShop\PrestaShop\Adapter\AttributeGroup\Validate\AttributeGroupValidator;
use PrestaShop\PrestaShop\Adapter\Domain\LocalizedObjectModelTrait;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Command\EditAttributeGroupCommand;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\CommandHandler\EditAttributeGroupHandlerInterface;
/**
* Handles editing of attribute groups using legacy logic.
*/
#[AsCommandHandler]
final class EditAttributeGroupHandler implements EditAttributeGroupHandlerInterface
{
use LocalizedObjectModelTrait;
public function __construct(
private AttributeGroupRepository $attributeGroupRepository,
private AttributeGroupValidator $validator,
) {
}
/**
* {@inheritdoc}
*/
public function handle(EditAttributeGroupCommand $command): void
{
$attributeGroup = $this->attributeGroupRepository->get($command->getAttributeGroupId());
$propertiesToUpdate = [];
if (null !== $command->getLocalizedNames()) {
$this->fillLocalizedValues($attributeGroup, 'name', $command->getLocalizedNames(), $propertiesToUpdate);
}
if (null !== $command->getLocalizedPublicNames()) {
$this->fillLocalizedValues($attributeGroup, 'public_name', $command->getLocalizedPublicNames(), $propertiesToUpdate);
}
if (null !== $command->getType()) {
$propertiesToUpdate[] = 'group_type';
$attributeGroup->group_type = $command->getType()->getValue();
$propertiesToUpdate[] = 'is_color_group';
$attributeGroup->is_color_group = $attributeGroup->group_type === 'color';
}
if (null !== $command->getAssociatedShopIds()) {
$attributeGroup->id_shop_list = $command->getAssociatedShopIds();
$propertiesToUpdate[] = 'id_shop_list';
}
$this->validator->validate($attributeGroup);
$this->attributeGroupRepository->partialUpdate($attributeGroup, $propertiesToUpdate);
}
}

View File

@@ -0,0 +1,79 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
declare(strict_types=1);
namespace PrestaShop\PrestaShop\Adapter\AttributeGroup\QueryHandler;
use AttributeGroup as AttributeGroupObjectModel;
use PrestaShop\PrestaShop\Adapter\Attribute\Repository\AttributeRepository;
use PrestaShop\PrestaShop\Adapter\AttributeGroup\Repository\AttributeGroupRepository;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Attribute\QueryResult\Attribute;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\QueryResult\AttributeGroup;
use ProductAttribute as AttributeObjectModel;
abstract class AbstractAttributeGroupQueryHandler
{
/**
* @var AttributeRepository
*/
protected $attributeRepository;
/**
* @var AttributeGroupRepository
*/
protected $attributeGroupRepository;
public function __construct(
AttributeRepository $attributeRepository,
AttributeGroupRepository $attributeGroupRepository
) {
$this->attributeRepository = $attributeRepository;
$this->attributeGroupRepository = $attributeGroupRepository;
}
/**
* @param array<int, AttributeGroupObjectModel> $attributeGroups
* @param array<int, array<int, AttributeObjectModel>> $attributes
*
* @return AttributeGroup[]
*/
protected function formatAttributeGroupsList(
array $attributeGroups,
array $attributes
): array {
$attributeGroupsResult = [];
foreach ($attributeGroups as $attributeGroupId => $attributeGroup) {
if (!isset($attributes[$attributeGroupId])) {
$attributesResult = [];
} else {
$attributesResult = [];
foreach ($attributes[$attributeGroupId] as $attributeId => $attribute) {
$attributesResult[] = new Attribute(
$attributeId,
$attribute->position,
$attribute->color,
$attribute->name,
file_exists(_PS_COL_IMG_DIR_ . $attributeId . '.jpg') ? _THEME_COL_DIR_ . $attributeId . '.jpg' : null
);
}
}
$attributeGroupsResult[] = new AttributeGroup(
$attributeGroupId,
$attributeGroup->name,
$attributeGroup->public_name,
$attributeGroup->group_type,
$attributeGroup->is_color_group,
$attributeGroup->position,
$attributesResult
);
}
return $attributeGroupsResult;
}
}

View File

@@ -0,0 +1,47 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
declare(strict_types=1);
namespace PrestaShop\PrestaShop\Adapter\AttributeGroup\QueryHandler;
use PrestaShop\PrestaShop\Adapter\AttributeGroup\Repository\AttributeGroupRepository;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsQueryHandler;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Query\GetAttributeGroupForEditing;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\QueryHandler\GetAttributeGroupForEditingHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\QueryResult\EditableAttributeGroup;
/**
* Handles query which gets attribute group for editing
*/
#[AsQueryHandler]
final class GetAttributeGroupForEditingHandler implements GetAttributeGroupForEditingHandlerInterface
{
private AttributeGroupRepository $attributeGroupRepository;
public function __construct(AttributeGroupRepository $attributeGroupRepository)
{
$this->attributeGroupRepository = $attributeGroupRepository;
}
/**
* {@inheritdoc}
*/
public function handle(GetAttributeGroupForEditing $query): EditableAttributeGroup
{
$attributeGroup = $this->attributeGroupRepository->get(
$query->getAttributeGroupId()
);
return new EditableAttributeGroup(
$query->getAttributeGroupId()->getValue(),
$attributeGroup->name,
$attributeGroup->public_name,
$attributeGroup->group_type,
$attributeGroup->getAssociatedShops()
);
}
}

View File

@@ -0,0 +1,40 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
declare(strict_types=1);
namespace PrestaShop\PrestaShop\Adapter\AttributeGroup\QueryHandler;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsQueryHandler;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Query\GetAttributeGroupList;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\QueryHandler\GetAttributeGroupListHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\ValueObject\AttributeGroupId;
/**
* Handles the query GetAttributeGroupList using Doctrine repository
*/
#[AsQueryHandler]
class GetAttributeGroupListHandler extends AbstractAttributeGroupQueryHandler implements GetAttributeGroupListHandlerInterface
{
/**
* {@inheritDoc}
*/
public function handle(GetAttributeGroupList $query): array
{
$shopConstraint = $query->getShopConstraint();
$attributeGroups = $this->attributeGroupRepository->getAttributeGroups($shopConstraint);
return $this->formatAttributeGroupsList(
$attributeGroups,
$this->attributeRepository->getGroupedAttributes(
$shopConstraint,
array_map(static function (int $id): AttributeGroupId {
return new AttributeGroupId($id);
}, array_keys($attributeGroups))
)
);
}
}

View File

@@ -0,0 +1,203 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
declare(strict_types=1);
namespace PrestaShop\PrestaShop\Adapter\AttributeGroup\Repository;
use AttributeGroup;
use Doctrine\DBAL\Connection;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Exception\AttributeGroupNotFoundException;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Exception\CannotAddAttributeGroupException;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Exception\CannotUpdateAttributeGroupException;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\ValueObject\AttributeGroupId;
use PrestaShop\PrestaShop\Core\Domain\Shop\Exception\InvalidShopConstraintException;
use PrestaShop\PrestaShop\Core\Domain\Shop\Exception\ShopAssociationNotFound;
use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopConstraint;
use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopId;
use PrestaShop\PrestaShop\Core\Exception\CoreException;
use PrestaShop\PrestaShop\Core\Repository\AbstractMultiShopObjectModelRepository;
class AttributeGroupRepository extends AbstractMultiShopObjectModelRepository
{
public function __construct(
private Connection $connection,
private string $dbPrefix,
) {
}
/**
* @param AttributeGroup $attributeGroup
*
* @return AttributeGroupId
*
* @throws CoreException
*/
public function add(AttributeGroup $attributeGroup): AttributeGroupId
{
$attributeGroupId = $this->addObjectModelToShops(
$attributeGroup,
array_map(fn (int $shopId) => new ShopId((int) $shopId), $attributeGroup->id_shop_list),
CannotAddAttributeGroupException::class
);
return new AttributeGroupId($attributeGroupId);
}
public function partialUpdate(AttributeGroup $attribute, array $propertiesToUpdate, int $errorCode = 0): void
{
$this->partiallyUpdateObjectModel($attribute, $propertiesToUpdate, CannotUpdateAttributeGroupException::class, $errorCode);
$this->updateObjectModelShopAssociations(
(int) $attribute->id,
AttributeGroup::class,
$attribute->id_shop_list
);
}
/**
* @param AttributeGroupId $attributeGroupId
*
* @return AttributeGroup
*
* @throws ShopAssociationNotFound
* @throws CoreException
*/
public function get(AttributeGroupId $attributeGroupId): AttributeGroup
{
/** @var AttributeGroup $attributeGroup */
$attributeGroup = $this->getObjectModel(
$attributeGroupId->getValue(),
AttributeGroup::class,
AttributeGroupNotFoundException::class
);
return $attributeGroup;
}
/**
* @param ShopConstraint $shopConstraint
* @param AttributeGroupId[] $attributeGroupIds get only certain attribute groups (e.g. when need to get only certain combinations attributes groups)
*
* @return array<int, AttributeGroup> array key is the id of attribute group
*/
public function getAttributeGroups(ShopConstraint $shopConstraint, array $attributeGroupIds = []): array
{
if ($shopConstraint->getShopGroupId()) {
throw new InvalidShopConstraintException('Shop Group constraint is not supported');
}
$shopIdValue = $shopConstraint->getShopId() ? $shopConstraint->getShopId()->getValue() : null;
$qb = $this->connection->createQueryBuilder()
->select('ag.*, agl.*')
->from($this->dbPrefix . 'attribute_group', 'ag')
->innerJoin(
'ag',
$this->dbPrefix . 'attribute_group_lang',
'agl',
'ag.id_attribute_group = agl.id_attribute_group'
)
->orderBy('ag.position', 'ASC')
;
if (!empty($attributeGroupIds)) {
$attributeGroupIdValues = array_map(static function (AttributeGroupId $attributeGroupId): int {
return $attributeGroupId->getValue();
}, $attributeGroupIds);
$qb->andWhere($qb->expr()->in('ag.id_attribute_group', ':attributeGroupIds'))
->setParameter('attributeGroupIds', $attributeGroupIdValues, Connection::PARAM_INT_ARRAY)
;
}
if ($shopIdValue) {
$qb
->innerJoin(
'ag',
$this->dbPrefix . 'attribute_group_shop',
'ags',
'ag.id_attribute_group = ags.id_attribute_group'
)
->andWhere('ags.id_shop = :shopId')
->setParameter('shopId', $shopIdValue)
;
}
$results = $qb->executeQuery()->fetchAllAssociative();
if (!$results) {
return [];
}
$attributeGroups = [];
foreach ($results as $result) {
$attributeGroupId = (int) $result['id_attribute_group'];
$langId = (int) $result['id_lang'];
if (isset($attributeGroups[$attributeGroupId])) {
$attributeGroup = $attributeGroups[$attributeGroupId];
} else {
$attributeGroup = new AttributeGroup();
$attributeGroups[$attributeGroupId] = $attributeGroup;
}
$attributeGroup->id = $attributeGroupId;
$attributeGroup->is_color_group = (bool) $result['is_color_group'];
$attributeGroup->group_type = (string) $result['group_type'];
$attributeGroup->position = (int) $result['position'];
$attributeGroup->name[$langId] = (string) $result['name'];
$attributeGroup->public_name[$langId] = (string) $result['public_name'];
}
return $attributeGroups;
}
/**
* Asserts that attribute groups exists in all the provided shops.
* If at least one of them is missing in any shop, it throws exception.
*
* @param AttributeGroupId[] $attributeGroupIds
* @param ShopId[] $shopIds
*
* @throws ShopAssociationNotFound
*/
public function assertExistsInEveryShop(array $attributeGroupIds, array $shopIds): void
{
$attributeGroupIdValues = array_map(static function (AttributeGroupId $attributeGroupId): int {
return $attributeGroupId->getValue();
}, $attributeGroupIds);
$shopIdValues = array_map(static function (ShopId $shopId): int {
return $shopId->getValue();
}, $shopIds);
$qb = $this->connection->createQueryBuilder();
$results = $qb
->select('ags.id_shop, ag.id_attribute_group')
->from($this->dbPrefix . 'attribute_group', 'ag')
->innerJoin(
'ag',
$this->dbPrefix . 'attribute_group_shop',
'ags',
'ag.id_attribute_group = ags.id_attribute_group AND ags.id_shop IN (:shopIds)'
)
->andWhere($qb->expr()->in('ag.id_attribute_group', ':attributeGroupIds'))
->setParameter('shopIds', $shopIdValues, Connection::PARAM_INT_ARRAY)
->setParameter('attributeGroupIds', $attributeGroupIdValues, Connection::PARAM_INT_ARRAY)
->executeQuery()
->fetchAllAssociative()
;
$attributeGroupShops = [];
foreach ($results as $result) {
$attributeGroupShops[(int) $result['id_attribute_group']][] = (int) $result['id_shop'];
}
foreach ($attributeGroupIdValues as $attributeGroupIdValue) {
if (!isset($attributeGroupShops[$attributeGroupIdValue]) || $attributeGroupShops[$attributeGroupIdValue] !== $shopIdValues) {
throw new ShopAssociationNotFound('Provided attribute groups does not exist in every shop');
}
}
}
}

View File

@@ -0,0 +1,47 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
declare(strict_types=1);
namespace PrestaShop\PrestaShop\Adapter\AttributeGroup\Validate;
use AttributeGroup;
use PrestaShop\PrestaShop\Adapter\AbstractObjectModelValidator;
use PrestaShop\PrestaShop\Adapter\Shop\Repository\ShopRepository;
use PrestaShop\PrestaShop\Core\Domain\AttributeGroup\Exception\AttributeGroupConstraintException;
use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopId;
use PrestaShop\PrestaShop\Core\Exception\CoreException;
/**
* Validates Attribute Group properties using legacy object model
*/
class AttributeGroupValidator extends AbstractObjectModelValidator
{
public function __construct(
private ShopRepository $shopRepository
) {
}
/**
* @param AttributeGroup $attributeGroup
*
* @throws CoreException
*/
public function validate(AttributeGroup $attributeGroup): void
{
$this->validateObjectModelLocalizedProperty($attributeGroup, 'name', AttributeGroupConstraintException::class, AttributeGroupConstraintException::INVALID_NAME);
$this->validateObjectModelLocalizedProperty($attributeGroup, 'public_name', AttributeGroupConstraintException::class, AttributeGroupConstraintException::INVALID_PUBLIC_NAME);
$this->validateObjectModelProperty($attributeGroup, 'group_type', AttributeGroupConstraintException::class, AttributeGroupConstraintException::INVALID_TYPE);
$this->validateShopsExists($attributeGroup->id_shop_list);
}
private function validateShopsExists(array $shopIds): void
{
foreach ($shopIds as $shopId) {
$this->shopRepository->assertShopExists(new ShopId((int) $shopId));
}
}
}

View File

@@ -0,0 +1,88 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\Backup;
use DateTimeImmutable;
use PrestaShop\PrestaShop\Adapter\Entity\PrestaShopBackup;
use PrestaShop\PrestaShop\Core\Backup\BackupInterface;
/**
* Class Backup represents single database backup.
*
* @internal
*/
final class Backup implements BackupInterface
{
/**
* @var PrestaShopBackup
*/
private $legacyBackup;
/**
* @var string
*/
private $fileName;
/**
* @param string $fileName Backup file name
*/
public function __construct($fileName)
{
$this->fileName = $fileName;
$this->legacyBackup = new PrestaShopBackup($fileName);
}
/**
* {@inheritdoc}
*/
public function getFileName()
{
return $this->fileName;
}
/**
* {@inheritdoc}
*/
public function getFilePath()
{
return $this->legacyBackup->getBackupPath() . $this->getFileName();
}
/**
* {@inheritdoc}
*/
public function getUrl()
{
return $this->legacyBackup->getBackupURL();
}
/**
* {@inheritdoc}
*/
public function getSize()
{
return filesize($this->legacyBackup->id);
}
/**
* {@inheritdoc}
*/
public function getAge()
{
return time() - $this->getDate()->getTimestamp();
}
/**
* {@inheritdoc}
*/
public function getDate()
{
[$timestamp] = explode('-', $this->fileName);
return new DateTimeImmutable('@' . $timestamp);
}
}

View File

@@ -0,0 +1,29 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\Backup;
use PrestaShop\PrestaShop\Adapter\Entity\PrestaShopBackup;
use PrestaShop\PrestaShop\Core\Backup\BackupInterface;
use PrestaShop\PrestaShop\Core\Backup\Manager\BackupRemoverInterface;
/**
* Class BackupRemover deletes given backup.
*
* @internal
*/
final class BackupRemover implements BackupRemoverInterface
{
/**
* {@inheritdoc}
*/
public function remove(BackupInterface $backup): bool
{
$legacyBackup = new PrestaShopBackup($backup->getFileName());
return $legacyBackup->delete();
}
}

View File

@@ -0,0 +1,42 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\Backup;
use PrestaShop\PrestaShop\Adapter\Entity\PrestaShopBackup;
use PrestaShop\PrestaShop\Core\Backup\BackupCollection;
use PrestaShop\PrestaShop\Core\Backup\Repository\BackupRepositoryInterface;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Finder\SplFileInfo;
/**
* Class BackupRepository is responsible for providing available backups.
*
* @internal
*/
final class BackupRepository implements BackupRepositoryInterface
{
/**
* {@inheritdoc}
*/
public function retrieveBackups()
{
$backupFinder = (new Finder())
->files()
->in(PrestaShopBackup::getBackupPath())
->name('/^([_a-zA-Z0-9\-]*[\d]+-[a-z\d]+)\.sql(\.gz|\.bz2)?$/')
->depth(0);
$backups = new BackupCollection();
/** @var SplFileInfo $file */
foreach ($backupFinder as $file) {
$backups->add(new Backup($file->getFilename()));
}
return $backups;
}
}

View File

@@ -0,0 +1,42 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\Backup;
use PrestaShop\PrestaShop\Adapter\Entity\PrestaShopBackup;
use PrestaShop\PrestaShop\Core\Backup\BackupInterface;
use PrestaShop\PrestaShop\Core\Backup\Exception\BackupException;
use PrestaShop\PrestaShop\Core\Backup\Exception\DirectoryIsNotWritableException;
use PrestaShop\PrestaShop\Core\Backup\Manager\BackupCreatorInterface;
/**
* Class DatabaseBackupCreator is responsible for creating database backups.
*
* @internal
*/
final class DatabaseBackupCreator implements BackupCreatorInterface
{
/**
* {@inheritdoc}
*/
public function createBackup(): BackupInterface
{
ini_set('max_execution_time', '0');
if (!is_writable(PrestaShopBackup::getBackupPath())) {
throw new DirectoryIsNotWritableException('To create backup, its directory must be writable');
}
$legacyBackup = new PrestaShopBackup();
if (!$legacyBackup->add()) {
throw new BackupException('Failed to create backup');
}
$backupFilePathParts = explode(DIRECTORY_SEPARATOR, $legacyBackup->id);
return new Backup(end($backupFilePathParts));
}
}

View File

@@ -0,0 +1,87 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\BestSales;
use PrestaShop\PrestaShop\Core\Product\Search\ProductSearchContext;
use PrestaShop\PrestaShop\Core\Product\Search\ProductSearchProviderInterface;
use PrestaShop\PrestaShop\Core\Product\Search\ProductSearchQuery;
use PrestaShop\PrestaShop\Core\Product\Search\ProductSearchResult;
use PrestaShop\PrestaShop\Core\Product\Search\SortOrder;
use PrestaShop\PrestaShop\Core\Product\Search\SortOrdersCollection;
use ProductSale;
use Symfony\Contracts\Translation\TranslatorInterface;
class BestSalesProductSearchProvider implements ProductSearchProviderInterface
{
/**
* @var TranslatorInterface
*/
private $translator;
/**
* @var SortOrdersCollection
*/
private $sortOrdersCollection;
public function __construct(
TranslatorInterface $translator
) {
$this->translator = $translator;
$this->sortOrdersCollection = new SortOrdersCollection($this->translator);
}
/**
* @param ProductSearchContext $context
* @param ProductSearchQuery $query
*
* @return ProductSearchResult
*/
public function runQuery(
ProductSearchContext $context,
ProductSearchQuery $query
) {
// If provided sort order is unsupported random, we set a fallback
if ($query->getSortOrder()->isRandom()) {
$query->setSortOrder((new SortOrder('product', 'sales', 'desc'))->setLabel(
$this->translator->trans('Sales, highest to lowest', [], 'Shop.Theme.Catalog')
));
}
if (!$products = ProductSale::getBestSales(
$context->getIdLang(),
$query->getPage(),
$query->getResultsPerPage(),
$query->getSortOrder()->toLegacyOrderBy(),
$query->getSortOrder()->toLegacyOrderWay()
)) {
$products = [];
}
$count = (int) ProductSale::getNbSales();
$result = new ProductSearchResult();
if (!empty($products)) {
$result
->setProducts($products)
->setTotalProductsCount($count);
// We use default set of sort orders + option to sort by sales
$result->setAvailableSortOrders(
array_merge(
[
(new SortOrder('product', 'sales', 'desc'))->setLabel(
$this->translator->trans('Sales, highest to lowest', [], 'Shop.Theme.Catalog')
),
],
$this->sortOrdersCollection->getDefaults())
);
}
return $result;
}
}

View File

@@ -0,0 +1,51 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\Bundle;
use PrestaShopBundle\Console\PrestaShopApplication;
use PrestaShopException;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\HttpKernel\KernelInterface;
/**
* Class AssetsInstaller aim to install bundle assets into filesystem.
*
* @internal
*/
final class AssetsInstaller
{
public function installAssets(string $adminFolder): void
{
// We retrieve the kernel from the global scope
global $kernel;
if (!$kernel instanceof KernelInterface) {
throw new PrestaShopException('Kernel is not initialized. Cannot install assets.');
}
// We need to use the PrestaShopApplication to run the command
$application = new PrestaShopApplication($kernel);
$application->setAutoExit(false);
// Run the command to install bundles assets
// (for now! maybe we should use another way to install assets in the future)
$output = new BufferedOutput();
$errorCode = $application->run(new ArrayInput([
'command' => 'assets:install',
'target' => $adminFolder,
'--symlink' => true,
]), $output);
// If the command failed (!= 0), we throw an exception with the output of the command
if (0 !== $errorCode) {
throw new PrestaShopException(sprintf(
'Failed to install bundle assets: %s',
$output->fetch()
));
}
}
}

View File

@@ -0,0 +1,57 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\CMS;
use CMS;
/**
* Class CMSDataProvider provides CMS data using legacy code.
*/
class CMSDataProvider
{
/**
* Gets all CMS pages.
*
* @param int $languageId
*
* @return array
*/
public function getCMSPages($languageId = null)
{
return CMS::listCms($languageId);
}
/**
* Gets one CMS object by ID.
*
* @param int $cmsId
*
* @return CMS
*/
public function getCMSById($cmsId)
{
return new CMS($cmsId);
}
/**
* Gets CMS choices for choice type.
*
* @param int $languageId
*
* @return array
*/
public function getCMSChoices($languageId = null)
{
$choices = [];
foreach ($this->getCMSPages($languageId) as $cms) {
$choices[$cms['meta_title']] = (int) $cms['id_cms'];
}
return $choices;
}
}

View File

@@ -0,0 +1,67 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\CMS\Page\CommandHandler;
use CMS;
use CMSCategory;
use PrestaShop\PrestaShop\Adapter\Domain\AbstractObjectModelHandler;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\Exception\CmsPageException;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\Exception\CmsPageNotFoundException;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Exception\CmsPageCategoryException;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Exception\CmsPageCategoryNotFoundException;
use PrestaShopException;
/**
* Abstraction which holds all common functions required for cms page functionality.
*
* @internal
*/
abstract class AbstractCmsPageHandler extends AbstractObjectModelHandler
{
/**
* Gets cms object if it exists. If it does not exist it throws exceptions.
*
* @param int $cmsId
*
* @return CMS
*
* @throws CmsPageException
*/
protected function getCmsPageIfExistsById($cmsId)
{
try {
$cms = new CMS($cmsId);
if (0 >= $cms->id) {
throw new CmsPageNotFoundException(sprintf('Cms page with id "%s" not found', $cmsId));
}
} catch (PrestaShopException) {
throw new CmsPageException(sprintf('An error occurred when trying to get cms page with id %s', $cmsId));
}
return $cms;
}
/**
* Checks whether cms page category exists by provided id.
*
* @param int $cmsCategoryId
*
* @throws CmsPageCategoryException
*/
protected function assertCmsCategoryExists($cmsCategoryId)
{
try {
$cmsCategory = new CMSCategory($cmsCategoryId);
if (0 >= $cmsCategory->id) {
throw new CmsPageCategoryNotFoundException(sprintf('Cms page category with id "%s" not found', $cmsCategoryId));
}
} catch (PrestaShopException) {
throw new CmsPageCategoryException(sprintf('An error occurred when trying to get cms page category with id %s', $cmsCategoryId));
}
}
}

View File

@@ -0,0 +1,69 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\CMS\Page\CommandHandler;
use CMS;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\Command\AddCmsPageCommand;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\CommandHandler\AddCmsPageHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\Exception\CannotAddCmsPageException;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\Exception\CmsPageException;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\ValueObject\CmsPageId;
use PrestaShopException;
/**
* Handles AddCmsPageCommand using legacy object model
*/
#[AsCommandHandler]
final class AddCmsPageHandler extends AbstractCmsPageHandler implements AddCmsPageHandlerInterface
{
/**
* {@inheritdoc}
*/
public function handle(AddCmsPageCommand $command)
{
$cms = $this->createCmsFromCommand($command);
try {
if (false === $cms->validateFields(false) || false === $cms->validateFieldsLang(false)) {
throw new CmsPageException('Cms page contains invalid field values');
}
if (false === $cms->add()) {
throw new CannotAddCmsPageException('Failed to add cms page');
}
$this->associateWithShops($cms, $command->getShopAssociation());
} catch (PrestaShopException $e) {
throw new CmsPageException('An unexpected error occurred when adding cms page', 0, $e);
}
return new CmsPageId((int) $cms->id);
}
/**
* @param AddCmsPageCommand $command
*
* @return CMS
*/
protected function createCmsFromCommand(AddCmsPageCommand $command)
{
$cmsCategoryId = $command->getCmsPageCategory()->getValue();
$this->assertCmsCategoryExists($cmsCategoryId);
$cms = new CMS();
$cms->id_cms_category = $cmsCategoryId;
$cms->meta_title = $command->getLocalizedTitle();
$cms->head_seo_title = $command->getLocalizedMetaTitle();
$cms->meta_description = $command->getLocalizedMetaDescription();
$cms->link_rewrite = $command->getLocalizedFriendlyUrl();
$cms->content = $command->getLocalizedContent();
$cms->indexation = $command->isIndexedForSearch();
$cms->active = $command->isDisplayed();
return $cms;
}
}

View File

@@ -0,0 +1,41 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\CMS\Page\CommandHandler;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\Command\BulkDeleteCmsPageCommand;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\CommandHandler\BulkDeleteCmsPageHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\Exception\CannotDeleteCmsPageException;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\Exception\CmsPageException;
use PrestaShopException;
/**
* Deletes multiple cms pages
*/
#[AsCommandHandler]
final class BulkDeleteCmsPageHandler extends AbstractCmsPageHandler implements BulkDeleteCmsPageHandlerInterface
{
/**
* {@inheritdoc}
*
* @throws CmsPageException
*/
public function handle(BulkDeleteCmsPageCommand $command)
{
try {
foreach ($command->getCmsPages() as $cmsPageId) {
$cms = $this->getCmsPageIfExistsById($cmsPageId->getValue());
if (false === $cms->delete()) {
throw new CannotDeleteCmsPageException(sprintf('An error occurred when deleting cms page with id %s', $cmsPageId->getValue()), CannotDeleteCmsPageException::FAILED_BULK_DELETE);
}
}
} catch (PrestaShopException $exception) {
throw new CmsPageException('An unexpected error occurred when deleting cms page', 0, $exception);
}
}
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\CMS\Page\CommandHandler;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\Command\BulkDisableCmsPageCommand;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\CommandHandler\BulkDisableCmsPageHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\Exception\CannotDisableCmsPageException;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\Exception\CmsPageException;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\Exception\CmsPageNotFoundException;
use PrestaShopDatabaseException;
use PrestaShopException;
/**
* Disables multiple cms pages.
*/
#[AsCommandHandler]
final class BulkDisableCmsPageHandler extends AbstractCmsPageHandler implements BulkDisableCmsPageHandlerInterface
{
/**
* {@inheritdoc}
*
* @param BulkDisableCmsPageCommand $command
*
* @throws CannotDisableCmsPageException
* @throws CmsPageException
* @throws CmsPageNotFoundException
*/
public function handle(BulkDisableCmsPageCommand $command)
{
try {
$this->disableCmsPages($command);
} catch (PrestaShopException $exception) {
throw new CmsPageException('An error occurred when bulk disabling the cms pages', 0, $exception);
}
}
/**
* @param BulkDisableCmsPageCommand $command
*
* @throws CannotDisableCmsPageException
* @throws PrestaShopException
* @throws PrestaShopDatabaseException
* @throws CmsPageException
* @throws CmsPageNotFoundException
*/
private function disableCmsPages(BulkDisableCmsPageCommand $command)
{
foreach ($command->getCmsPages() as $cmsPage) {
$cms = $this->getCmsPageIfExistsById($cmsPage->getValue());
$cms->active = false;
if (false === $cms->update()) {
throw new CannotDisableCmsPageException(sprintf('Failed to disable cms page with id %s', $cmsPage->getValue()));
}
}
}
}

View File

@@ -0,0 +1,59 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\CMS\Page\CommandHandler;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\Command\BulkEnableCmsPageCommand;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\CommandHandler\BulkEnableCmsPageHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\Exception\CannotEnableCmsPageException;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\Exception\CmsPageException;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\Exception\CmsPageNotFoundException;
use PrestaShopDatabaseException;
use PrestaShopException;
/**
* Enables multiple cms pages.
*/
#[AsCommandHandler]
final class BulkEnableCmsPageHandler extends AbstractCmsPageHandler implements BulkEnableCmsPageHandlerInterface
{
/**
* {@inheritdoc}
*
* @throws CmsPageException
*/
public function handle(BulkEnableCmsPageCommand $command)
{
try {
$this->enableCmsPages($command);
} catch (PrestaShopException $exception) {
throw new CmsPageException('An error occurred when bulk enabling the cms pages', 0, $exception);
}
}
/**
* @param BulkEnableCmsPageCommand $command
*
* @throws CannotEnableCmsPageException
* @throws CmsPageException
* @throws PrestaShopException
* @throws PrestaShopDatabaseException
* @throws CmsPageNotFoundException
*/
private function enableCmsPages(BulkEnableCmsPageCommand $command)
{
foreach ($command->getCmsPages() as $cmsPage) {
$cms = $this->getCmsPageIfExistsById($cmsPage->getValue());
$cms->active = true;
if (false === $cms->update()) {
throw new CannotEnableCmsPageException(sprintf('Failed to enable cms page with id %s', $cmsPage->getValue()));
}
}
}
}

View File

@@ -0,0 +1,39 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\CMS\Page\CommandHandler;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\Command\DeleteCmsPageCommand;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\CommandHandler\DeleteCmsPageHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\Exception\CannotDeleteCmsPageException;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\Exception\CmsPageException;
use PrestaShopException;
/**
* Deletes given cms page.
*/
#[AsCommandHandler]
final class DeleteCmsPageHandler extends AbstractCmsPageHandler implements DeleteCmsPageHandlerInterface
{
/**
* {@inheritdoc}
*
* @throws CmsPageException
*/
public function handle(DeleteCmsPageCommand $command)
{
$cms = $this->getCmsPageIfExistsById($command->getCmsPageId()->getValue());
try {
if (false === $cms->delete()) {
throw new CannotDeleteCmsPageException(sprintf('An error occurred when deleting cms page with id %s', $command->getCmsPageId()->getValue()), CannotDeleteCmsPageException::FAILED_DELETE);
}
} catch (PrestaShopException $e) {
throw new CmsPageException(sprintf('An unexpected error occurred when deleting cms page with id %s', $command->getCmsPageId()->getValue()), 0, $e);
}
}
}

View File

@@ -0,0 +1,99 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\CMS\Page\CommandHandler;
use CMS;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\Command\EditCmsPageCommand;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\CommandHandler\EditCmsPageHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\Exception\CannotEditCmsPageException;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\Exception\CmsPageException;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\Exception\CmsPageNotFoundException;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Exception\CmsPageCategoryException;
use PrestaShopException;
/**
* Edits cms page
*/
#[AsCommandHandler]
final class EditCmsPageHandler extends AbstractCmsPageHandler implements EditCmsPageHandlerInterface
{
/**
* {@inheritdoc}
*
* @throws CmsPageException
* @throws CmsPageCategoryException
*/
public function handle(EditCmsPageCommand $command)
{
$cms = $this->createCmsFromCommand($command);
try {
if (false === $cms->validateFields(false) || false === $cms->validateFieldsLang(false)) {
throw new CmsPageException('Cms page contains invalid field values');
}
if (false === $cms->update()) {
throw new CannotEditCmsPageException(sprintf('Failed to update cms page with id %s', $command->getCmsPageId()->getValue()));
}
if (null !== $command->getShopAssociation()) {
$this->associateWithShops($cms, $command->getShopAssociation());
}
} catch (PrestaShopException $e) {
throw new CmsPageException(sprintf('An unexpected error occurred when editing cms page with id %s', $command->getCmsPageId()->getValue()), 0, $e);
}
}
/**
* @param EditCmsPageCommand $command
*
* @return CMS
*
* @throws CmsPageException
* @throws CmsPageNotFoundException
* @throws CmsPageCategoryException
*/
private function createCmsFromCommand(EditCmsPageCommand $command)
{
$cms = $this->getCmsPageIfExistsById($command->getCmsPageId()->getValue());
if (null !== $command->getCmsPageCategoryId()) {
$this->assertCmsCategoryExists($command->getCmsPageCategoryId()->getValue());
$cms->id_cms_category = $command->getCmsPageCategoryId()->getValue();
}
if (null !== $command->getLocalizedTitle()) {
$cms->meta_title = $command->getLocalizedTitle();
}
if (null !== $command->getLocalizedMetaTitle()) {
$cms->head_seo_title = $command->getLocalizedMetaTitle();
}
if (null !== $command->getLocalizedMetaDescription()) {
$cms->meta_description = $command->getLocalizedMetaDescription();
}
if (null !== $command->getLocalizedFriendlyUrl()) {
$cms->link_rewrite = $command->getLocalizedFriendlyUrl();
}
if (null !== $command->getLocalizedContent()) {
$cms->content = $command->getLocalizedContent();
}
if (null !== $command->isIndexedForSearch()) {
$cms->indexation = $command->isIndexedForSearch();
}
if (null !== $command->isDisplayed()) {
$cms->active = $command->isDisplayed();
}
return $cms;
}
}

View File

@@ -0,0 +1,39 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\CMS\Page\CommandHandler;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\Command\ToggleCmsPageStatusCommand;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\CommandHandler\ToggleCmsPageStatusHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\Exception\CannotToggleCmsPageException;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\Exception\CmsPageException;
use PrestaShopException;
/**
* Changes the status of cms page.
*/
#[AsCommandHandler]
final class ToggleCmsPageStatusHandler extends AbstractCmsPageHandler implements ToggleCmsPageStatusHandlerInterface
{
/**
* {@inheritdoc}
*
* @throws CmsPageException
*/
public function handle(ToggleCmsPageStatusCommand $command)
{
$cms = $this->getCmsPageIfExistsById($command->getCmsPageId()->getValue());
try {
if (false === $cms->toggleStatus()) {
throw new CannotToggleCmsPageException(sprintf('Failed to toggle cms page with id %s status', $command->getCmsPageId()->getValue()));
}
} catch (PrestaShopException) {
throw new CmsPageException(sprintf('An unexpected error occurred when toggling cms page with id %s status', $command->getCmsPageId()->getValue()));
}
}
}

View File

@@ -0,0 +1,37 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\CMS\Page\QueryHandler;
use PrestaShop\PrestaShop\Adapter\CMS\Page\CommandHandler\AbstractCmsPageHandler;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsQueryHandler;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\Exception\CmsPageException;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\Query\GetCmsCategoryIdForRedirection;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\QueryHandler\GetCmsCategoryIdHandlerForRedirectionInterface;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\ValueObject\CmsPageCategoryId;
/**
* This class is used for getting the id which is used later on to redirect to the right page after certain controller
* actions.
*/
#[AsQueryHandler]
final class GetCmsCategoryIdForRedirectionHandler extends AbstractCmsPageHandler implements GetCmsCategoryIdHandlerForRedirectionInterface
{
/**
* {@inheritdoc}
*/
public function handle(GetCmsCategoryIdForRedirection $query)
{
try {
$cms = $this->getCmsPageIfExistsById($query->getCmsPageId()->getValue());
$categoryId = (int) $cms->id_cms_category;
} catch (CmsPageException) {
$categoryId = CmsPageCategoryId::ROOT_CMS_PAGE_CATEGORY_ID;
}
return new CmsPageCategoryId($categoryId);
}
}

View File

@@ -0,0 +1,78 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\CMS\Page\QueryHandler;
use Link;
use PrestaShop\PrestaShop\Adapter\CMS\Page\CommandHandler\AbstractCmsPageHandler;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsQueryHandler;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\Exception\CmsPageException;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\Exception\CmsPageNotFoundException;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\Query\GetCmsPageForEditing;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\QueryHandler\GetCmsPageForEditingHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\CmsPage\QueryResult\EditableCmsPage;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Exception\CmsPageCategoryException;
use PrestaShopException;
/**
* Gets cms page for editing
*/
#[AsQueryHandler]
final class GetCmsPageForEditingHandler extends AbstractCmsPageHandler implements GetCmsPageForEditingHandlerInterface
{
/**
* @var Link
*/
private $link;
/**
* @var int
*/
private $langId;
/**
* @param Link $link
* @param int $langId
*/
public function __construct(Link $link, $langId)
{
$this->link = $link;
$this->langId = $langId;
}
/**
* @param GetCmsPageForEditing $query
*
* @return EditableCmsPage
*
* @throws CmsPageException
* @throws CmsPageCategoryException
* @throws CmsPageNotFoundException
*/
public function handle(GetCmsPageForEditing $query)
{
$cmsPageId = $query->getCmsPageId()->getValue();
$cms = $this->getCmsPageIfExistsById($cmsPageId);
try {
return new EditableCmsPage(
(int) $cms->id,
(int) $cms->id_cms_category,
$cms->meta_title,
$cms->head_seo_title,
$cms->meta_description,
$cms->link_rewrite,
$cms->content,
$cms->indexation,
$cms->active,
$cms->getAssociatedShops(),
$this->link->getCMSLink($cms, null, null, $this->langId)
);
} catch (PrestaShopException) {
throw new CmsPageException(sprintf('An error occurred when getting cms page for editing with id "%s"', $cmsPageId));
}
}
}

View File

@@ -0,0 +1,110 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\CMS\PageCategory;
use Db;
use DbQuery;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\ValueObject\CmsPageCategoryId;
use PrestaShopDatabaseException;
/**
* Class CategoriesProvider is responsible for providing cms page categories data.
*/
class CategoriesProvider
{
/**
* @var int
*/
private $contextLanguageId;
/**
* @var array
*/
private $contextShopIds;
/**
* @param int $contextLanguageId
* @param array $contextShopIds
*/
public function __construct(
$contextLanguageId,
array $contextShopIds
) {
$this->contextLanguageId = (int) $contextLanguageId;
$this->contextShopIds = array_map(function ($item) { return (int) $item; }, $contextShopIds);
}
/**
* Gets all nested cms page categories.
*
* @return array
*
* @throws PrestaShopDatabaseException
*/
public function getAllNestedCategories()
{
return $this->collectNestedCategoriesIdsAndNames(CmsPageCategoryId::ROOT_CMS_PAGE_CATEGORY_ID);
}
/**
* Gets recursive category ids and names
*
* @param int $cmsPageCategoryId
*
* @return array - [
* 'id_cms_category' => 1,
* 'name' => 'root category',
* 'children' => [...]
* ]
*
* @throws PrestaShopDatabaseException
*/
private function collectNestedCategoriesIdsAndNames($cmsPageCategoryId)
{
$mainCategoryQuery = new DbQuery();
$mainCategoryQuery
->select('c.`id_cms_category`, cl.`name`')
->from('cms_category', 'c')
->innerJoin(
'cms_category_lang',
'cl',
'cl.`id_cms_category` = c.`id_cms_category`'
)
->where('c.`id_cms_category` = ' . (int) $cmsPageCategoryId)
->where('cl.`id_lang` = ' . $this->contextLanguageId)
->where('cl.`id_shop` IN (' . implode(',', $this->contextShopIds) . ')')
->groupBy('c.`id_cms_category`')
;
$result = Db::getInstance()->getRow($mainCategoryQuery);
$categories = is_array($result) ? $result : [];
$childrenQuery = new DbQuery();
$childrenQuery
->select('c.`id_cms_category`, cl.`name`')
->from('cms_category', 'c')
->innerJoin(
'cms_category_lang',
'cl',
'cl.`id_cms_category` = c.`id_cms_category`'
)
->where('c.`id_parent` = ' . (int) $cmsPageCategoryId)
->where('cl.`id_lang` = ' . $this->contextLanguageId)
->where('cl.`id_shop` IN (' . implode(',', $this->contextShopIds) . ')')
->groupBy('c.`id_cms_category`')
;
$childCategories = Db::getInstance()->executeS($childrenQuery);
$childCategories = is_array($childCategories) ? $childCategories : [];
foreach ($childCategories as $childCategory) {
$categories['children'][] = $this->collectNestedCategoriesIdsAndNames($childCategory['id_cms_category']);
}
return $categories;
}
}

View File

@@ -0,0 +1,77 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\CMS\PageCategory\CommandHandler;
use PrestaShop\PrestaShop\Adapter\Domain\AbstractObjectModelHandler;
use PrestaShop\PrestaShop\Core\ConstraintValidator\Constraints\CleanHtml;
use PrestaShop\PrestaShop\Core\ConstraintValidator\Constraints\DefaultLanguage;
use PrestaShop\PrestaShop\Core\ConstraintValidator\Constraints\IsUrlRewrite;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Exception\CmsPageCategoryConstraintException;
use Symfony\Component\Validator\Validator\ValidatorInterface;
/**
* Holds the abstraction required for Adding or updating the cms page category.
*/
abstract class AbstractCmsPageCategoryHandler extends AbstractObjectModelHandler
{
/**
* @var ValidatorInterface
*/
private $validator;
/**
* @param ValidatorInterface $validator
*/
public function __construct(ValidatorInterface $validator)
{
$this->validator = $validator;
}
/**
* @param array $localisedTexts
*
* @return bool
*/
protected function assertHasDefaultLanguage(array $localisedTexts)
{
$errors = $this->validator->validate($localisedTexts, new DefaultLanguage());
return 0 === count($errors);
}
/**
* @param array $localisedUrls
*
* @throws CmsPageCategoryConstraintException
*/
protected function assertIsValidLinkRewrite(array $localisedUrls)
{
foreach ($localisedUrls as $localisedUrl) {
$errors = $this->validator->validate($localisedUrl, new IsUrlRewrite());
if (0 !== count($errors)) {
throw new CmsPageCategoryConstraintException(sprintf('Given friendly url "%s" is not valid for link rewrite', $localisedUrl), CmsPageCategoryConstraintException::INVALID_LINK_REWRITE);
}
}
}
/**
* @param array $localisedDescription
*
* @throws CmsPageCategoryConstraintException
*/
protected function assertDescriptionContainsCleanHtml(array $localisedDescription)
{
foreach ($localisedDescription as $description) {
$errors = $this->validator->validate($description, new CleanHtml());
if (0 !== count($errors)) {
throw new CmsPageCategoryConstraintException(sprintf('Given description "%s" contains javascript events or script tags', $description), CmsPageCategoryConstraintException::INVALID_DESCRIPTION);
}
}
}
}

View File

@@ -0,0 +1,65 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\CMS\PageCategory\CommandHandler;
use CMSCategory;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Command\AddCmsPageCategoryCommand;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\CommandHandler\AddCmsPageCategoryHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Exception\CannotAddCmsPageCategoryException;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Exception\CmsPageCategoryConstraintException;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Exception\CmsPageCategoryException;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\ValueObject\CmsPageCategoryId;
use PrestaShopException;
/**
* Adds cms page category
*/
#[AsCommandHandler]
final class AddCmsPageCategoryHandler extends AbstractCmsPageCategoryHandler implements AddCmsPageCategoryHandlerInterface
{
/**
* {@inheritdoc}
*
* @throws CmsPageCategoryException
*/
public function handle(AddCmsPageCategoryCommand $command)
{
if (!$this->assertHasDefaultLanguage($command->getLocalisedName())) {
throw new CmsPageCategoryConstraintException('Missing name in default language', CmsPageCategoryConstraintException::MISSING_DEFAULT_LANGUAGE_FOR_NAME);
}
if (!$this->assertHasDefaultLanguage($command->getLocalisedFriendlyUrl())) {
throw new CmsPageCategoryConstraintException('Missing friendly url in default language', CmsPageCategoryConstraintException::MISSING_DEFAULT_LANGUAGE_FOR_FRIENDLY_URL);
}
$this->assertIsValidLinkRewrite($command->getLocalisedFriendlyUrl());
$this->assertDescriptionContainsCleanHtml($command->getLocalisedDescription());
try {
$cmsPageCategory = new CMSCategory();
$cmsPageCategory->name = $command->getLocalisedName();
$cmsPageCategory->active = $command->isDisplayed();
$cmsPageCategory->id_parent = $command->getParentId()->getValue();
$cmsPageCategory->description = $command->getLocalisedDescription();
$cmsPageCategory->meta_title = $command->getLocalisedMetaTitle();
$cmsPageCategory->meta_description = $command->getLocalisedMetaDescription();
$cmsPageCategory->link_rewrite = $command->getLocalisedFriendlyUrl();
if (false === $cmsPageCategory->add()) {
throw new CannotAddCmsPageCategoryException('Failed to add cms page category');
}
$this->associateWithShops($cmsPageCategory, $command->getShopAssociation());
} catch (PrestaShopException $exception) {
throw new CmsPageCategoryException('An unexpected error occurred when adding cms page category', 0, $exception);
}
return new CmsPageCategoryId((int) $cmsPageCategory->id);
}
}

View File

@@ -0,0 +1,47 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\CMS\PageCategory\CommandHandler;
use CMSCategory;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Command\BulkDeleteCmsPageCategoryCommand;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\CommandHandler\BulkDeleteCmsPageCategoryHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Exception\CannotDeleteCmsPageCategoryException;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Exception\CmsPageCategoryException;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Exception\CmsPageCategoryNotFoundException;
use PrestaShopException;
/**
* Class BulkDeleteCmsPageCategoryHandler is responsible for deleting multiple cms page categories.
*/
#[AsCommandHandler]
final class BulkDeleteCmsPageCategoryHandler implements BulkDeleteCmsPageCategoryHandlerInterface
{
/**
* {@inheritdoc}
*
* @throws CmsPageCategoryException
*/
public function handle(BulkDeleteCmsPageCategoryCommand $command)
{
try {
foreach ($command->getCmsPageCategoryIds() as $cmsPageCategoryId) {
$entity = new CMSCategory($cmsPageCategoryId->getValue());
if (0 >= $entity->id) {
throw new CmsPageCategoryNotFoundException(sprintf('Cms category object with id "%s" has not been found for deleting.', $cmsPageCategoryId->getValue()));
}
if (false === $entity->delete()) {
throw new CannotDeleteCmsPageCategoryException(sprintf('Unable to delete cms category object with id "%s"', $cmsPageCategoryId->getValue()), CannotDeleteCmsPageCategoryException::FAILED_BULK_DELETE);
}
}
} catch (PrestaShopException $e) {
throw new CmsPageCategoryException('Unexpected error occurred when handling bulk delete cms category', 0, $e);
}
}
}

View File

@@ -0,0 +1,49 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\CMS\PageCategory\CommandHandler;
use CMSCategory;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Command\BulkDisableCmsPageCategoryCommand;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\CommandHandler\BulkDisableCmsPageCategoryHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Exception\CannotDisableCmsPageCategoryException;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Exception\CmsPageCategoryException;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Exception\CmsPageCategoryNotFoundException;
use PrestaShopException;
/**
* Class BulkDisableCmsPageCategoryHandler is responsible for deleting multiple cms page categories.
*/
#[AsCommandHandler]
final class BulkDisableCmsPageCategoryHandler implements BulkDisableCmsPageCategoryHandlerInterface
{
/**
* {@inheritdoc}
*
* @throws CmsPageCategoryException
*/
public function handle(BulkDisableCmsPageCategoryCommand $command)
{
try {
foreach ($command->getCmsPageCategoryIds() as $cmsPageCategoryId) {
$entity = new CMSCategory($cmsPageCategoryId->getValue());
if (0 >= $entity->id) {
throw new CmsPageCategoryNotFoundException(sprintf('Cms category object with id "%s" has not been found for disabling status.', $cmsPageCategoryId->getValue()));
}
$entity->active = false;
if (false === $entity->update()) {
throw new CannotDisableCmsPageCategoryException(sprintf('Unable to disable cms category object with id "%s"', $cmsPageCategoryId->getValue()));
}
}
} catch (PrestaShopException $e) {
throw new CmsPageCategoryException('Unexpected error occurred when handling bulk disable cms category', 0, $e);
}
}
}

View File

@@ -0,0 +1,49 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\CMS\PageCategory\CommandHandler;
use CMSCategory;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Command\BulkEnableCmsPageCategoryCommand;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\CommandHandler\BulkEnableCmsPageCategoryHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Exception\CannotEnableCmsPageCategoryException;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Exception\CmsPageCategoryException;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Exception\CmsPageCategoryNotFoundException;
use PrestaShopException;
/**
* Class BulkEnableCmsPageCategoryCommand is responsible for enabling cms category pages.
*/
#[AsCommandHandler]
final class BulkEnableCmsPageCategoryHandler implements BulkEnableCmsPageCategoryHandlerInterface
{
/**
* {@inheritdoc}
*
* @throws CmsPageCategoryException
*/
public function handle(BulkEnableCmsPageCategoryCommand $command)
{
try {
foreach ($command->getCmsPageCategoryIds() as $cmsPageCategoryId) {
$entity = new CMSCategory($cmsPageCategoryId->getValue());
if (0 >= $entity->id) {
throw new CmsPageCategoryNotFoundException(sprintf('Cms category object with id "%s" has not been found for enabling status.', $cmsPageCategoryId->getValue()));
}
$entity->active = true;
if (false === $entity->update()) {
throw new CannotEnableCmsPageCategoryException(sprintf('Unable to enable cms category object with id "%s"', $cmsPageCategoryId->getValue()));
}
}
} catch (PrestaShopException $e) {
throw new CmsPageCategoryException('Unexpected error occurred when handling bulk enable cms category', 0, $e);
}
}
}

View File

@@ -0,0 +1,45 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\CMS\PageCategory\CommandHandler;
use CMSCategory;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Command\DeleteCmsPageCategoryCommand;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\CommandHandler\DeleteCmsPageCategoryHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Exception\CannotDeleteCmsPageCategoryException;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Exception\CmsPageCategoryException;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Exception\CmsPageCategoryNotFoundException;
use PrestaShopException;
/**
* Class DeleteCmsPageCategoryHandler is responsible for deleting cms page category.
*/
#[AsCommandHandler]
final class DeleteCmsPageCategoryHandler implements DeleteCmsPageCategoryHandlerInterface
{
/**
* {@inheritdoc}
*
* @throws CmsPageCategoryException
*/
public function handle(DeleteCmsPageCategoryCommand $command)
{
try {
$entity = new CMSCategory($command->getCmsPageCategoryId()->getValue());
if (0 >= $entity->id) {
throw new CmsPageCategoryNotFoundException(sprintf('Cms category object with id "%s" has not been found for deletion.', $command->getCmsPageCategoryId()->getValue()));
}
if (false === $entity->delete()) {
throw new CannotDeleteCmsPageCategoryException(sprintf('Unable to delete cms category object with id "%s"', $command->getCmsPageCategoryId()->getValue()), CannotDeleteCmsPageCategoryException::FAILED_DELETE);
}
} catch (PrestaShopException $exception) {
throw new CmsPageCategoryException(sprintf('An error occurred when deleting cms category object with id "%s"', $command->getCmsPageCategoryId()->getValue()), 0, $exception);
}
}
}

View File

@@ -0,0 +1,106 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\CMS\PageCategory\CommandHandler;
use CMSCategory;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Command\EditCmsPageCategoryCommand;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\CommandHandler\EditCmsPageCategoryHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Exception\CannotUpdateCmsPageCategoryException;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Exception\CmsPageCategoryConstraintException;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Exception\CmsPageCategoryException;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Exception\CmsPageCategoryNotFoundException;
use PrestaShopException;
/**
* Edits cms page category.
*/
#[AsCommandHandler]
final class EditCmsPageCategoryHandler extends AbstractCmsPageCategoryHandler implements EditCmsPageCategoryHandlerInterface
{
/**
* {@inheritdoc}
*
* @throws CmsPageCategoryException
*/
public function handle(EditCmsPageCategoryCommand $command)
{
try {
$cmsPageCategory = new CMSCategory($command->getCmsPageCategoryId()->getValue());
if (0 >= $cmsPageCategory->id) {
throw new CmsPageCategoryNotFoundException(sprintf('Unable to find cms page category with id "%s"', $cmsPageCategory->id));
}
if (null !== $command->getLocalisedName()) {
if (!$this->assertHasDefaultLanguage($command->getLocalisedName())) {
throw new CmsPageCategoryConstraintException('Missing name in default language', CmsPageCategoryConstraintException::MISSING_DEFAULT_LANGUAGE_FOR_NAME);
}
$cmsPageCategory->name = $command->getLocalisedName();
}
if (null !== $command->isDisplayed()) {
$cmsPageCategory->active = $command->isDisplayed();
}
if (null !== $command->getParentId()) {
$this->assertCmsCategoryCanBeMovedToParent(
$command->getCmsPageCategoryId()->getValue(),
$command->getParentId()->getValue()
);
$cmsPageCategory->id_parent = $command->getParentId()->getValue();
}
if (null !== $command->getLocalisedDescription()) {
$this->assertDescriptionContainsCleanHtml($command->getLocalisedDescription());
$cmsPageCategory->description = $command->getLocalisedDescription();
}
if (null !== $command->getLocalisedMetaTitle()) {
$cmsPageCategory->meta_title = $command->getLocalisedMetaTitle();
}
if (null !== $command->getLocalisedMetaDescription()) {
$cmsPageCategory->meta_description = $command->getLocalisedMetaDescription();
}
if (null !== $command->getLocalisedFriendlyUrl()) {
if (!$this->assertHasDefaultLanguage($command->getLocalisedFriendlyUrl())) {
throw new CmsPageCategoryConstraintException('Missing friendly url in default language', CmsPageCategoryConstraintException::MISSING_DEFAULT_LANGUAGE_FOR_FRIENDLY_URL);
}
$this->assertIsValidLinkRewrite($command->getLocalisedFriendlyUrl());
$cmsPageCategory->link_rewrite = $command->getLocalisedFriendlyUrl();
}
if (false === $cmsPageCategory->update()) {
throw new CannotUpdateCmsPageCategoryException('Failed to update cms page category');
}
if (null !== $command->getShopAssociation()) {
$this->associateWithShops($cmsPageCategory, $command->getShopAssociation());
}
} catch (PrestaShopException $exception) {
throw new CmsPageCategoryException('An unexpected error occurred when updating cms page category', 0, $exception);
}
}
/**
* Adds if the current category is not being moved to the same category or its own child.
*
* @param int $cmsCategoryId
* @param int $cmsCategoryParentId
*
* @throws CmsPageCategoryConstraintException
*/
private function assertCmsCategoryCanBeMovedToParent($cmsCategoryId, $cmsCategoryParentId)
{
if (!CMSCategory::checkBeforeMove($cmsCategoryId, $cmsCategoryParentId)) {
throw new CmsPageCategoryConstraintException(sprintf('Unable to move cms category "%s" to parent category "%s"', $cmsCategoryId, $cmsCategoryParentId), CmsPageCategoryConstraintException::CANNOT_MOVE_CATEGORY_TO_PARENT);
}
}
}

View File

@@ -0,0 +1,45 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\CMS\PageCategory\CommandHandler;
use CMSCategory;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsCommandHandler;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Command\ToggleCmsPageCategoryStatusCommand;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\CommandHandler\ToggleCmsPageCategoryStatusHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Exception\CannotToggleCmsPageCategoryStatusException;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Exception\CmsPageCategoryException;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Exception\CmsPageCategoryNotFoundException;
use PrestaShopException;
/**
* Class ToggleCmsPageCategoryStatusHandler is responsible for turning on and off cms page category status.
*/
#[AsCommandHandler]
final class ToggleCmsPageCategoryStatusHandler implements ToggleCmsPageCategoryStatusHandlerInterface
{
/**
* {@inheritdoc}
*
* @throws CmsPageCategoryException
*/
public function handle(ToggleCmsPageCategoryStatusCommand $command)
{
try {
$entity = new CMSCategory($command->getCmsPageCategoryId()->getValue());
if (0 >= $entity->id) {
throw new CmsPageCategoryNotFoundException(sprintf('Cms category object with id "%s" has not been found for status changing.', $command->getCmsPageCategoryId()->getValue()));
}
if (false === $entity->toggleStatus()) {
throw new CannotToggleCmsPageCategoryStatusException(sprintf('Unable to toggle cms category with id "%s"', $command->getCmsPageCategoryId()->getValue()));
}
} catch (PrestaShopException $exception) {
throw new CmsPageCategoryException(sprintf('An error occurred when toggling status for cms page object with id "%s"', $command->getCmsPageCategoryId()->getValue()), 0, $exception);
}
}
}

View File

@@ -0,0 +1,93 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\CMS\PageCategory\QueryHandler;
use CMSCategory;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsQueryHandler;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Exception\CmsPageCategoryException;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Exception\CmsPageCategoryNotFoundException;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Query\GetCmsPageCategoriesForBreadcrumb;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\QueryHandler\GetCmsPageCategoriesForBreadcrumbHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\QueryResult\Breadcrumb;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\QueryResult\BreadcrumbItem;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\ValueObject\CmsPageCategoryId;
use PrestaShopException;
/**
* Class GetCmsPageCategoriesForBreadcrumbHandler is responsible for providing required data for displaying cms page category
* breadcrumbs.
*/
#[AsQueryHandler]
final class GetCmsPageCategoriesForBreadcrumbHandler implements GetCmsPageCategoriesForBreadcrumbHandlerInterface
{
/**
* @var int
*/
private $contextLanguageId;
/**
* @param int $contextLanguageId
*/
public function __construct($contextLanguageId)
{
$this->contextLanguageId = $contextLanguageId;
}
/**
* {@inheritdoc}
*
* @throws CmsPageCategoryException
*/
public function handle(GetCmsPageCategoriesForBreadcrumb $query)
{
try {
$currentCategory = new CMSCategory(
$query->getCurrentCategoryId()->getValue(),
$this->contextLanguageId
);
if (0 >= $currentCategory->id) {
throw new CmsPageCategoryNotFoundException(sprintf('Cms category object with id "%s" has not been found for retrieving breadcrumbs', $query->getCurrentCategoryId()->getValue()));
}
$rootCategory = new CMSCategory(
CmsPageCategoryId::ROOT_CMS_PAGE_CATEGORY_ID,
$this->contextLanguageId
);
} catch (PrestaShopException $exception) {
throw new CmsPageCategoryException(sprintf('An error occurred when finding cms category object with id "%s" or root category by id "%s"', $query->getCurrentCategoryId()->getValue(), CmsPageCategoryId::ROOT_CMS_PAGE_CATEGORY_ID), 0, $exception);
}
$rootCategoryData = [
'id_cms_category' => CmsPageCategoryId::ROOT_CMS_PAGE_CATEGORY_ID,
'name' => $rootCategory->name,
];
if (CmsPageCategoryId::ROOT_CMS_PAGE_CATEGORY_ID === $query->getCurrentCategoryId()->getValue()) {
return new Breadcrumb([
new BreadcrumbItem(
(int) $rootCategoryData['id_cms_category'],
$rootCategoryData['name']
),
]);
}
$parentCategories = $currentCategory->getParentsCategories($this->contextLanguageId);
$parentCategories[] = $rootCategoryData;
$parentCategories = array_reverse($parentCategories);
$categories = [];
foreach ($parentCategories as $category) {
$categories[] = new BreadcrumbItem(
(int) $category['id_cms_category'],
$category['name']
);
}
return new Breadcrumb($categories);
}
}

View File

@@ -0,0 +1,56 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\CMS\PageCategory\QueryHandler;
use CMSCategory;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsQueryHandler;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Exception\CmsPageCategoryException;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Exception\CmsPageCategoryNotFoundException;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Query\GetCmsPageCategoryForEditing;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\QueryHandler\GetCmsPageCategoryForEditingHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\QueryResult\EditableCmsPageCategory;
use PrestaShopException;
/**
* Class GetCmsPageCategoryForEditingHandler is responsible for retrieving cms page category form data.
*
* @internal
*/
#[AsQueryHandler]
final class GetCmsPageCategoryForEditingHandler implements GetCmsPageCategoryForEditingHandlerInterface
{
/**
* {@inheritdoc}
*
* @throws CmsPageCategoryException
*/
public function handle(GetCmsPageCategoryForEditing $query)
{
try {
$cmsPageCategory = new CMSCategory($query->getCmsPageCategoryId()->getValue());
if (0 >= $cmsPageCategory->id) {
throw new CmsPageCategoryNotFoundException(sprintf('Cms category object with id "%s" has not been found', $query->getCmsPageCategoryId()->getValue()));
}
$shopIds = $cmsPageCategory->getAssociatedShops();
} catch (PrestaShopException $exception) {
throw new CmsPageCategoryException(sprintf('An error occurred when retrieving cms page category data with id %s', $query->getCmsPageCategoryId()->getValue()), 0, $exception);
}
return new EditableCmsPageCategory(
$cmsPageCategory->name,
$cmsPageCategory->active,
(int) $cmsPageCategory->id_parent,
$cmsPageCategory->description,
$cmsPageCategory->meta_description,
$cmsPageCategory->meta_title,
$cmsPageCategory->link_rewrite,
$shopIds
);
}
}

View File

@@ -0,0 +1,69 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
namespace PrestaShop\PrestaShop\Adapter\CMS\PageCategory\QueryHandler;
use CMSCategory;
use PrestaShop\PrestaShop\Core\CommandBus\Attributes\AsQueryHandler;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\Query\GetCmsPageCategoryNameForListing;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\QueryHandler\GetCmsPageCategoryNameForListingHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\ValueObject\CmsPageCategoryId;
use Symfony\Component\HttpFoundation\RequestStack;
/**
* Gets name by cms category which are used for display in cms listing.
*/
#[AsQueryHandler]
final class GetCmsPageCategoryNameForListingHandler implements GetCmsPageCategoryNameForListingHandlerInterface
{
/**
* @var int
*/
private $contextLanguageId;
/**
* @var RequestStack
*/
private $requestStack;
/**
* @param int $contextLanguageId
* @param RequestStack $requestStack
*/
public function __construct(
$contextLanguageId,
RequestStack $requestStack
) {
$this->contextLanguageId = $contextLanguageId;
$this->requestStack = $requestStack;
}
/**
* {@inheritdoc}
*/
public function handle(GetCmsPageCategoryNameForListing $query)
{
$cmsCategory = new CMSCategory($this->getCmsCategoryIdFromRequest());
return isset($cmsCategory->name[$this->contextLanguageId]) ? $cmsCategory->name[$this->contextLanguageId] : '';
}
/**
* Gets id from request or fall-backs to the default one if not found.
*
* @return int
*/
private function getCmsCategoryIdFromRequest()
{
$currentRequest = $this->requestStack->getCurrentRequest();
$categoryIdFromRequest = null;
if (null !== $currentRequest) {
$categoryIdFromRequest = $currentRequest->query->getInt('id_cms_category');
}
return $categoryIdFromRequest ?: CmsPageCategoryId::ROOT_CMS_PAGE_CATEGORY_ID;
}
}

Some files were not shown because too many files have changed in this diff Show More