Commit 4c0fd85f authored by Markus Klösges's avatar Markus Klösges Committed by Anja Leichsenring
Browse files

[BUGFIX] Reuse entities of overridden classes in persistence session

When saving and retrieving object from the persistence session,
implementation class names set via \TYPO3\CMS\Extbase\Object\Container
are now respected by storing and retrieving them with that overridden
class name.

Resolves: #77617
Releases: master, 9.5
Change-Id: I887cb164328afe789dc274c92c8f8774b2098a40
Reviewed-on: https://review.typo3.org/58959


Tested-by: default avatarTYPO3com <noreply@typo3.com>
Reviewed-by: Mona Muzaffar's avatarMona Muzaffar <mona.muzaffar@gmx.de>
Tested-by: Mona Muzaffar's avatarMona Muzaffar <mona.muzaffar@gmx.de>
Reviewed-by: Anja Leichsenring's avatarAnja Leichsenring <aleichsenring@ab-softlab.de>
Tested-by: Anja Leichsenring's avatarAnja Leichsenring <aleichsenring@ab-softlab.de>
parent 265ced72
......@@ -22,6 +22,11 @@ use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
*/
class Session implements \TYPO3\CMS\Core\SingletonInterface
{
/**
* @var \TYPO3\CMS\Extbase\Object\Container\Container
*/
protected $objectContainer;
/**
* Reconstituted objects
*
......@@ -42,8 +47,9 @@ class Session implements \TYPO3\CMS\Core\SingletonInterface
/**
* Constructs a new Session
*/
public function __construct()
public function __construct(\TYPO3\CMS\Extbase\Object\Container\Container $container)
{
$this->objectContainer = $container;
$this->reconstitutedEntities = new ObjectStorage();
$this->objectMap = new ObjectStorage();
}
......@@ -128,7 +134,7 @@ class Session implements \TYPO3\CMS\Core\SingletonInterface
*/
public function hasIdentifier($identifier, $className)
{
return isset($this->identifierMap[strtolower($className)][$identifier]);
return isset($this->identifierMap[$this->getClassIdentifier($className)][$identifier]);
}
/**
......@@ -140,7 +146,7 @@ class Session implements \TYPO3\CMS\Core\SingletonInterface
*/
public function getObjectByIdentifier($identifier, $className)
{
return $this->identifierMap[strtolower($className)][$identifier];
return $this->identifierMap[$this->getClassIdentifier($className)][$identifier];
}
/**
......@@ -168,7 +174,7 @@ class Session implements \TYPO3\CMS\Core\SingletonInterface
public function registerObject($object, $identifier)
{
$this->objectMap[$object] = $identifier;
$this->identifierMap[strtolower(get_class($object))][$identifier] = $object;
$this->identifierMap[$this->getClassIdentifier(get_class($object))][$identifier] = $object;
}
/**
......@@ -178,7 +184,7 @@ class Session implements \TYPO3\CMS\Core\SingletonInterface
*/
public function unregisterObject($object)
{
unset($this->identifierMap[strtolower(get_class($object))][$this->objectMap[$object]]);
unset($this->identifierMap[$this->getClassIdentifier(get_class($object))][$this->objectMap[$object]]);
$this->objectMap->detach($object);
}
......@@ -192,4 +198,16 @@ class Session implements \TYPO3\CMS\Core\SingletonInterface
$this->objectMap = new ObjectStorage();
$this->reconstitutedEntities = new ObjectStorage();
}
/**
* Objects are stored in the cache with their implementation class name
* to allow reusing instances of different classes that point to the same implementation
*
* @param string $className
* @return string a unique class identifier respecting configured implementation class names
*/
protected function getClassIdentifier($className): string
{
return strtolower($this->objectContainer->getImplementationClassName($className));
}
}
<?php
declare(strict_types = 1);
namespace ExtbaseTeam\A\Domain\Model;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Class ExtbaseTeam\A\Domain\Model\A
*/
class A extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity
{
/**
* @var string
*/
protected $a;
/**
* @return string
*/
public function getA(): string
{
return $this->a;
}
/**
* @param string $a
*/
public function setA(string $a): void
{
$this->a = $a;
}
}
<?php
declare(strict_types = 1);
namespace ExtbaseTeam\A\Domain\Model;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Extbase\Persistence\Repository;
/**
* Class ExtbaseTeam\A\Domain\Model\ARepository
*/
class ARepository extends Repository
{
}
<?php
return [
'ctrl' => [
'label' => 'uid',
'title' => 'A',
'tstamp' => 'tstamp',
'crdate' => 'crdate',
],
'interface' => [
'showRecordFieldList' => 'title'
],
'columns' => [
'a' => [
'label' => 'a',
'config' => [
'type' => 'input',
'size' => 25,
'max' => 255,
]
],
],
'types' => [
'1' => ['showitem' => '--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:general,a']
],
];
<?php
$EM_CONF[$_EXTKEY] = [
'title' => '',
'description' => '',
'category' => 'example',
'author' => '',
'author_company' => '',
'author_email' => '',
'state' => 'stable',
'uploadfolder' => 0,
'createDirs' => '',
'clearCacheOnLoad' => 1,
'version' => '10.0.0',
'constraints' => [
'depends' => [
'typo3' => '10.0.0',
],
'conflicts' => [],
'suggests' => [],
],
];
--
-- Table structure for table 'tx_a_domain_model_a'
--
CREATE TABLE tx_a_domain_model_a (
uid int(11) NOT NULL auto_increment,
pid int(11) DEFAULT '0' NOT NULL,
tstamp int(11) DEFAULT '0' NOT NULL,
crdate int(11) DEFAULT '0' NOT NULL,
a varchar(255) DEFAULT '' NOT NULL,
PRIMARY KEY (uid),
KEY parent (pid)
);
<?php
declare(strict_types = 1);
namespace ExtbaseTeam\B\Domain\Model;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Class ExtbaseTeam\B\Domain\Model\B
*/
class B extends \ExtbaseTeam\A\Domain\Model\A
{
/**
* @var string
*/
protected $b;
/**
* @return string
*/
public function getB(): string
{
return $this->b;
}
/**
* @param string $b
*/
public function setB(string $b): void
{
$this->b = $b;
}
}
<?php
declare(strict_types = 1);
namespace ExtbaseTeam\B\Domain\Model;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Extbase\Persistence\Repository;
/**
* Class ExtbaseTeam\B\Domain\Model\BRepository
*/
class BRepository extends Repository
{
}
<?php
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTCAcolumns(
'tx_a_domain_model_a',
[
'b' => [
'label' => 'b',
'config' => [
'type' => 'input',
'size' => 25,
'max' => 255,
]
],
]
);
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addToAllTCAtypes('tx_a_domain_model_a', 'b');
<?php
$EM_CONF[$_EXTKEY] = [
'title' => '',
'description' => '',
'category' => 'example',
'author' => '',
'author_company' => '',
'author_email' => '',
'state' => 'stable',
'uploadfolder' => 0,
'createDirs' => '',
'clearCacheOnLoad' => 1,
'version' => '10.0.0',
'constraints' => [
'depends' => [
'typo3' => '10.0.0',
],
'conflicts' => [],
'suggests' => [],
],
];
<?php
defined('TYPO3_MODE') or die();
\TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\Container\Container::class)
->registerImplementation(
\ExtbaseTeam\A\Domain\Model\A::class,
\ExtbaseTeam\B\Domain\Model\B::class
);
--
-- Table structure for table 'tx_a_domain_model_a'
--
CREATE TABLE tx_a_domain_model_a (
b varchar(255) DEFAULT '' NOT NULL
);
config.tx_extbase.persistence {
classes {
ExtbaseTeam\B\Domain\Model\B {
mapping {
tableName = tx_a_domain_model_a
}
}
}
}
"tx_a_domain_model_a",,,,,,
,"uid", "pid", "tstamp", "crdate", "a", "b"
,1, 0, 1546178428, 1546178428, "A", "B"
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Extbase\Tests\Functional;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Object\ObjectManager;
/**
* Class TYPO3\CMS\Extbase\Tests\Functional\ImplementationClassNameTest
*/
class ImplementationClassNameTest extends \TYPO3\TestingFramework\Core\Functional\FunctionalTestCase
{
/**
* @var array
*/
protected $testExtensionsToLoad = [
'typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/class_overriding/a',
'typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/class_overriding/b',
];
/**
* @var array
*/
protected $coreExtensionsToLoad = ['extbase', 'fluid'];
/**
* @var ObjectManager
*/
private $objectManager;
public function setUp(): void
{
parent::setUp();
$this->objectManager = GeneralUtility::makeInstance(ObjectManager::class);
$this->importCSVDataSet(GeneralUtility::getFileAbsFileName(
'typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/class_overriding/b/tx_a_domain_model_a.csv'
));
}
/**
* @test
*/
public function testARepositoryObjectsAreTakenFromSession(): void
{
$aRepository = $this->objectManager->get(\ExtbaseTeam\A\Domain\Model\ARepository::class);
$a1 = $aRepository->findByUid(1);
$a2 = $aRepository->findByUid(1);
$this->assertSame($a1, $a2);
}
/**
* @test
*/
public function testBRepositoryObjectsAreTakenFromSession(): void
{
$bRepository = $this->objectManager->get(\ExtbaseTeam\B\Domain\Model\BRepository::class);
$b1 = $bRepository->findByUid(1);
$b2 = $bRepository->findByUid(1);
$this->assertSame($b1, $b2);
}
}
......@@ -16,6 +16,7 @@ namespace TYPO3\CMS\Extbase\Tests\Unit\Persistence\Generic\Mapper;
*/
use TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface;
use TYPO3\CMS\Extbase\Object\Container\Container;
use TYPO3\CMS\Extbase\Persistence\Generic\Exception\UnexpectedTypeException;
use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap;
use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper;
......@@ -248,7 +249,7 @@ class DataMapperTest extends UnitTestCase
$classSchema1 = new ClassSchema(Fixture\DummyParentEntity::class);
$identifier = 1;
$session = new \TYPO3\CMS\Extbase\Persistence\Generic\Session();
$session = new \TYPO3\CMS\Extbase\Persistence\Generic\Session(new Container());
$session->registerObject($child, $identifier);
$mockReflectionService = $this->getMockBuilder(\TYPO3\CMS\Extbase\Reflection\ReflectionService::class)
......
......@@ -16,6 +16,7 @@ namespace TYPO3\CMS\Extbase\Tests\Unit\Persistence\Generic;
*/
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
use TYPO3\CMS\Extbase\Object\Container\Container;
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
use TYPO3\CMS\Extbase\Persistence\Generic\Backend;
use TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface;
......@@ -288,7 +289,7 @@ class PersistenceManagerTest extends UnitTestCase
PersistenceManager::class,
['dummy']
);
$session = new Session();
$session = new Session(new Container());
$changedEntities = new ObjectStorage();
$entity1 = new $classNameWithNamespace();
/** @var RepositoryInterface|\TYPO3\TestingFramework\Core\AccessibleObjectInterface $repository */
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment