Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: PoC #295

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 1 addition & 21 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -8860,16 +8860,6 @@ parameters:
count: 1
path: src/lib/FieldType/Author/Value.php

-
message: "#^Cannot access offset 'fileSize' on array\\|void\\.$#"
count: 1
path: src/lib/FieldType/BinaryBase/BinaryBaseStorage.php

-
message: "#^Cannot access offset 'id' on array\\|void\\.$#"
count: 4
path: src/lib/FieldType/BinaryBase/BinaryBaseStorage.php

-
message: "#^Class Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage extends generic class Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\GatewayBasedStorage but does not specify its types\\: T$#"
count: 1
Expand Down Expand Up @@ -15585,11 +15575,6 @@ parameters:
count: 1
path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/ImageConverter.php

-
message: "#^Cannot call method addAttribute\\(\\) on SimpleXMLElement\\|null\\.$#"
count: 1
path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/ImageConverter.php

-
message: "#^Cannot call method getAttribute\\(\\) on DOMElement\\|null\\.$#"
count: 6
Expand Down Expand Up @@ -18900,11 +18885,6 @@ parameters:
count: 1
path: src/lib/Persistence/Legacy/Setting/Gateway/ExceptionConversion.php

-
message: "#^Cannot call method fetch\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#"
count: 1
path: src/lib/Persistence/Legacy/SharedGateway/DatabasePlatform/SqliteGateway.php

-
message: "#^Argument of an invalid type array\\<Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\SharedGateway\\\\Gateway\\>\\|iterable supplied for foreach, only iterables are supported\\.$#"
count: 1
Expand Down Expand Up @@ -29377,7 +29357,7 @@ parameters:

-
message: "#^Parameter \\#2 \\$fieldDefinition of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentTypeService\\:\\:removeFieldDefinition\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null given\\.$#"
count: 7
count: 6
path: tests/integration/Core/Repository/ContentTypeServiceTest.php

-
Expand Down
4 changes: 4 additions & 0 deletions src/lib/Persistence/Legacy/Content/FieldHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,10 @@ protected function getEmptyField(FieldDefinition $fieldDefinition, $languageCode
public function createExistingFieldsInNewVersion(Content $content)
{
foreach ($content->fields as $field) {
if ($field->id === null) {
// Virtual field with default value, skip creating field as it has no id
continue;
}
$this->createExistingFieldInNewVersion($field, $content);
}
}
Expand Down
63 changes: 55 additions & 8 deletions src/lib/Persistence/Legacy/Content/Mapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
use Ibexa\Contracts\Core\Persistence\Content\Language\Handler as LanguageHandler;
use Ibexa\Contracts\Core\Persistence\Content\Relation;
use Ibexa\Contracts\Core\Persistence\Content\Relation\CreateStruct as RelationCreateStruct;
use Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition;
use Ibexa\Contracts\Core\Persistence\Content\Type\Handler as ContentTypeHandler;
use Ibexa\Contracts\Core\Persistence\Content\VersionInfo;
use Ibexa\Core\Base\Exceptions\NotFoundException;
use Ibexa\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry as Registry;
Expand All @@ -39,16 +41,22 @@ class Mapper
*/
protected $languageHandler;

private ContentTypeHandler $contentTypeHandler;

/**
* Creates a new mapper.
*
* @param \Ibexa\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry $converterRegistry
* @param \Ibexa\Contracts\Core\Persistence\Content\Language\Handler $languageHandler
*/
public function __construct(Registry $converterRegistry, LanguageHandler $languageHandler)
{
public function __construct(
Registry $converterRegistry,
LanguageHandler $languageHandler,
ContentTypeHandler $contentTypeHandler
) {
$this->converterRegistry = $converterRegistry;
$this->languageHandler = $languageHandler;
$this->contentTypeHandler = $contentTypeHandler;
}

/**
Expand Down Expand Up @@ -182,33 +190,50 @@ public function convertToStorageValue(Field $field)
public function extractContentFromRows(array $rows, array $nameRows, $prefix = 'ezcontentobject_')
{
$versionedNameData = [];
$languageCodes = [];

foreach ($nameRows as $row) {
$contentId = (int)$row['ezcontentobject_name_contentobject_id'];
$versionNo = (int)$row['ezcontentobject_name_content_version'];
$versionedNameData[$contentId][$versionNo][$row['ezcontentobject_name_content_translation']] = $row['ezcontentobject_name_name'];
$contentId = (int)$row["{$prefix}name_contentobject_id"];
$versionNo = (int)$row["{$prefix}name_content_version"];
$versionedNameData[$contentId][$versionNo][$row["{$prefix}name_content_translation"]] = $row["{$prefix}name_name"];
$languageCodes[] = $row["{$prefix}name_content_translation"];
}

$contentInfos = [];
$versionInfos = [];
$fields = [];
$fieldDefinitions = [];

foreach ($rows as $row) {
$contentId = (int)$row["{$prefix}id"];
$versionId = (int)$row["{$prefix}version_id"];
$contentTypeId = (int)$row["{$prefix}contentclass_id"];
if (!isset($contentInfos[$contentId])) {
$contentInfos[$contentId] = $this->extractContentInfoFromRow($row, $prefix);
}
if (!isset($versionInfos[$contentId])) {
$versionInfos[$contentId] = [];
}

$versionId = (int)$row['ezcontentobject_version_id'];
if (!isset($fieldDefinitions[$contentId][$versionId])) {
$contentType = $this->contentTypeHandler->load($contentTypeId);
foreach ($contentType->fieldDefinitions as $fieldDefinition) {
foreach ($languageCodes as $languageCode) {
$fieldDefinitions[$contentId][$versionId][$languageCode][$fieldDefinition->id] = $fieldDefinition;
}
}
}

if (!isset($versionInfos[$contentId][$versionId])) {
$versionInfos[$contentId][$versionId] = $this->extractVersionInfoFromRow($row);
}

$fieldId = (int)$row['ezcontentobject_attribute_id'];
if (!isset($fields[$contentId][$versionId][$fieldId])) {
$fieldId = (int)$row["{$prefix}attribute_id"];
$fieldDefinitionId = (int)$row["{$prefix}attribute_contentclassattribute_id"];
$languageCode = $row["{$prefix}attribute_language_code"];
if (!isset($fields[$contentId][$versionId][$fieldId]) && isset($fieldDefinitions[$contentId][$versionId][$languageCode][$fieldDefinitionId])) {
$fields[$contentId][$versionId][$fieldId] = $this->extractFieldFromRow($row);
unset($fieldDefinitions[$contentId][$versionId][$languageCode][$fieldDefinitionId]);
}
}

Expand All @@ -227,6 +252,17 @@ public function extractContentFromRows(array $rows, array $nameRows, $prefix = '
$content->versionInfo->names = $names;
$content->versionInfo->contentInfo = $contentInfo;
$content->fields = array_values($fields[$contentId][$versionId]);

/** @var string $languageCode */
foreach ($fieldDefinitions[$contentId][$versionId] as $languageCode => $versionFieldDefinitions) {
foreach ($versionFieldDefinitions as $fieldDefinition) {
$content->fields[] = $this->createEmptyField(
$fieldDefinition,
$languageCode
);
}
}

$results[] = $content;
}
}
Expand Down Expand Up @@ -578,6 +614,17 @@ public function createRelationFromCreateStruct(RelationCreateStruct $struct)

return $relation;
}

private function createEmptyField(FieldDefinition $fieldDefinition, string $languageCode): Field
{
$field = new Field();
$field->fieldDefinitionId = $fieldDefinition->id;
$field->type = $fieldDefinition->fieldType;
$field->value = $fieldDefinition->defaultValue;
$field->languageCode = $languageCode;

return $field;
}
}

class_alias(Mapper::class, 'eZ\Publish\Core\Persistence\Legacy\Content\Mapper');
2 changes: 1 addition & 1 deletion src/lib/Persistence/Legacy/Content/StorageHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public function copyFieldData(VersionInfo $versionInfo, Field $field, Field $ori
public function getFieldData(VersionInfo $versionInfo, Field $field)
{
$storage = $this->storageRegistry->getStorage($field->type);
if ($storage->hasFieldData()) {
if ($storage->hasFieldData() && $field->id !== null) {
$storage->getFieldData($versionInfo, $field, $this->context);
}
}
Expand Down
1 change: 0 additions & 1 deletion src/lib/Persistence/Legacy/Content/Type/Handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -632,7 +632,6 @@ public function publish($contentTypeId)

try {
$fromType = $this->load($contentTypeId, Type::STATUS_DEFINED);
$this->updateHandler->updateContentObjects($fromType, $toType);
$this->updateHandler->deleteOldType($fromType);
} catch (Exception\TypeNotFound $e) {
// If no old type is found, no updates are necessary to it
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
namespace Ibexa\Core\Persistence\Legacy\Content\Type\Update\Handler;

use Ibexa\Contracts\Core\Persistence\Content\Type;
use Ibexa\Core\Persistence\Legacy\Content\Type\ContentUpdater;
use Ibexa\Core\Persistence\Legacy\Content\Type\Gateway;
use Ibexa\Core\Persistence\Legacy\Content\Type\Update\Handler;

Expand All @@ -23,21 +22,14 @@ final class DoctrineDatabase extends Handler
/** @var \Ibexa\Core\Persistence\Legacy\Content\Type\Gateway */
protected $contentTypeGateway;

/** @var \Ibexa\Core\Persistence\Legacy\Content\Type\ContentUpdater */
protected $contentUpdater;

public function __construct(Gateway $contentTypeGateway, ContentUpdater $contentUpdater)
public function __construct(Gateway $contentTypeGateway)
{
$this->contentTypeGateway = $contentTypeGateway;
$this->contentUpdater = $contentUpdater;
}

public function updateContentObjects(Type $fromType, Type $toType): void
{
$this->contentUpdater->applyUpdates(
$fromType->id,
$this->contentUpdater->determineActions($fromType, $toType)
);
// Do nothing, content objects are no longer updated
}

public function deleteOldType(Type $fromType): void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@

namespace Ibexa\Core\Persistence\Legacy\SharedGateway\DatabasePlatform;

use Doctrine\DBAL\Connection;
use Doctrine\DBAL\FetchMode;
use Ibexa\Core\Base\Exceptions\DatabaseException;
use Ibexa\Core\Persistence\Legacy\SharedGateway\Gateway;

Expand All @@ -20,39 +18,19 @@ final class SqliteGateway implements Gateway
*/
private const FATAL_ERROR_CODE = 7;

/** @var \Doctrine\DBAL\Connection */
private $connection;

/** @var \Doctrine\DBAL\Platforms\AbstractPlatform */
private $databasePlatform;

/** @var int[] */
private $lastInsertedIds = [];

/**
* @throws \Doctrine\DBAL\DBALException
*/
public function __construct(Connection $connection)
{
$this->connection = $connection;
$this->databasePlatform = $connection->getDatabasePlatform();
}

public function getColumnNextIntegerValue(
string $tableName,
string $columnName,
string $sequenceName
): ?int {
$query = $this->connection->createQueryBuilder();
$query
->select($this->databasePlatform->getMaxExpression($columnName))
->from($tableName);

$lastId = (int)$query->execute()->fetch(FetchMode::COLUMN);
$lastId = $this->lastInsertedIds[$sequenceName] ?? 0;
$nextId = (int)hrtime(true);

$this->lastInsertedIds[$sequenceName] = $lastId + 1;

return $this->lastInsertedIds[$sequenceName];
// $lastId === $nextId shouldn't happen using high-resolution time, but better safe than sorry
return $this->lastInsertedIds[$sequenceName] = $lastId === $nextId ? $nextId + 1 : $nextId;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ services:
arguments:
- '@Ibexa\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry'
- '@ibexa.spi.persistence.legacy.language.handler'
- '@ibexa.spi.persistence.legacy.content_type.handler'

Ibexa\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase.inner:
class: Ibexa\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ services:
class: Ibexa\Core\Persistence\Legacy\Content\Type\Update\Handler\DoctrineDatabase
arguments:
- '@ibexa.persistence.legacy.content_type.gateway'
- '@Ibexa\Core\Persistence\Legacy\Content\Type\ContentUpdater'

ibexa.persistence.legacy.content_type.update_handler:
alias: Ibexa\Core\Persistence\Legacy\Content\Type\Update\Handler\DoctrineDatabase
Expand Down
60 changes: 0 additions & 60 deletions tests/integration/Core/Repository/ContentTypeServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2085,66 +2085,6 @@ public function testRemoveFieldDefinitionRemovesFieldFromContentRemoved($data)
);
}

/**
* @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::removeFieldDefinition()
*/
public function testRemoveFieldDefinitionRemovesOrphanedRelations(): void
{
$repository = $this->getRepository();

$contentTypeService = $repository->getContentTypeService();
$contentService = $repository->getContentService();

// Create ContentType
$contentTypeDraft = $this->createContentTypeDraft([$this->getRelationFieldDefinition()]);
$contentTypeService->publishContentTypeDraft($contentTypeDraft);
$publishedType = $contentTypeService->loadContentType($contentTypeDraft->id);

// Create Content with Relation
$contentDraft = $this->createContentDraft();
$publishedVersion = $contentService->publishVersion($contentDraft->versionInfo);

$newDraft = $contentService->createContentDraft($publishedVersion->contentInfo);
$updateStruct = $contentService->newContentUpdateStruct();
$updateStruct->setField('relation', 14, 'eng-US');
$contentDraft = $contentService->updateContent($newDraft->versionInfo, $updateStruct);
$publishedContent = $contentService->publishVersion($contentDraft->versionInfo);

// Remove field definition from ContentType
$contentTypeDraft = $contentTypeService->createContentTypeDraft($publishedType);
$relationField = $contentTypeDraft->getFieldDefinition('relation');
$contentTypeService->removeFieldDefinition($contentTypeDraft, $relationField);
$contentTypeService->publishContentTypeDraft($contentTypeDraft);

// Load Content
$content = $contentService->loadContent($publishedContent->contentInfo->id);

$this->assertCount(0, $contentService->loadRelations($content->versionInfo));
}

private function getRelationFieldDefinition(): FieldDefinitionCreateStruct
{
$repository = $this->getRepository();

$contentTypeService = $repository->getContentTypeService();

$relationFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct(
'relation',
'ezobjectrelation'
);
$relationFieldCreate->names = ['eng-US' => 'Relation'];
$relationFieldCreate->descriptions = ['eng-US' => 'Relation to any Content'];
$relationFieldCreate->fieldGroup = 'blog-content';
$relationFieldCreate->position = 3;
$relationFieldCreate->isTranslatable = false;
$relationFieldCreate->isRequired = false;
$relationFieldCreate->isInfoCollector = false;
$relationFieldCreate->validatorConfiguration = [];
$relationFieldCreate->isSearchable = false;

return $relationFieldCreate;
}

/**
* Test for the addFieldDefinition() method.
*
Expand Down
Loading
Loading