Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ public function __construct(ViewCombination $viewCombination, IFactory $factory)
* @param IMetaModelDataDefinition $container The data container.
*
* @return void
*
* @SuppressWarnings(PHPMD.Superglobals)
*/
#[\Override]
protected function build(IMetaModelDataDefinition $container)
Expand All @@ -95,7 +97,9 @@ protected function build(IMetaModelDataDefinition $container)
->setTableName($container->getName())
->setClassName(Driver::class)
->setInitializationData(['source' => $container->getName()])
->setVersioningEnabled(false);
->setVersioningEnabled(
(bool) ($GLOBALS['TL_DCA'][$container->getName()]['config']['enableVersioning'] ?? false)
);
// MetaModels\CoreBundle\EventListener\DcGeneral\LogPersistedItemsListener logs items
// with their rendered label instead - logging both would duplicate every entry under
// two different wordings. A separate statement, not chained onto the calls above:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
'config' => [
'dataContainer' => General::class,
'switchToEdit' => true,
'enableVersioning' => false,
'enableVersioning' => true,
'sql' => [
'keys' => [
'id' => 'primary',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
'label' => 'list_label.label',
'description' => 'list_label.description',
'switchToEdit' => false,
'enableVersioning' => false,
'enableVersioning' => true,
'sql' => [
'keys' => [
'id' => 'primary',
Expand Down
2 changes: 1 addition & 1 deletion src/CoreBundle/Resources/contao/dca/tl_metamodel_item.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
'config' => [
'dataContainer' => General::class,
'switchToEdit' => false,
'enableVersioning' => false,
'enableVersioning' => true,
],
'dca_config' => [
'data_provider' => [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
'dataContainer' => General::class,
'ptable' => 'tl_metamodel',
'switchToEdit' => false,
'enableVersioning' => false,
'enableVersioning' => true,
'sql' => [
'keys' => [
'id' => 'primary',
Expand Down
164 changes: 141 additions & 23 deletions src/DcGeneral/Data/Driver.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@
use ContaoCommunityAlliance\DcGeneral\Data\FilterOptionCollectionInterface;
use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface;
use ContaoCommunityAlliance\DcGeneral\Data\MultiLanguageDataProviderInterface;
use ContaoCommunityAlliance\DcGeneral\Data\VersionModel;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Exception;
use MetaModels\Attribute\IAttribute;
use MetaModels\Attribute\IComplex;
use MetaModels\Attribute\ITranslated;
Expand Down Expand Up @@ -159,19 +161,49 @@
/**
* Save a new Version of a record.
*
* Stores the model's properties the same way a normal edit would arrive at them - through
* {@see Model::getProperty()}, which already runs every attribute's valueToWidget() - so
* restoring later can go back through setProperty()/widgetToValue() and reuse the very same
* save path a regular edit takes. This covers complex attributes (tags, table fields, ...)
* without any attribute-specific code, at the cost of only covering the currently active
* language for translated attributes - see ".claude/dcg-versionierung.md".
*
* @param ModelInterface $model The model to be saved.
* @param string $username The username that creates the new version.
*
* @return void
*
* @throws \RuntimeException As this is currently unimplemented, an Exception is thrown.
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
* @throws Exception When the database can not be queried.
*/
#[\Override]
public function saveVersion(ModelInterface $model, $username)
{
throw new \RuntimeException('Versioning not supported in MetaModels so far.');
assert($this->connection instanceof Connection);
$fromTable = $this->getMetaModel()->getTableName();

$count = (int) $this->connection
->createQueryBuilder()
->select('COUNT(*) AS count')
->from('tl_version')
->andWhere('pid = :pid')
->andWhere('fromTable = :fromTable')
->setParameter('pid', $model->getId())
->setParameter('fromTable', $fromTable)
->executeQuery()
->fetchOne();

$newVersion = $count + 1;

$this->connection->insert('tl_version', [
'pid' => $model->getId(),
'tstamp' => \time(),
'version' => $newVersion,
'fromTable' => $fromTable,
'username' => $username,
'data' => \serialize($model->getPropertiesAsArray()),
]);

$this->setVersionActive($model->getId(), $newVersion);
}

/**
Expand All @@ -180,16 +212,48 @@
* @param mixed $mixID The ID of record.
* @param mixed $mixVersion The ID of the version.
*
* @return never-return
*
* @throws \RuntimeException As this is currently unimplemented, an Exception is thrown.
* @return ModelInterface|null
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
* @throws Exception When the database can not be queried.
*/
#[\Override]
public function getVersion($mixID, $mixVersion)
{
throw new \RuntimeException('Versioning not supported in MetaModels so far.');
assert($this->connection instanceof Connection);

$row = $this->connection
->createQueryBuilder()
->select('data')
->from('tl_version')
->andWhere('pid = :pid')
->andWhere('version = :version')
->andWhere('fromTable = :fromTable')
->setParameter('pid', $mixID)
->setParameter('version', $mixVersion)
->setParameter('fromTable', $this->getMetaModel()->getTableName())
->executeQuery()
->fetchAssociative();

if (false === $row) {
return null;
}

$data = \unserialize((string) $row['data']);
if (!\is_array($data)) {
return null;
}

$model = $this->getEmptyModel();
$model->setId($mixID);
foreach ($data as $propertyName => $value) {
if ('id' === $propertyName) {
continue;
}

$model->setProperty((string) $propertyName, $value);
}

return $model;
}

/**
Expand All @@ -200,14 +264,23 @@
*
* @return void
*
* @throws \RuntimeException As this is currently unimplemented, an Exception is thrown.
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
* @throws Exception When the database can not be queried.
*/
#[\Override]
public function setVersionActive($mixID, $mixVersion)
{
throw new \RuntimeException('Versioning not supported in MetaModels so far.');
assert($this->connection instanceof Connection);
$fromTable = $this->getMetaModel()->getTableName();
$updateValues = ['pid' => $mixID, 'fromTable' => $fromTable];

// "active" is a strict tinyint(1), not the char(1) flag most Contao/MetaModels tables use -
// an empty string fails under strict SQL mode.
$this->connection->update('tl_version', ['active' => 0], $updateValues);
$this->connection->update(
'tl_version',
['active' => 1],
['pid' => $mixID, 'fromTable' => $fromTable, 'version' => $mixVersion]
);
}

/**
Expand All @@ -217,14 +290,27 @@
*
* @return mixed
*
* @throws \RuntimeException As this is currently unimplemented, an Exception is thrown.
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
* @throws Exception When the database can not be queried.
*/
#[\Override]
public function getActiveVersion($mixID)
{
throw new \RuntimeException('Versioning not supported in MetaModels so far.');
assert($this->connection instanceof Connection);

$version = $this->connection
->createQueryBuilder()
->select('version')
->from('tl_version')
->andWhere('pid = :pid')
->andWhere('fromTable = :fromTable')
->andWhere('active = :active')
->setParameter('pid', $mixID)
->setParameter('fromTable', $this->getMetaModel()->getTableName())
->setParameter('active', '1')
->executeQuery()
->fetchOne();

return false === $version ? null : $version;
}

/**
Expand Down Expand Up @@ -571,13 +657,48 @@
*
* @return CollectionInterface
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
* @throws Exception When the database can not be queried.
*/
#[\Override]
public function getVersions($mixID, $onlyActive = false)
{
// No version support on MetaModels so far, sorry.
return new DefaultCollection();
assert($this->connection instanceof Connection);

$queryBuilder = $this->connection
->createQueryBuilder()
->select('tstamp', 'version', 'username', 'active')
->from('tl_version')
->andWhere('pid = :pid')
->andWhere('fromTable = :fromTable')
->setParameter('pid', $mixID)
->setParameter('fromTable', $this->getMetaModel()->getTableName());

if ($onlyActive) {
$queryBuilder->andWhere('active = :active')->setParameter('active', '1');
} else {
$queryBuilder->orderBy('version', 'DESC');
}

// A version-list row is metadata about a version (tstamp/version/username/active), not a
// MetaModels item - VersionModel (a plain property bag) fits, the full Item/attribute
// machinery getEmptyModel() would pull in does not and does not implement
// VersionModelInterface, which the edit mask's version drop-down requires.
$collection = $this->getEmptyCollection();
foreach ($queryBuilder->executeQuery()->fetchAllAssociative() as $row) {
$model = new VersionModel();

Check failure on line 688 in src/DcGeneral/Data/Driver.php

View workflow job for this annotation

GitHub Actions / PHP: 8.5 Contao: ~5.7.0

UndefinedClass: Class, interface or enum named ContaoCommunityAlliance\DcGeneral\Data\VersionModel does not exist (reported by psalm)

Check failure on line 688 in src/DcGeneral/Data/Driver.php

View workflow job for this annotation

GitHub Actions / PHP: 8.4 Contao: ~5.7.0

UndefinedClass: Class, interface or enum named ContaoCommunityAlliance\DcGeneral\Data\VersionModel does not exist (reported by psalm)

Check failure on line 688 in src/DcGeneral/Data/Driver.php

View workflow job for this annotation

GitHub Actions / PHP: 8.5 Contao: ~5.7.0

UndefinedClass: Class, interface or enum named ContaoCommunityAlliance\DcGeneral\Data\VersionModel does not exist (reported by psalm)

Check failure on line 688 in src/DcGeneral/Data/Driver.php

View workflow job for this annotation

GitHub Actions / PHP: 8.4 Contao: ~5.7.0

UndefinedClass: Class, interface or enum named ContaoCommunityAlliance\DcGeneral\Data\VersionModel does not exist (reported by psalm)
$model->setProviderName($this->getMetaModel()->getTableName());

Check failure on line 689 in src/DcGeneral/Data/Driver.php

View workflow job for this annotation

GitHub Actions / PHP: 8.5 Contao: ~5.7.0

UndefinedClass: Class, interface or enum named ContaoCommunityAlliance\DcGeneral\Data\VersionModel does not exist (reported by psalm)

Check failure on line 689 in src/DcGeneral/Data/Driver.php

View workflow job for this annotation

GitHub Actions / PHP: 8.4 Contao: ~5.7.0

UndefinedClass: Class, interface or enum named ContaoCommunityAlliance\DcGeneral\Data\VersionModel does not exist (reported by psalm)

Check failure on line 689 in src/DcGeneral/Data/Driver.php

View workflow job for this annotation

GitHub Actions / PHP: 8.5 Contao: ~5.7.0

UndefinedClass: Class, interface or enum named ContaoCommunityAlliance\DcGeneral\Data\VersionModel does not exist (reported by psalm)

Check failure on line 689 in src/DcGeneral/Data/Driver.php

View workflow job for this annotation

GitHub Actions / PHP: 8.4 Contao: ~5.7.0

UndefinedClass: Class, interface or enum named ContaoCommunityAlliance\DcGeneral\Data\VersionModel does not exist (reported by psalm)
foreach ($row as $propertyName => $value) {
$model->setProperty($propertyName, $value);

Check failure on line 691 in src/DcGeneral/Data/Driver.php

View workflow job for this annotation

GitHub Actions / PHP: 8.5 Contao: ~5.7.0

UndefinedClass: Class, interface or enum named ContaoCommunityAlliance\DcGeneral\Data\VersionModel does not exist (reported by psalm)

Check failure on line 691 in src/DcGeneral/Data/Driver.php

View workflow job for this annotation

GitHub Actions / PHP: 8.4 Contao: ~5.7.0

UndefinedClass: Class, interface or enum named ContaoCommunityAlliance\DcGeneral\Data\VersionModel does not exist (reported by psalm)

Check failure on line 691 in src/DcGeneral/Data/Driver.php

View workflow job for this annotation

GitHub Actions / PHP: 8.5 Contao: ~5.7.0

UndefinedClass: Class, interface or enum named ContaoCommunityAlliance\DcGeneral\Data\VersionModel does not exist (reported by psalm)

Check failure on line 691 in src/DcGeneral/Data/Driver.php

View workflow job for this annotation

GitHub Actions / PHP: 8.4 Contao: ~5.7.0

UndefinedClass: Class, interface or enum named ContaoCommunityAlliance\DcGeneral\Data\VersionModel does not exist (reported by psalm)
}

// The template submits this as "version" to restore - the item id stays with $mixID and
// is not what identifies a single entry in this list, the version number does.
$model->setIdRaw($row['version']);

Check failure on line 696 in src/DcGeneral/Data/Driver.php

View workflow job for this annotation

GitHub Actions / PHP: 8.5 Contao: ~5.7.0

UndefinedClass: Class, interface or enum named ContaoCommunityAlliance\DcGeneral\Data\VersionModel does not exist (reported by psalm)

Check failure on line 696 in src/DcGeneral/Data/Driver.php

View workflow job for this annotation

GitHub Actions / PHP: 8.4 Contao: ~5.7.0

UndefinedClass: Class, interface or enum named ContaoCommunityAlliance\DcGeneral\Data\VersionModel does not exist (reported by psalm)

Check failure on line 696 in src/DcGeneral/Data/Driver.php

View workflow job for this annotation

GitHub Actions / PHP: 8.5 Contao: ~5.7.0

UndefinedClass: Class, interface or enum named ContaoCommunityAlliance\DcGeneral\Data\VersionModel does not exist (reported by psalm)

Check failure on line 696 in src/DcGeneral/Data/Driver.php

View workflow job for this annotation

GitHub Actions / PHP: 8.4 Contao: ~5.7.0

UndefinedClass: Class, interface or enum named ContaoCommunityAlliance\DcGeneral\Data\VersionModel does not exist (reported by psalm)

$collection->push($model);

Check failure on line 698 in src/DcGeneral/Data/Driver.php

View workflow job for this annotation

GitHub Actions / PHP: 8.5 Contao: ~5.7.0

InvalidArgument: Argument 1 of ContaoCommunityAlliance\DcGeneral\Data\CollectionInterface::push expects ContaoCommunityAlliance\DcGeneral\Data\ModelInterface, but ContaoCommunityAlliance\DcGeneral\Data\VersionModel provided (reported by psalm)

Check failure on line 698 in src/DcGeneral/Data/Driver.php

View workflow job for this annotation

GitHub Actions / PHP: 8.4 Contao: ~5.7.0

InvalidArgument: Argument 1 of ContaoCommunityAlliance\DcGeneral\Data\CollectionInterface::push expects ContaoCommunityAlliance\DcGeneral\Data\ModelInterface, but ContaoCommunityAlliance\DcGeneral\Data\VersionModel provided (reported by psalm)

Check failure on line 698 in src/DcGeneral/Data/Driver.php

View workflow job for this annotation

GitHub Actions / PHP: 8.5 Contao: ~5.7.0

InvalidArgument: Argument 1 of ContaoCommunityAlliance\DcGeneral\Data\CollectionInterface::push expects ContaoCommunityAlliance\DcGeneral\Data\ModelInterface, but ContaoCommunityAlliance\DcGeneral\Data\VersionModel provided (reported by psalm)

Check failure on line 698 in src/DcGeneral/Data/Driver.php

View workflow job for this annotation

GitHub Actions / PHP: 8.4 Contao: ~5.7.0

InvalidArgument: Argument 1 of ContaoCommunityAlliance\DcGeneral\Data\CollectionInterface::push expects ContaoCommunityAlliance\DcGeneral\Data\ModelInterface, but ContaoCommunityAlliance\DcGeneral\Data\VersionModel provided (reported by psalm)
}

return $collection;
}

/**
Expand Down Expand Up @@ -761,9 +882,6 @@
assert($objNative1 instanceof IItem);
$objNative2 = $secondModel->getItem();
assert($objNative2 instanceof IItem);
if ($objNative1->getMetaModel() === $objNative2->getMetaModel()) {
return true;
}
foreach ($objNative1->getMetaModel()->getAttributes() as $objAttribute) {
if ($objNative1->get($objAttribute->getColName()) !== $objNative2->get($objAttribute->getColName())) {
return false;
Expand Down
Loading