Skip to content

Commit

Permalink
[!!!][TASK] Integrate Record object into ContentBlockData
Browse files Browse the repository at this point in the history
  • Loading branch information
nhovratov committed Aug 4, 2024
1 parent e9a4f9a commit e2c003c
Show file tree
Hide file tree
Showing 9 changed files with 220 additions and 151 deletions.
151 changes: 127 additions & 24 deletions Classes/DataProcessing/ContentBlockData.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@

namespace TYPO3\CMS\ContentBlocks\DataProcessing;

use TYPO3\CMS\Core\Domain\RawRecord;
use TYPO3\CMS\Core\Domain\Record;
use TYPO3\CMS\Core\Domain\Record\ComputedProperties;
use TYPO3\CMS\Core\Domain\Record\LanguageInfo;
use TYPO3\CMS\Core\Domain\Record\SystemProperties;
use TYPO3\CMS\Core\Domain\Record\VersionInfo;
use TYPO3\CMS\Core\Domain\RecordInterface;

/**
* This class represents the `data` object inside the Fluid template for Content Blocks.
*
Expand Down Expand Up @@ -44,47 +52,142 @@
* - {data.originalUid}
* - {data.originalPid}
*
* To access the raw database record use:
* - {data._raw.some_field}
*
* @internal This is not public TYPO3 PHP API. Only to be used inside of Fluid templates by accessing as variable.
*/
final class ContentBlockData extends \stdClass
final class ContentBlockData implements \ArrayAccess, RecordInterface
{
/**
* This is a hint for f:debug users.
*/
public string $_debug_hint = 'To access data under `_processed` you must omit the key: {data.identifier}';

public function __construct(
private string $_name = '',
private array $_raw = [],
protected ?Record $_record = null,
protected string $_name = '',
/** @var array<string, RelationGrid>|array<string, RenderedGridItem[]> */
private array $_grids = [],
private array $_processed = [],
protected array $_grids = [],
protected array $_processed = [],
) {}

public function __get(string $name = ''): mixed
public function getUid(): int
{
return $this->_record->getUid();
}

public function getPid(): int
{
return $this->_record->getPid();
}

public function getFullType(): string
{
return $this->_record->getFullType();
}

public function getRecordType(): ?string
{
if ($name === '_name') {
return $this->_record->getRecordType();
}

public function getMainType(): string
{
return $this->_record->getMainType();
}

public function toArray(bool $includeSpecialProperties = false): array
{
return $this->_record->toArray($includeSpecialProperties);
}

public function offsetExists(mixed $offset): bool
{
if ($offset === '_name') {
return true;
}
if ($offset === '_raw') {
return true;
}
if ($offset === '_grids') {
return true;
}
if (array_key_exists($offset, $this->_processed)) {
return true;
}
return $this->_record->offsetExists($offset);
}

public function offsetGet(mixed $offset): mixed
{
if ($offset === '_name') {
return $this->_name;
}
if ($name === '_raw') {
return $this->_raw;
if ($offset === '_raw') {
return $this->toArray(true);
}
if ($name === '_grids') {
if ($offset === '_grids') {
return $this->_grids;
}
if (array_key_exists($name, $this->_processed)) {
return $this->_processed[$name];
if (array_key_exists($offset, $this->_processed)) {
return $this->_processed[$offset];
}
return null;
return $this->_record->offsetGet($offset);
}

public function offsetSet(mixed $offset, mixed $value): void
{
$this->_processed[$offset] = $value;
}

public function offsetUnset(mixed $offset): void
{
unset($this->_processed[$offset]);
}

public function getVersionInfo(): ?VersionInfo
{
return $this->_record->getVersionInfo();
}

public function getLanguageInfo(): ?LanguageInfo
{
return $this->_record->getLanguageInfo();
}

public function getLanguageId(): ?int
{
return $this->_record->getLanguageId();
}

public function getSystemProperties(): ?SystemProperties
{
return $this->_record->getSystemProperties();
}

public function getComputedProperties(): ComputedProperties
{
return $this->_record->getComputedProperties();
}

public function getRawRecord(): RawRecord
{
return $this->_record->getRawRecord();
}

public function getOverlaidUid(): int
{
return $this->_record->getOverlaidUid();
}

public function get_Name(): string
{
return $this->_name;
}

public function get_Grids(): array
{
return $this->_grids;
}

public function override(ContentBlockData $contentBlockData): void
{
foreach (get_object_vars($contentBlockData) as $var => $value) {
$this->$var = $contentBlockData->$var;
}
$this->_record = $contentBlockData->_record;
$this->_name = $contentBlockData->_name;
$this->_grids = $contentBlockData->_grids;
$this->_processed = $contentBlockData->_processed;
}
}
80 changes: 12 additions & 68 deletions Classes/DataProcessing/ContentBlockDataDecorator.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
use TYPO3\CMS\ContentBlocks\Definition\TableDefinitionCollection;
use TYPO3\CMS\ContentBlocks\Definition\TcaFieldDefinition;
use TYPO3\CMS\ContentBlocks\FieldType\FieldType;
use TYPO3\CMS\Core\Schema\TcaSchemaFactory;
use TYPO3\CMS\Core\Domain\RecordFactory;

/**
* @internal Not part of TYPO3's public API.
Expand All @@ -35,10 +35,10 @@ final class ContentBlockDataDecorator

public function __construct(
private readonly TableDefinitionCollection $tableDefinitionCollection,
private readonly TcaSchemaFactory $tcaSchemaFactory,
private readonly ContentBlockDataDecoratorSession $contentBlockDataDecoratorSession,
private readonly GridProcessor $gridProcessor,
private readonly ContentObjectProcessor $contentObjectProcessor,
private readonly RecordFactory $recordFactory,
) {}

public function setRequest(ServerRequestInterface $request): void
Expand All @@ -55,8 +55,9 @@ public function decorate(
): ContentBlockData {
$identifier = $this->getRecordIdentifier($resolvedRelation->table, $resolvedRelation->raw);
$this->contentBlockDataDecoratorSession->addContentBlockData($identifier, new ContentBlockData());
$record = $this->recordFactory->createFromDatabaseRow($resolvedRelation->table, $resolvedRelation->raw);
$resolvedContentBlockDataRelation = new ResolvedContentBlockDataRelation();
$resolvedContentBlockDataRelation->raw = $resolvedRelation->raw;
$resolvedContentBlockDataRelation->record = $record;
$resolvedContentBlockDataRelation->resolved = $resolvedRelation->resolved;
$contentBlockData = $this->buildContentBlockDataObjectRecursive(
$contentTypeDefinition,
Expand Down Expand Up @@ -102,9 +103,6 @@ private function buildContentBlockDataObjectRecursive(
$resolvedRelation->resolved = $processedContentBlockData;
$contentBlockDataObject = $this->buildContentBlockDataObject(
$resolvedRelation,
$tableDefinition->getTable(),
$tableDefinition->getTypeField(),
$contentTypeDefinition->getTypeName(),
$contentTypeDefinition->getName(),
$grids,
);
Expand Down Expand Up @@ -243,7 +241,8 @@ private function transformSingleRelation(
): ContentBlockData {
$contentBlockRelation = new ResolvedContentBlockDataRelation();
$foreignTable = $item->table;
$contentBlockRelation->raw = $item->raw;
$record = $this->recordFactory->createFromDatabaseRow($item->table, $item->raw);
$contentBlockRelation->record = $record;
$contentBlockRelation->resolved = $item->resolved;
$hasTableDefinition = $this->tableDefinitionCollection->hasTable($foreignTable);
$collectionTableDefinition = null;
Expand All @@ -252,10 +251,10 @@ private function transformSingleRelation(
}
$typeDefinition = null;
if ($hasTableDefinition) {
$typeDefinition = ContentTypeResolver::resolve($collectionTableDefinition, $contentBlockRelation->raw);
$typeDefinition = ContentTypeResolver::resolve($collectionTableDefinition, $contentBlockRelation->record->toArray());
}
if ($collectionTableDefinition !== null && $typeDefinition !== null) {
$identifier = $this->getRecordIdentifier($foreignTable, $contentBlockRelation->raw);
$identifier = $this->getRecordIdentifier($foreignTable, $contentBlockRelation->record->toArray());
if ($this->contentBlockDataDecoratorSession->hasContentBlockData($identifier)) {
$contentBlockData = $this->contentBlockDataDecoratorSession->getContentBlockData($identifier);
return $contentBlockData;
Expand All @@ -271,7 +270,7 @@ private function transformSingleRelation(
$this->contentBlockDataDecoratorSession->setContentBlockData($identifier, $contentBlockData);
return $contentBlockData;
}
$contentBlockData = $this->buildFakeContentBlockDataObject($foreignTable, $contentBlockRelation);
$contentBlockData = $this->buildFakeContentBlockDataObject($contentBlockRelation);
return $contentBlockData;
}

Expand All @@ -280,79 +279,24 @@ private function transformSingleRelation(
*/
private function buildContentBlockDataObject(
ResolvedContentBlockDataRelation $resolvedRelation,
string $table,
?string $typeField,
string|int $typeName,
string $name = '',
array $grids = [],
): ContentBlockData {
$rawData = $resolvedRelation->raw;
$resolvedData = $resolvedRelation->resolved;
$baseData = [
'uid' => $rawData['uid'],
'pid' => $rawData['pid'],
'tableName' => $table,
'typeName' => $typeName,
];
// Duplicates typeName, but needed for Fluid Styled Content layout integration.
if ($typeField !== null) {
$baseData[$typeField] = $typeName;
}
if (array_key_exists('sys_language_uid', $rawData)) {
$baseData['languageId'] = $rawData['sys_language_uid'];
}
if (array_key_exists('tstamp', $rawData)) {
$baseData['updateDate'] = $rawData['tstamp'];
}
if (array_key_exists('crdate', $rawData)) {
$baseData['creationDate'] = $rawData['crdate'];
}
$baseData = $this->enrichBaseDataWithComputedProperties($baseData, $rawData);
$contentBlockDataArray = $baseData + $resolvedData;
$contentBlockData = new ContentBlockData($name, $rawData, $grids, $contentBlockDataArray);

// Add dynamic fields so that Fluid can detect them with `property_exists()`.
foreach ($baseData as $key => $baseDataItem) {
$contentBlockData->$key = $baseDataItem;
}
foreach ($resolvedData as $key => $processedContentBlockDataItem) {
$contentBlockData->$key = $processedContentBlockDataItem;
}
$contentBlockData = new ContentBlockData($resolvedRelation->record, $name, $grids, $resolvedData);
return $contentBlockData;
}

private function enrichBaseDataWithComputedProperties(array $baseData, array $data): array
{
$computedProperties = [
'localizedUid' => '_LOCALIZED_UID',
'originalUid' => '_ORIG_uid',
'originalPid' => '_ORIG_pid',
];
$baseDataWithComputedProperties = $baseData;
foreach ($computedProperties as $key => $computedProperty) {
if (array_key_exists($computedProperty, $data)) {
$baseDataWithComputedProperties[$key] = $data[$computedProperty];
}
}
return $baseDataWithComputedProperties;
}

/**
* If the record is not defined by Content Blocks, we build a fake
* Content Block data object for consistent usage.
*/
private function buildFakeContentBlockDataObject(string $table, ResolvedContentBlockDataRelation $resolvedRelation): ContentBlockData
private function buildFakeContentBlockDataObject(ResolvedContentBlockDataRelation $resolvedRelation): ContentBlockData
{
$tcaSchema = $this->tcaSchemaFactory->get($table);
$typeField = $tcaSchema->getSubSchemaDivisorField();
$typeFieldIdentifier = $typeField?->getName();
$typeName = $typeField !== null ? $resolvedRelation->raw[$typeField->getName()] : '1';
$typeName = $resolvedRelation->record->getRecordType() !== null ? $resolvedRelation->record->getRecordType() : '1';
$fakeName = 'core/' . $typeName;
$contentBlockDataObject = $this->buildContentBlockDataObject(
$resolvedRelation,
$table,
$typeFieldIdentifier,
$typeName,
$fakeName,
);
return $contentBlockDataObject;
Expand Down
6 changes: 3 additions & 3 deletions Classes/DataProcessing/ContentObjectProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,9 @@ public function processContentObject(ContentBlockData $contentBlockData, Rendere
$this->session->addRenderedGrid($contentBlockData, new RenderedGridItem());
$frontendTypoScript = $this->request->getAttribute('frontend.typoscript');
$setup = $frontendTypoScript->getSetupArray();
$table = $contentBlockData->tableName;
$this->contentObjectRenderer->start($contentBlockData->_raw, $table);
$typeName = $contentBlockData->typeName;
$table = $contentBlockData->getMainType();
$this->contentObjectRenderer->start($contentBlockData->toArray(), $table);
$typeName = $contentBlockData->getRecordType();
$typoScriptObjectPath = $table . '.' . $typeName;
$pathSegments = GeneralUtility::trimExplode('.', $typoScriptObjectPath);
$lastSegment = (string)array_pop($pathSegments);
Expand Down
2 changes: 1 addition & 1 deletion Classes/DataProcessing/ContentObjectProcessorSession.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public function getRenderedGrid(ContentBlockData $contentBlockData): RenderedGri

private function createIdentifier(ContentBlockData $contentBlockData): string
{
$identifier = $contentBlockData->tableName . $contentBlockData->uid;
$identifier = $contentBlockData->getMainType() . $contentBlockData->getUid();
return $identifier;
}
}
4 changes: 2 additions & 2 deletions Classes/DataProcessing/GridFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ public function build(PageLayoutContext $context, string $columnName, array $rec
GridColumnItem::class,
$context,
$column,
$record->_raw,
$record->tableName,
$record->toArray(),
$record->getMainType(),
);
$column->addItem($gridColumnItem);
}
Expand Down
4 changes: 3 additions & 1 deletion Classes/DataProcessing/ResolvedContentBlockDataRelation.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@

namespace TYPO3\CMS\ContentBlocks\DataProcessing;

use TYPO3\CMS\Core\Domain\Record;

/**
* @internal Not part of TYPO3's public API.
*/
final class ResolvedContentBlockDataRelation
{
public array $raw = [];
public Record $record;
public array $resolved = [];
}
Loading

0 comments on commit e2c003c

Please sign in to comment.