init
This commit is contained in:
@@ -0,0 +1,212 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Esun\Translation\Model\Entity;
|
||||
|
||||
use Esun\Translation\Api\TranslatableEntityInterface;
|
||||
use Esun\Translation\Model\Html\HtmlTranslationValidator;
|
||||
use Magento\Cms\Api\BlockRepositoryInterface;
|
||||
use Magento\Cms\Model\Block;
|
||||
use Magento\Cms\Model\BlockFactory;
|
||||
use Magento\Cms\Model\ResourceModel\Block\CollectionFactory;
|
||||
|
||||
class CmsBlockEntity implements TranslatableEntityInterface
|
||||
{
|
||||
private const FIELDS = [
|
||||
'title' => ['label' => 'Block Title', 'is_html' => false],
|
||||
'content' => ['label' => 'Content', 'is_html' => true],
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
private readonly BlockRepositoryInterface $blockRepository,
|
||||
private readonly BlockFactory $blockFactory,
|
||||
private readonly CollectionFactory $collectionFactory,
|
||||
private readonly HtmlTranslationValidator $htmlValidator
|
||||
) {
|
||||
}
|
||||
|
||||
public function getType(): string
|
||||
{
|
||||
return 'cms_block';
|
||||
}
|
||||
|
||||
public function getLabel(): string
|
||||
{
|
||||
return 'CMS Block';
|
||||
}
|
||||
|
||||
public function getTitle(int $entityId): string
|
||||
{
|
||||
return (string)$this->getBlock($entityId)->getTitle();
|
||||
}
|
||||
|
||||
public function getEditUrlPath(): string
|
||||
{
|
||||
return 'cms/block/edit';
|
||||
}
|
||||
|
||||
public function getEditIdParam(): string
|
||||
{
|
||||
return 'block_id';
|
||||
}
|
||||
|
||||
public function getFields(int $entityId, array $targetStoreIds = []): array
|
||||
{
|
||||
$source = $this->getBlock($entityId);
|
||||
$target = $this->findTargetBlock($source, $targetStoreIds);
|
||||
$fields = [];
|
||||
|
||||
foreach (self::FIELDS as $code => $metadata) {
|
||||
$fields[] = [
|
||||
'code' => $code,
|
||||
'label' => $metadata['label'],
|
||||
'source_value' => (string)($source->getData($code) ?? ''),
|
||||
'target_value' => $target ? (string)($target->getData($code) ?? '') : '',
|
||||
'is_html' => (bool)$metadata['is_html'],
|
||||
];
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
public function apply(int $entityId, array $storeIds, array $fields): array
|
||||
{
|
||||
$source = $this->getBlock($entityId);
|
||||
$fields = $this->filterAndValidateFields($fields);
|
||||
if ($fields === []) {
|
||||
throw new \RuntimeException('No translatable CMS block fields were provided.');
|
||||
}
|
||||
|
||||
$updated = 0;
|
||||
$created = 0;
|
||||
foreach (array_unique(array_map('intval', $storeIds)) as $storeId) {
|
||||
if ($storeId <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$block = $this->findByIdentifierAndStore((string)$source->getIdentifier(), $storeId);
|
||||
if ($block) {
|
||||
$this->applyFields($block, $fields);
|
||||
$block->setStores([$storeId]);
|
||||
$this->blockRepository->save($block);
|
||||
$updated++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (in_array(0, array_map('intval', (array)$source->getStores()), true)) {
|
||||
throw new \RuntimeException(
|
||||
'This CMS block is assigned to All Store Views. Create store-specific blocks first or remove All Store Views before creating localized copies.'
|
||||
);
|
||||
}
|
||||
|
||||
$copy = $this->cloneForStore($source, $storeId);
|
||||
$this->applyFields($copy, $fields);
|
||||
$this->blockRepository->save($copy);
|
||||
$created++;
|
||||
}
|
||||
|
||||
if ($updated + $created === 0) {
|
||||
throw new \RuntimeException('No valid target store views were provided.');
|
||||
}
|
||||
|
||||
return [
|
||||
'updated' => $updated,
|
||||
'created' => $created,
|
||||
'message' => __('CMS block translations applied. Updated: %1, Created: %2.', $updated, $created)->render(),
|
||||
];
|
||||
}
|
||||
|
||||
private function getBlock(int $blockId): Block
|
||||
{
|
||||
$block = $this->blockRepository->getById($blockId);
|
||||
if (!$block instanceof Block) {
|
||||
throw new \RuntimeException('CMS block model type is not supported.');
|
||||
}
|
||||
|
||||
return $block;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $targetStoreIds
|
||||
*/
|
||||
private function findTargetBlock(Block $source, array $targetStoreIds): ?Block
|
||||
{
|
||||
foreach ($targetStoreIds as $storeId) {
|
||||
$block = $this->findByIdentifierAndStore((string)$source->getIdentifier(), (int)$storeId);
|
||||
if ($block && (int)$block->getId() !== (int)$source->getId()) {
|
||||
return $block;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function findByIdentifierAndStore(string $identifier, int $storeId): ?Block
|
||||
{
|
||||
if ($storeId <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$collection = $this->collectionFactory->create();
|
||||
$collection->addFieldToFilter('identifier', $identifier);
|
||||
$collection->addStoreFilter($storeId, false);
|
||||
$collection->setPageSize(1);
|
||||
$block = $collection->getFirstItem();
|
||||
|
||||
return $block && $block->getId() ? $block : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $fields
|
||||
* @return array<string, string>
|
||||
*/
|
||||
private function filterAndValidateFields(array $fields): array
|
||||
{
|
||||
$result = [];
|
||||
foreach ($fields as $code => $value) {
|
||||
$code = (string)$code;
|
||||
if (!isset(self::FIELDS[$code])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$value = (string)$value;
|
||||
if ((bool)self::FIELDS[$code]['is_html']) {
|
||||
$errors = $this->htmlValidator->validate($value);
|
||||
if ($errors !== []) {
|
||||
throw new \RuntimeException(sprintf(
|
||||
'HTML for "%s" is invalid: %s',
|
||||
$code,
|
||||
implode('; ', $errors)
|
||||
));
|
||||
}
|
||||
}
|
||||
$result[$code] = $value;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string> $fields
|
||||
*/
|
||||
private function applyFields(Block $block, array $fields): void
|
||||
{
|
||||
foreach ($fields as $code => $value) {
|
||||
$block->setData($code, $value);
|
||||
}
|
||||
}
|
||||
|
||||
private function cloneForStore(Block $source, int $storeId): Block
|
||||
{
|
||||
$data = $source->getData();
|
||||
unset($data['block_id'], $data['store_id']);
|
||||
|
||||
$copy = $this->blockFactory->create();
|
||||
$copy->setData($data);
|
||||
$copy->setStores([$storeId]);
|
||||
$copy->setData('store_id', [$storeId]);
|
||||
|
||||
return $copy;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user