Commit db92f57d authored by Sebastian Kurfürst's avatar Sebastian Kurfürst
Browse files

Extbase:

(Changes by Jochen)
This commit merges the rewrite of the persistence layer back to trunk. It will probably break your existing installation. Please check out fluid and blog_example again.
* Implemented an Object Manager (a wrapper for t3lib_div::makeInstance())
* !!! Backport of the Query and the Query Object Model from FLOW3.
* !!! Removed Repository->fetch*()
* Splited the functionality of the Object Relational Mapper into the Persistence Backend, the Data Mapper and the SQL Storage Backend.
* Implemented the Lazy Loading Proxy.
* supported Request types is now Tx_Extbase_MVC_Request instead of Tx_Extbase_MVC_Web_Request.
* Changed _reconstituteProperty() to _setProperty() (according to FLOW3).
* Added _getProperty() to the Domain Object.
* The Dispatcher now instanciates and uses a PersistenceManager to commit changes.
* Added TypoScript converter method (to convert from new TS to classic TS). Fixes #3293.
* Renamed SqlBackend to Typo3DbBackend
* Now using constants in Tx_Extbase_Persistence_PropertyType instead of Tx_Extbase_Persistence_Mapper_ColumnMap
* Improved conversion from table value to property value
* Persistence_Repository: findByUid() checks now for positive integer
* Fixed: Only Aggregate Roots are now registered as Reconstituted Objects
* !!! Added _isNew() to the Domain Object Interface
* Revision of the Persistence Backend (CRUD operations work now - again)
* Fixed Query Object Model "UpperCase"
* Implemented addRow(), deleteRow() and updateRow()
* Fixed: _memorizeCleanState() now clones a property value if it is an object
* Argument->findObjectByUid() memorizes now the clean state (todo: check if the object is an Aggregate Root)
* Removed unnecessary method getRelations()
* Fixed wrong class name of PropertyError; resolves issue #3855
* Fixed typo in Persistence Query; resolves issue #3831
* Changed: Equivalent Value Objects (all properties are equal) are now persisted only once
* Backport and enhanced several Unit Tests (not finished yet)
* Enhanced Base Test Case
* Changed signature of hasValueObject()
* Changed: hasValueObject() returns the uid of the already persisted value object - if any
* Fixed: relation tables are now filled with the right values if a value object already exists in the database
* Changed: The Persistence Manager and the Query Factory are now "injected" into the Arguments and Argument
* Deleted obsolete files
parent 8b698268
...@@ -32,10 +32,25 @@ ...@@ -32,10 +32,25 @@
* @package Extbase * @package Extbase
* @subpackage Controller * @subpackage Controller
* @version $ID:$ * @version $ID:$
* @todo - is this ever called?
*/ */
class Tx_Extbase_Controller_StandardController extends Tx_Extbase_MVC_Controller_ActionController { class Tx_Extbase_Controller_StandardController extends Tx_Extbase_MVC_Controller_ActionController {
/**
* @var Tx_Extbase_View_StandardView
*/
protected $standardView;
/**
* Injects the StandardView.
*
* @param Tx_Extbase_View_StandardView $notFoundView
* @return void
*/
public function injectStandardView(Tx_Extbase_View_StandardView $standardView) {
// TODO inject Standard View; implement view and template
$this->standardView = $standardView;
}
/** /**
* Processes a generic request and returns a response * Processes a generic request and returns a response
* *
...@@ -45,7 +60,7 @@ class Tx_Extbase_Controller_StandardController extends Tx_Extbase_MVC_Controller ...@@ -45,7 +60,7 @@ class Tx_Extbase_Controller_StandardController extends Tx_Extbase_MVC_Controller
public function processRequest(Tx_Extbase_MVC_Request $request, Tx_Extbase_MVC_Response $response) { public function processRequest(Tx_Extbase_MVC_Request $request, Tx_Extbase_MVC_Response $response) {
$response->setContent( $response->setContent(
"\nWelcome to TYPO3!\n\n" . "\nWelcome to TYPO3!\n\n" .
"This is the standard view of the TYPO3 MVC object. You see this message because no \n" . "This is the standard view of the TYPO3 Extbase project. You see this message because no \n" .
"other view is available. Please refer to the Developer's Guide for more information \n" . "other view is available. Please refer to the Developer's Guide for more information \n" .
"how to create and configure one.\n\n" . "how to create and configure one.\n\n" .
"Have fun! The TYPO3 Development Team\n" "Have fun! The TYPO3 Development Team\n"
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
* @version $ID:$ * @version $ID:$
*/ */
class Tx_Extbase_Dispatcher { class Tx_Extbase_Dispatcher {
/** /**
* @var Tx_Extbase_Reflection_Service * @var Tx_Extbase_Reflection_Service
*/ */
...@@ -48,11 +49,24 @@ class Tx_Extbase_Dispatcher { ...@@ -48,11 +49,24 @@ class Tx_Extbase_Dispatcher {
t3lib_div::sysLog('Extbase was not able to dispatch the request. No configuration.', 'extbase', t3lib_div::SYSLOG_SEVERITY_ERROR); t3lib_div::sysLog('Extbase was not able to dispatch the request. No configuration.', 'extbase', t3lib_div::SYSLOG_SEVERITY_ERROR);
return $content; return $content;
} }
$requestBuilder = t3lib_div::makeInstance('Tx_Extbase_MVC_Web_RequestBuilder'); $requestBuilder = t3lib_div::makeInstance('Tx_Extbase_MVC_Web_RequestBuilder');
$request = $requestBuilder->initialize($configuration); $request = $requestBuilder->initialize($configuration);
$request = $requestBuilder->build(); $request = $requestBuilder->build();
$response = t3lib_div::makeInstance('Tx_Extbase_MVC_Web_Response'); $response = t3lib_div::makeInstance('Tx_Extbase_MVC_Web_Response');
$persistenceSession = t3lib_div::makeInstance('Tx_Extbase_Persistence_Session'); // singleton $persistenceSession = t3lib_div::makeInstance('Tx_Extbase_Persistence_Session'); // singleton
$storageBackend = t3lib_div::makeInstance('Tx_Extbase_Persistence_Storage_Typo3DbBackend');
$dataMapper = t3lib_div::makeInstance('Tx_Extbase_Persistence_Mapper_DataMapper');
$persistenceBackend = t3lib_div::makeInstance('Tx_Extbase_Persistence_Backend', $persistenceSession, $storageBackend); // singleton
$persistenceBackend->injectDataMapper($dataMapper);
$persistenceBackend->injectIdentityMap(t3lib_div::makeInstance('Tx_Extbase_Persistence_IdentityMap'));
$persistenceBackend->injectQOMFactory(t3lib_div::makeInstance('Tx_Extbase_Persistence_QOM_QueryObjectModelFactory', $storageBackend, $dataMapper));
$persistenceBackend->injectValueFactory(t3lib_div::makeInstance('Tx_Extbase_Persistence_ValueFactory'));
$persistenceManager = t3lib_div::makeInstance('Tx_Extbase_Persistence_Manager', $persistenceBackend); // singleton
$persistenceManager->injectSession($persistenceSession);
$dispatchLoopCount = 0; $dispatchLoopCount = 0;
while (!$request->isDispatched()) { while (!$request->isDispatched()) {
...@@ -62,13 +76,11 @@ class Tx_Extbase_Dispatcher { ...@@ -62,13 +76,11 @@ class Tx_Extbase_Dispatcher {
$controller->processRequest($request, $response); $controller->processRequest($request, $response);
} catch (Tx_Extbase_Exception_StopAction $ignoredException) { } catch (Tx_Extbase_Exception_StopAction $ignoredException) {
} catch (Tx_Extbase_Exception_InvalidArgumentValue $exception) { } catch (Tx_Extbase_Exception_InvalidArgumentValue $exception) {
$persistenceSession->clear();
return ''; return '';
} }
} }
$persistenceSession->commit(); $persistenceManager->persistAll();
$persistenceSession->clear();
$this->reflectionService->shutdown(); $this->reflectionService->shutdown();
if (count($response->getAdditionalHeaderData()) > 0) { if (count($response->getAdditionalHeaderData()) > 0) {
$GLOBALS['TSFE']->additionalHeaderData[$request->getControllerExtensionName()] = implode("\n", $response->getAdditionalHeaderData()); $GLOBALS['TSFE']->additionalHeaderData[$request->getControllerExtensionName()] = implode("\n", $response->getAdditionalHeaderData());
...@@ -110,6 +122,7 @@ class Tx_Extbase_Dispatcher { ...@@ -110,6 +122,7 @@ class Tx_Extbase_Dispatcher {
$this->reflectionService->initialize(); $this->reflectionService->initialize();
} }
$validatorResolver = t3lib_div::makeInstance('Tx_Extbase_Validation_ValidatorResolver'); $validatorResolver = t3lib_div::makeInstance('Tx_Extbase_Validation_ValidatorResolver');
$validatorResolver->injectObjectManager(t3lib_div::makeInstance('Tx_Extbase_Object_Manager'));
$validatorResolver->injectReflectionService($this->reflectionService); $validatorResolver->injectReflectionService($this->reflectionService);
$controller->injectValidatorResolver($validatorResolver); $controller->injectValidatorResolver($validatorResolver);
$controller->injectReflectionService($this->reflectionService); $controller->injectReflectionService($this->reflectionService);
...@@ -164,4 +177,4 @@ class Tx_Extbase_Dispatcher { ...@@ -164,4 +177,4 @@ class Tx_Extbase_Dispatcher {
} }
} }
?> ?>
\ No newline at end of file
...@@ -55,9 +55,6 @@ abstract class Tx_Extbase_DomainObject_AbstractDomainObject implements Tx_Extbas ...@@ -55,9 +55,6 @@ abstract class Tx_Extbase_DomainObject_AbstractDomainObject implements Tx_Extbas
* @internal * @internal
*/ */
public function __wakeup() { public function __wakeup() {
foreach ($GLOBALS['Extbase']['reconstituteObject']['properties'] as $propertyName => $propertyValue) {
$this->_reconstituteProperty($propertyName, $propertyValue);
}
$this->initializeObject(); $this->initializeObject();
} }
...@@ -73,48 +70,57 @@ abstract class Tx_Extbase_DomainObject_AbstractDomainObject implements Tx_Extbas ...@@ -73,48 +70,57 @@ abstract class Tx_Extbase_DomainObject_AbstractDomainObject implements Tx_Extbas
/** /**
* Getter for uid * Getter for uid
* *
* @return string * @return int
*/ */
final public function getUid() { final public function getUid() {
return $this->uid; return (int)$this->uid;
}
/**
* Getter for the identifier
*
* @return int
*/
final public function getIdentifier() {
return (int)$this->uid;
} }
/** /**
* Reconstitutes a property. This method should only be called at reconstitution time by the framework! * Reconstitutes a property. Only for internal use.
* *
* @param string $propertyName * @param string $propertyName
* @param string $value * @param string $value
* @return void * @return void
* @internal * @internal
*/ */
public function _reconstituteProperty($propertyName, $value) { public function _setProperty($propertyName, $propertyValue) {
if (property_exists($this, $propertyName)) { if (property_exists($this, $propertyName)) {
$this->$propertyName = $value; $this->$propertyName = $propertyValue;
return TRUE; return TRUE;
} }
return FALSE; return FALSE;
} }
/** /**
* Returns a hash map of property names and property values. Only for internal use. * Returns the property value of the given property name. Only for internal use.
* *
* @return array The properties * @return mixed The propertyValue
* @internal * @internal
*/ */
public function _getProperties() { public function _getProperty($propertyName) {
$properties = get_object_vars($this); return $this->$propertyName;
// unset($properties['_cleanProperties']); // TODO Check this again
return $properties;
} }
/** /**
* Returns the property value of the given property name. Only for internal use. * Returns a hash map of property names and property values. Only for internal use.
* *
* @return array The propertyName * @return array The properties
* @internal * @internal
*/ */
public function _getPropertyValue($propertyName) { public function _getProperties() {
return $this->$propertyName; $properties = get_object_vars($this);
unset($properties['_cleanProperties']);
return $properties;
} }
/** /**
......
...@@ -41,21 +41,45 @@ abstract class Tx_Extbase_DomainObject_AbstractEntity extends Tx_Extbase_DomainO ...@@ -41,21 +41,45 @@ abstract class Tx_Extbase_DomainObject_AbstractEntity extends Tx_Extbase_DomainO
* Register an object's clean state, e.g. after it has been reconstituted * Register an object's clean state, e.g. after it has been reconstituted
* from the database * from the database
* *
* @param string $propertyName The name of the property to be memorized. If omittet all persistable properties are memorized.
* @return void * @return void
* @internal * @internal
*/ */
public function _memorizeCleanState() { public function _memorizeCleanState($propertyName = NULL) {
// TODO Remove dependency to $dataMapper // TODO Remove dependency to $dataMapper
$dataMapper = t3lib_div::makeInstance('Tx_Extbase_Persistence_Mapper_ObjectRelationalMapper'); // singleton if ($propertyName !== NULL) {
$this->_cleanProperties = array(); } else {
$properties = get_object_vars($this); $dataMapper = t3lib_div::makeInstance('Tx_Extbase_Persistence_Mapper_DataMapper'); // singleton
foreach ($properties as $propertyName => $propertyValue) { $this->_cleanProperties = array();
if ($dataMapper->isPersistableProperty(get_class($this), $propertyName)) { $properties = get_object_vars($this);
$this->_cleanProperties[$propertyName] = $this->$propertyName; foreach ($properties as $propertyName => $propertyValue) {
if ($dataMapper->isPersistableProperty(get_class($this), $propertyName)) {
$this->_memorizePropertyCleanState($propertyName);
}
} }
} }
} }
/**
* Register an properties's clean state, e.g. after it has been reconstituted
* from the database
*
* @param string $propertyName The name of the property to be memorized. If omittet all persistable properties are memorized.
* @return void
* @internal
*/
public function _memorizePropertyCleanState($propertyName) {
$propertyValue = $this->$propertyName;
if (!is_array($this->_cleanProperties)) {
$this->_cleanProperties = array();
}
if (is_object($propertyValue)) {
$this->_cleanProperties[$propertyName] = clone($propertyValue);
} else {
$this->_cleanProperties[$propertyName] = $propertyValue;
}
}
/** /**
* Returns a hash map of dirty properties and $values * Returns a hash map of dirty properties and $values
* *
......
...@@ -32,23 +32,21 @@ ...@@ -32,23 +32,21 @@
interface Tx_Extbase_DomainObject_DomainObjectInterface { interface Tx_Extbase_DomainObject_DomainObjectInterface {
/** /**
* Reconstitutes a property. This method should only be called at reconstitution time! * Register an object's clean state, e.g. after it has been reconstituted
* from the database
* *
* @param string $propertyName
* @param string $value
* @return void * @return void
* @internal * @internal
*/ */
public function _reconstituteProperty($propertyName, $value); public function _memorizeCleanState();
/** /**
* Register an object's clean state, e.g. after it has been reconstituted * Returns TRUE if the object is new (the uid was not set, yet). Only for internal use
* from the database
* *
* @return void * @return boolean
* @internal * @internal
*/ */
public function _memorizeCleanState(); public function _isNew();
/** /**
* Returns TRUE if the properties were modified after reconstitution * Returns TRUE if the properties were modified after reconstitution
...@@ -57,6 +55,25 @@ interface Tx_Extbase_DomainObject_DomainObjectInterface { ...@@ -57,6 +55,25 @@ interface Tx_Extbase_DomainObject_DomainObjectInterface {
* @internal * @internal
*/ */
public function _isDirty(); public function _isDirty();
/**
* Reconstitutes a property. Only for internal use.
*
* @param string $propertyName
* @param string $value
* @return void
* @internal
*/
public function _setProperty($propertyName, $value);
/**
* Returns the property value of the given property name. Only for internal use.
*
* @return mixed The propertyValue
* @internal
*/
public function _getProperty($propertyName);
/** /**
* Returns a hash map of property names and property values * Returns a hash map of property names and property values
* *
...@@ -64,6 +81,7 @@ interface Tx_Extbase_DomainObject_DomainObjectInterface { ...@@ -64,6 +81,7 @@ interface Tx_Extbase_DomainObject_DomainObjectInterface {
* @internal * @internal
*/ */
public function _getProperties(); public function _getProperties();
/** /**
* Returns a hash map of dirty properties and $values * Returns a hash map of dirty properties and $values
* *
......
...@@ -34,6 +34,11 @@ ...@@ -34,6 +34,11 @@
*/ */
abstract class Tx_Extbase_MVC_Controller_AbstractController implements Tx_Extbase_MVC_Controller_ControllerInterface { abstract class Tx_Extbase_MVC_Controller_AbstractController implements Tx_Extbase_MVC_Controller_ControllerInterface {
/**
* @var Tx_Extbase_Object_ManageInterface
*/
protected $objectManager;
/** /**
* @var Tx_Extbase_MVC_Web_Routing_URIBuilder * @var Tx_Extbase_MVC_Web_Routing_URIBuilder
*/ */
...@@ -88,13 +93,16 @@ abstract class Tx_Extbase_MVC_Controller_AbstractController implements Tx_Extbas ...@@ -88,13 +93,16 @@ abstract class Tx_Extbase_MVC_Controller_AbstractController implements Tx_Extbas
* (additional) request types. * (additional) request types.
* @var array * @var array
*/ */
protected $supportedRequestTypes = array('Tx_Extbase_MVC_Web_Request'); protected $supportedRequestTypes = array('Tx_Extbase_MVC_Request');
/** /**
* Constructs the controller. * Constructs the controller.
*/ */
public function __construct() { public function __construct() {
$this->objectManager = t3lib_div::makeInstance('Tx_Extbase_Object_Manager');
$this->arguments = t3lib_div::makeInstance('Tx_Extbase_MVC_Controller_Arguments'); $this->arguments = t3lib_div::makeInstance('Tx_Extbase_MVC_Controller_Arguments');
$this->arguments->injectPersistenceManager(t3lib_div::makeInstance('Tx_Extbase_Persistence_Manager'));
$this->arguments->injectQueryFactory(t3lib_div::makeInstance('Tx_Extbase_Persistence_QueryFactory'));
list(, $this->extensionName) = explode('_', get_class($this)); list(, $this->extensionName) = explode('_', get_class($this));
} }
...@@ -314,4 +322,4 @@ abstract class Tx_Extbase_MVC_Controller_AbstractController implements Tx_Extbas ...@@ -314,4 +322,4 @@ abstract class Tx_Extbase_MVC_Controller_AbstractController implements Tx_Extbas
$this->argumentsMappingResults = $this->propertyMapper->getMappingResults(); $this->argumentsMappingResults = $this->propertyMapper->getMappingResults();
} }
} }
?> ?>
\ No newline at end of file
...@@ -34,7 +34,12 @@ ...@@ -34,7 +34,12 @@
* @scope prototype * @scope prototype
*/ */
class Tx_Extbase_MVC_Controller_Argument { class Tx_Extbase_MVC_Controller_Argument {
/**
* @var Tx_Extbase_Persistence_ManagerInterface
*/
protected $persistenceManager;
/** /**
* @var Tx_Extbase_Persistence_QueryFactory * @var Tx_Extbase_Persistence_QueryFactory
*/ */
...@@ -101,7 +106,6 @@ class Tx_Extbase_MVC_Controller_Argument { ...@@ -101,7 +106,6 @@ class Tx_Extbase_MVC_Controller_Argument {
* @throws InvalidArgumentException if $name is not a string or empty * @throws InvalidArgumentException if $name is not a string or empty
*/ */
public function __construct($name, $dataType = 'Text') { public function __construct($name, $dataType = 'Text') {
// $this->queryFactory = t3lib_div::makeInstance('Tx_Extbase_Persistence_QueryFactory');
$this->propertyMapper = t3lib_div::makeInstance('Tx_Extbase_Property_Mapper'); $this->propertyMapper = t3lib_div::makeInstance('Tx_Extbase_Property_Mapper');
if (!is_string($name) || strlen($name) < 1) throw new InvalidArgumentException('$name must be of type string, ' . gettype($name) . ' given.', 1187951688); if (!is_string($name) || strlen($name) < 1) throw new InvalidArgumentException('$name must be of type string, ' . gettype($name) . ' given.', 1187951688);
$this->name = $name; $this->name = $name;
...@@ -111,6 +115,28 @@ class Tx_Extbase_MVC_Controller_Argument { ...@@ -111,6 +115,28 @@ class Tx_Extbase_MVC_Controller_Argument {
$this->setDataType($dataType); $this->setDataType($dataType);
} }
} }
/**
* Injects the Persistence Manager
*
* @param Tx_Extbase_Persistence_ManagerInterface
* @return void
* @internal
*/
public function injectPersistenceManager(Tx_Extbase_Persistence_ManagerInterface $persistenceManager) {
$this->persistenceManager = $persistenceManager;
}
/**
* Injects a QueryFactory instance
*
* @param Tx_Extbase_Persistence_QueryFactoryInterface $queryFactory
* @return void
* @internal
*/
public function injectQueryFactory(Tx_Extbase_Persistence_QueryFactoryInterface $queryFactory) {
$this->queryFactory = $queryFactory;
}
/** /**
* Returns the name of this argument * Returns the name of this argument
...@@ -219,9 +245,9 @@ class Tx_Extbase_MVC_Controller_Argument { ...@@ -219,9 +245,9 @@ class Tx_Extbase_MVC_Controller_Argument {
* @param array Object names of the validators * @param array Object names of the validators
* @return Tx_Extbase_MVC_Controller_Argument Returns $this (used for fluent interface) * @return Tx_Extbase_MVC_Controller_Argument Returns $this (used for fluent interface)
*/ */
public function setNewValidatorChain(array $objectNames) { public function setNewValidatorConjunction(array $objectNames) {
if ($this->validator === NULL) { if ($this->validator === NULL) {
$this->validator = t3lib_div::makeInstance('Tx_Extbase_Validation_Validator_ChainValidator'); $this->validator = t3lib_div::makeInstance('Tx_Extbase_Validation_Validator_ConjunctionValidator');
} }
foreach ($objectNames as $objectName) { foreach ($objectNames as $objectName) {
if (!class_exists($objectName)) $objectName = 'Tx_Extbase_Validation_Validator_' . $objectName; if (!class_exists($objectName)) $objectName = 'Tx_Extbase_Validation_Validator_' . $objectName;
...@@ -235,7 +261,7 @@ class Tx_Extbase_MVC_Controller_Argument { ...@@ -235,7 +261,7 @@ class Tx_Extbase_MVC_Controller_Argument {
* @return Tx_Extbase_Validation_Validator_ValidatorInterface The set validator, NULL if none was set * @return Tx_Extbase_Validation_Validator_ValidatorInterface The set validator, NULL if none was set
*/ */
public function getValidator() { public function getValidator() {
return $this->validator; return $this->validator;
} }
/** /**
...@@ -277,18 +303,15 @@ class Tx_Extbase_MVC_Controller_Argument { ...@@ -277,18 +303,15 @@ class Tx_Extbase_MVC_Controller_Argument {
* @return mixed Either the object matching the uid or, if none or more than one object was found, FALSE * @return mixed Either the object matching the uid or, if none or more than one object was found, FALSE
*/ */
protected function findObjectByUid($uid) { protected function findObjectByUid($uid) {
$repositoryClassName = $this->dataType . 'Repository'; $query = $this->queryFactory->create($this->dataType);
if (class_exists($repositoryClassName)) { $object = current($query->matching($query->withUid($uid))->execute());
$repository = t3lib_div::makeInstance($this->dataType . 'Repository'); // TODO Check if the object is an Aggregate Root (this can be quite difficult because we have no Repository registration
$object = $repository->findOneByUid($uid); if ($object !== NULL) {
} $this->persistenceManager->getSession()->registerReconstitutedObject($object);
return $object; return $object;
// TODO replace code as soon as the query object is available } else {
// $query = $this->queryFactory->create($this->dataType); return FALSE;
// $query->matching('uid=' . intval($uid)); }
// $objects = $query->execute();
// if (count($objects) === 1 ) return current($objects);
// return FALSE;
} }
/** /**
......
...@@ -35,11 +35,42 @@ ...@@ -35,11 +35,42 @@
*/ */
class Tx_Extbase_MVC_Controller_Arguments extends ArrayObject { class Tx_Extbase_MVC_Controller_Arguments extends ArrayObject {
/**
* @var Tx_Extbase_Persistence_ManagerInterface
*/
protected $persistenceManager;
/**
* @var Tx_Extbase_Persistence_QueryFactory
*/
protected $queryFactory;
/** /**
* @var array Names of the arguments contained by this object * @var array Names of the arguments contained by this object
*/