Skip to content

Commit 9a62410

Browse files
authored
IBX-8562: Fixed flooding content attributes table with duplicates
For more details see https://issues.ibexa.co/browse/IBX-8562 and #409 Key changes: * Fixed replacing only field of Content Type with virtual field
1 parent 17e78be commit 9a62410

File tree

6 files changed

+87
-25
lines changed

6 files changed

+87
-25
lines changed

eZ/Publish/Core/Persistence/Legacy/Content/FieldHandler.php

+8-1
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,13 @@ public function updateFields(Content $content, UpdateStruct $updateStruct, Type
358358
// also update copied field data
359359
// Register for processing after all given fields are updated
360360
$nonTranslatableCopiesUpdateSet[$fieldDefinition->id][] = $languageCode;
361+
} elseif (isset($contentFieldMap[$fieldDefinition->id][$languageCode])) {
362+
$field = clone $contentFieldMap[$fieldDefinition->id][$languageCode];
363+
$field->versionNo = $content->versionInfo->versionNo;
364+
// Persist virtual field
365+
if (null === $field->id) {
366+
$this->createNewField($field, $content);
367+
}
361368
}
362369

363370
// If no above conditions were met - do nothing
@@ -417,7 +424,7 @@ protected function updateCopiedField(Field $field, Field $updateField, Field $or
417424
* @param \eZ\Publish\SPI\Persistence\Content\Field[] $fields
418425
* @param array $languageCodes
419426
*
420-
* @return \eZ\Publish\SPI\Persistence\Content\Field[][]
427+
* @return array<int, array<string, \eZ\Publish\SPI\Persistence\Content\Field>>
421428
*/
422429
protected function getFieldMap(array $fields, &$languageCodes = null)
423430
{

eZ/Publish/Core/Persistence/Legacy/Content/Handler.php

+24-2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use eZ\Publish\SPI\Persistence\Content\CreateStruct;
1616
use eZ\Publish\SPI\Persistence\Content\Field;
1717
use eZ\Publish\SPI\Persistence\Content\Handler as BaseContentHandler;
18+
use eZ\Publish\SPI\Persistence\Content\Language\Handler as LanguageHandler;
1819
use eZ\Publish\SPI\Persistence\Content\MetadataUpdateStruct;
1920
use eZ\Publish\SPI\Persistence\Content\Relation\CreateStruct as RelationCreateStruct;
2021
use eZ\Publish\SPI\Persistence\Content\Type\Handler as ContentTypeHandler;
@@ -84,6 +85,11 @@ class Handler implements BaseContentHandler
8485
*/
8586
protected $treeHandler;
8687

88+
/**
89+
* @var \eZ\Publish\SPI\Persistence\Content\Language\Handler
90+
*/
91+
protected $languageHandler;
92+
8793
/** @var \Psr\Log\LoggerInterface */
8894
private $logger;
8995

@@ -109,6 +115,7 @@ public function __construct(
109115
UrlAliasGateway $urlAliasGateway,
110116
ContentTypeHandler $contentTypeHandler,
111117
TreeHandler $treeHandler,
118+
LanguageHandler $languageHandler,
112119
LoggerInterface $logger = null
113120
) {
114121
$this->contentGateway = $contentGateway;
@@ -119,6 +126,7 @@ public function __construct(
119126
$this->urlAliasGateway = $urlAliasGateway;
120127
$this->contentTypeHandler = $contentTypeHandler;
121128
$this->treeHandler = $treeHandler;
129+
$this->languageHandler = $languageHandler;
122130
$this->logger = null !== $logger ? $logger : new NullLogger();
123131
}
124132

@@ -255,6 +263,8 @@ public function publish($contentId, $versionNo, MetadataUpdateStruct $metaDataUp
255263
* @param string|null $languageCode
256264
*
257265
* @return \eZ\Publish\SPI\Persistence\Content
266+
*
267+
* @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException
258268
*/
259269
public function createDraftFromVersion($contentId, $srcVersion, $userId, ?string $languageCode = null)
260270
{
@@ -275,6 +285,14 @@ public function createDraftFromVersion($contentId, $srcVersion, $userId, ?string
275285
// Clone fields from previous version and append them to the new one
276286
$this->fieldHandler->createExistingFieldsInNewVersion($content);
277287

288+
// Persist virtual fields
289+
$contentType = $this->contentTypeHandler->load($content->versionInfo->contentInfo->contentTypeId);
290+
$this->fieldHandler->updateFields($content, new UpdateStruct([
291+
'initialLanguageId' => $this->languageHandler->loadByLanguageCode(
292+
$content->versionInfo->initialLanguageCode
293+
)->id,
294+
]), $contentType);
295+
278296
// Create relations for new version
279297
$relations = $this->contentGateway->loadRelations($contentId, $srcVersion);
280298
foreach ($relations as $relation) {
@@ -320,7 +338,9 @@ public function load($id, $version = null, array $translations = null)
320338
$this->contentGateway->loadVersionedNameData([[
321339
'id' => $id,
322340
'version' => $rows[0]['ezcontentobject_version_version'],
323-
]])
341+
]]),
342+
'ezcontentobject_',
343+
$translations
324344
);
325345
$content = $contentObjects[0];
326346
unset($rows, $contentObjects);
@@ -371,7 +391,9 @@ public function loadContentList(array $contentIds, array $translations = null):
371391
try {
372392
$contentList = $this->mapper->extractContentFromRows(
373393
$contentItemsRow,
374-
$contentItemNameData[$contentId]
394+
$contentItemNameData[$contentId],
395+
'ezcontentobject_',
396+
$translations
375397
);
376398
$contentItems[$contentId] = $contentList[0];
377399
} catch (Exception $e) {

eZ/Publish/Core/Persistence/Legacy/Content/Mapper.php

+16-7
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ public function convertToStorageValue(Field $field)
220220
* @param array<array<string, scalar>> $rows
221221
* @param array<array<string, scalar>> $nameRows
222222
* @param string $prefix
223+
* @param array<string>|null $translations
223224
*
224225
* @return \eZ\Publish\SPI\Persistence\Content[]
225226
*
@@ -228,7 +229,8 @@ public function convertToStorageValue(Field $field)
228229
public function extractContentFromRows(
229230
array $rows,
230231
array $nameRows,
231-
string $prefix = 'ezcontentobject_'
232+
string $prefix = 'ezcontentobject_',
233+
?array $translations = null
232234
): array {
233235
$versionedNameData = [];
234236

@@ -245,7 +247,8 @@ public function extractContentFromRows(
245247

246248
$fieldDefinitions = $this->loadCachedVersionFieldDefinitionsPerLanguage(
247249
$rows,
248-
$prefix
250+
$prefix,
251+
$translations
249252
);
250253

251254
foreach ($rows as $row) {
@@ -313,9 +316,10 @@ private function buildContentObjects(
313316
$content->versionInfo = $versionInfo;
314317
$content->versionInfo->names = $names;
315318
$content->versionInfo->contentInfo = $contentInfo;
316-
$content->fields = array_values($fields[$contentId][$versionId]);
319+
$content->fields = array_values($fields[$contentId][$versionId] ?? []);
320+
321+
$missingVersionFieldDefinitions = $missingFieldDefinitions[$contentId][$versionId] ?? [];
317322

318-
$missingVersionFieldDefinitions = $missingFieldDefinitions[$contentId][$versionId];
319323
foreach ($missingVersionFieldDefinitions as $languageCode => $versionFieldDefinitions) {
320324
foreach ($versionFieldDefinitions as $fieldDefinition) {
321325
$event = $this->eventDispatcher->dispatch(
@@ -341,13 +345,16 @@ private function buildContentObjects(
341345
}
342346

343347
/**
348+
* @param string[]|null $translations
349+
*
344350
* @phpstan-return TVersionedLanguageFieldDefinitionsMap
345351
*
346352
* @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException
347353
*/
348354
private function loadCachedVersionFieldDefinitionsPerLanguage(
349355
array $rows,
350-
string $prefix
356+
string $prefix,
357+
?array $translations = null
351358
): array {
352359
$fieldDefinitions = [];
353360
$contentTypes = [];
@@ -363,12 +370,14 @@ private function loadCachedVersionFieldDefinitionsPerLanguage(
363370
continue;
364371
}
365372

366-
$languageCodes = $this->extractLanguageCodesFromMask($languageMask, $allLanguages);
373+
$allLanguagesCodes = $this->extractLanguageCodesFromMask($languageMask, $allLanguages);
374+
$languageCodes = empty($translations) ? $allLanguagesCodes : array_intersect($translations, $allLanguagesCodes);
367375
$contentTypes[$contentTypeId] = $contentTypes[$contentTypeId] ?? $this->contentTypeHandler->load($contentTypeId);
368376
$contentType = $contentTypes[$contentTypeId];
369377
foreach ($contentType->fieldDefinitions as $fieldDefinition) {
370378
foreach ($languageCodes as $languageCode) {
371-
$id = $fieldDefinition->id;
379+
$id = (int)$fieldDefinition->id;
380+
$languageCode = (string)$languageCode;
372381
$fieldDefinitions[$contentId][$versionId][$languageCode][$id] = $fieldDefinition;
373382
}
374383
}

eZ/Publish/Core/Persistence/Legacy/Tests/Content/ContentHandlerTest.php

+30-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use eZ\Publish\Core\Persistence\Legacy\Content\FieldHandler;
1212
use eZ\Publish\Core\Persistence\Legacy\Content\Gateway as ContentGateway;
1313
use eZ\Publish\Core\Persistence\Legacy\Content\Handler;
14+
use eZ\Publish\Core\Persistence\Legacy\Content\Language\Handler as LanguageHandler;
1415
use eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway as LocationGateway;
1516
use eZ\Publish\Core\Persistence\Legacy\Content\Mapper;
1617
use eZ\Publish\Core\Persistence\Legacy\Content\TreeHandler;
@@ -108,6 +109,11 @@ class ContentHandlerTest extends TestCase
108109
*/
109110
protected $contentTypeHandlerMock;
110111

112+
/**
113+
* @var \PHPUnit\Framework\MockObject\MockObject&\eZ\Publish\Core\Persistence\Legacy\Content\Language\Handler
114+
*/
115+
private $languageHandlerMock;
116+
111117
/**
112118
* @covers \eZ\Publish\Core\Persistence\Legacy\Content\Handler::create
113119
*
@@ -384,6 +390,8 @@ public function testCreateDraftFromVersion()
384390
$mapperMock = $this->getMapperMock();
385391
$gatewayMock = $this->getGatewayMock();
386392
$fieldHandlerMock = $this->getFieldHandlerMock();
393+
$languageHandlerMock = $this->getLanguageHandlerMock();
394+
$contentTypeHandlerMock = $this->getContentTypeHandlerMock();
387395

388396
$handler->expects($this->once())
389397
->method('load')
@@ -402,11 +410,18 @@ public function testCreateDraftFromVersion()
402410
[
403411
'names' => [],
404412
'versionNo' => 3,
413+
'contentInfo' => new ContentInfo(),
405414
]
406415
)
407416
)
408417
);
409418

419+
$languageHandlerMock->method('loadByLanguageCode')
420+
->willReturn(new Content\Language());
421+
422+
$contentTypeHandlerMock->method('load')
423+
->willReturn(new Type());
424+
410425
$gatewayMock->expects($this->once())
411426
->method('insertVersion')
412427
->with(
@@ -1538,7 +1553,8 @@ protected function getContentHandler()
15381553
$this->getSlugConverterMock(),
15391554
$this->getUrlAliasGatewayMock(),
15401555
$this->getContentTypeHandlerMock(),
1541-
$this->getTreeHandlerMock()
1556+
$this->getTreeHandlerMock(),
1557+
$this->getLanguageHandlerMock(),
15421558
);
15431559
}
15441560

@@ -1566,6 +1582,7 @@ protected function getPartlyMockedHandler(array $methods)
15661582
$this->getUrlAliasGatewayMock(),
15671583
$this->getContentTypeHandlerMock(),
15681584
$this->getTreeHandlerMock(),
1585+
$this->getLanguageHandlerMock(),
15691586
]
15701587
)
15711588
->getMock();
@@ -1599,6 +1616,18 @@ protected function getContentTypeHandlerMock()
15991616
return $this->contentTypeHandlerMock;
16001617
}
16011618

1619+
/**
1620+
* @return \PHPUnit\Framework\MockObject\MockObject&\eZ\Publish\Core\Persistence\Legacy\Content\Language\Handler
1621+
*/
1622+
protected function getLanguageHandlerMock(): LanguageHandler
1623+
{
1624+
if (!isset($this->languageHandlerMock)) {
1625+
$this->languageHandlerMock = $this->createMock(LanguageHandler::class);
1626+
}
1627+
1628+
return $this->languageHandlerMock;
1629+
}
1630+
16021631
/**
16031632
* Returns a FieldHandler mock.
16041633
*

eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldHandlerTest.php

+8-14
Original file line numberDiff line numberDiff line change
@@ -633,7 +633,7 @@ public function assertUpdateFieldsForInitialLanguage($storageHandlerUpdatesField
633633
$callNo = 0;
634634
$fieldValue = new FieldValue();
635635
$fieldsToCopy = [];
636-
foreach ([1, 2, 3] as $fieldDefinitionId) {
636+
foreach ([1, 2, 3] as $id => $fieldDefinitionId) {
637637
$field = new Field(
638638
[
639639
'fieldDefinitionId' => $fieldDefinitionId,
@@ -646,6 +646,7 @@ public function assertUpdateFieldsForInitialLanguage($storageHandlerUpdatesField
646646
// These fields are copied from main language
647647
if ($fieldDefinitionId == 2 || $fieldDefinitionId == 3) {
648648
$originalField = clone $field;
649+
$originalField->id = $fieldDefinitionId;
649650
$originalField->languageCode = 'eng-GB';
650651
$fieldsToCopy[] = [
651652
'copy' => clone $field,
@@ -845,20 +846,13 @@ protected function getContentSingleLanguageFixture()
845846
$field->value = new FieldValue();
846847
$field->languageCode = 'eng-GB';
847848

848-
$firstField = clone $field;
849-
$firstField->fieldDefinitionId = 1;
850-
851-
$secondField = clone $field;
852-
$secondField->fieldDefinitionId = 2;
853-
854-
$thirdField = clone $field;
855-
$thirdField->fieldDefinitionId = 3;
849+
foreach ([1, 2, 3] as $id) {
850+
$contentField = clone $field;
851+
$contentField->id = $id;
852+
$contentField->fieldDefinitionId = $id;
856853

857-
$content->fields = [
858-
$firstField,
859-
$secondField,
860-
$thirdField,
861-
];
854+
$content->fields[] = $contentField;
855+
}
862856

863857
return $content;
864858
}

eZ/Publish/Core/settings/storage_engines/legacy/content.yml

+1
Original file line numberDiff line numberDiff line change
@@ -68,5 +68,6 @@ services:
6868
- "@ezpublish.persistence.legacy.url_alias.gateway"
6969
- "@ezpublish.spi.persistence.legacy.content_type.handler"
7070
- "@ezpublish.persistence.legacy.tree_handler"
71+
- "@ezpublish.spi.persistence.legacy.language.handler"
7172
- "@logger"
7273
lazy: true

0 commit comments

Comments
 (0)