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 @@
* @package Extbase
* @subpackage Controller
* @version $ID:$
* @todo - is this ever called?
*/
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
*
......@@ -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) {
$response->setContent(
"\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" .
"how to create and configure one.\n\n" .
"Have fun! The TYPO3 Development Team\n"
......
......@@ -31,6 +31,7 @@
* @version $ID:$
*/
class Tx_Extbase_Dispatcher {
/**
* @var Tx_Extbase_Reflection_Service
*/
......@@ -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);
return $content;
}
$requestBuilder = t3lib_div::makeInstance('Tx_Extbase_MVC_Web_RequestBuilder');
$request = $requestBuilder->initialize($configuration);
$request = $requestBuilder->build();
$response = t3lib_div::makeInstance('Tx_Extbase_MVC_Web_Response');
$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;
while (!$request->isDispatched()) {
......@@ -62,13 +76,11 @@ class Tx_Extbase_Dispatcher {
$controller->processRequest($request, $response);
} catch (Tx_Extbase_Exception_StopAction $ignoredException) {
} catch (Tx_Extbase_Exception_InvalidArgumentValue $exception) {
$persistenceSession->clear();
return '';
}
}
$persistenceSession->commit();
$persistenceSession->clear();
$persistenceManager->persistAll();
$this->reflectionService->shutdown();
if (count($response->getAdditionalHeaderData()) > 0) {
$GLOBALS['TSFE']->additionalHeaderData[$request->getControllerExtensionName()] = implode("\n", $response->getAdditionalHeaderData());
......@@ -110,6 +122,7 @@ class Tx_Extbase_Dispatcher {
$this->reflectionService->initialize();
}
$validatorResolver = t3lib_div::makeInstance('Tx_Extbase_Validation_ValidatorResolver');
$validatorResolver->injectObjectManager(t3lib_div::makeInstance('Tx_Extbase_Object_Manager'));
$validatorResolver->injectReflectionService($this->reflectionService);
$controller->injectValidatorResolver($validatorResolver);
$controller->injectReflectionService($this->reflectionService);
......@@ -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
* @internal
*/
public function __wakeup() {
foreach ($GLOBALS['Extbase']['reconstituteObject']['properties'] as $propertyName => $propertyValue) {
$this->_reconstituteProperty($propertyName, $propertyValue);
}
$this->initializeObject();
}
......@@ -73,48 +70,57 @@ abstract class Tx_Extbase_DomainObject_AbstractDomainObject implements Tx_Extbas
/**
* Getter for uid
*
* @return string
* @return int
*/
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 $value
* @return void
* @internal
*/
public function _reconstituteProperty($propertyName, $value) {
public function _setProperty($propertyName, $propertyValue) {
if (property_exists($this, $propertyName)) {
$this->$propertyName = $value;
$this->$propertyName = $propertyValue;
return TRUE;
}
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
*/
public function _getProperties() {
$properties = get_object_vars($this);
// unset($properties['_cleanProperties']); // TODO Check this again
return $properties;
public function _getProperty($propertyName) {
return $this->$propertyName;
}
/**
* 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
*/
public function _getPropertyValue($propertyName) {
return $this->$propertyName;
public function _getProperties() {
$properties = get_object_vars($this);
unset($properties['_cleanProperties']);
return $properties;
}
/**
......
......@@ -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
* 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 _memorizeCleanState() {
public function _memorizeCleanState($propertyName = NULL) {
// TODO Remove dependency to $dataMapper
$dataMapper = t3lib_div::makeInstance('Tx_Extbase_Persistence_Mapper_ObjectRelationalMapper'); // singleton
$this->_cleanProperties = array();
$properties = get_object_vars($this);
foreach ($properties as $propertyName => $propertyValue) {
if ($dataMapper->isPersistableProperty(get_class($this), $propertyName)) {
$this->_cleanProperties[$propertyName] = $this->$propertyName;
if ($propertyName !== NULL) {
} else {
$dataMapper = t3lib_div::makeInstance('Tx_Extbase_Persistence_Mapper_DataMapper'); // singleton
$this->_cleanProperties = array();
$properties = get_object_vars($this);
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
*
......
......@@ -32,23 +32,21 @@
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
* @internal
*/
public function _reconstituteProperty($propertyName, $value);
public function _memorizeCleanState();
/**
* Register an object's clean state, e.g. after it has been reconstituted
* from the database
* Returns TRUE if the object is new (the uid was not set, yet). Only for internal use
*
* @return void
* @return boolean
* @internal
*/
public function _memorizeCleanState();
public function _isNew();
/**
* Returns TRUE if the properties were modified after reconstitution
......@@ -57,6 +55,25 @@ interface Tx_Extbase_DomainObject_DomainObjectInterface {
* @internal
*/
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
*
......@@ -64,6 +81,7 @@ interface Tx_Extbase_DomainObject_DomainObjectInterface {
* @internal
*/
public function _getProperties();
/**
* Returns a hash map of dirty properties and $values
*
......
......@@ -34,6 +34,11 @@
*/
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
*/
......@@ -88,13 +93,16 @@ abstract class Tx_Extbase_MVC_Controller_AbstractController implements Tx_Extbas
* (additional) request types.
* @var array
*/
protected $supportedRequestTypes = array('Tx_Extbase_MVC_Web_Request');
protected $supportedRequestTypes = array('Tx_Extbase_MVC_Request');
/**
* Constructs the controller.
*/
public function __construct() {
$this->objectManager = t3lib_div::makeInstance('Tx_Extbase_Object_Manager');
$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));
}
......@@ -314,4 +322,4 @@ abstract class Tx_Extbase_MVC_Controller_AbstractController implements Tx_Extbas
$this->argumentsMappingResults = $this->propertyMapper->getMappingResults();
}
}
?>
\ No newline at end of file
?>
......@@ -34,7 +34,12 @@
* @scope prototype
*/
class Tx_Extbase_MVC_Controller_Argument {
/**
* @var Tx_Extbase_Persistence_ManagerInterface
*/
protected $persistenceManager;
/**
* @var Tx_Extbase_Persistence_QueryFactory
*/
......@@ -101,7 +106,6 @@ class Tx_Extbase_MVC_Controller_Argument {
* @throws InvalidArgumentException if $name is not a string or empty
*/
public function __construct($name, $dataType = 'Text') {
// $this->queryFactory = t3lib_div::makeInstance('Tx_Extbase_Persistence_QueryFactory');
$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);
$this->name = $name;
......@@ -111,6 +115,28 @@ class Tx_Extbase_MVC_Controller_Argument {
$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
......@@ -219,9 +245,9 @@ class Tx_Extbase_MVC_Controller_Argument {
* @param array Object names of the validators
* @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) {
$this->validator = t3lib_div::makeInstance('Tx_Extbase_Validation_Validator_ChainValidator');
$this->validator = t3lib_div::makeInstance('Tx_Extbase_Validation_Validator_ConjunctionValidator');
}
foreach ($objectNames as $objectName) {
if (!class_exists($objectName)) $objectName = 'Tx_Extbase_Validation_Validator_' . $objectName;
......@@ -235,7 +261,7 @@ class Tx_Extbase_MVC_Controller_Argument {
* @return Tx_Extbase_Validation_Validator_ValidatorInterface The set validator, NULL if none was set
*/
public function getValidator() {
return $this->validator;
return $this->validator;
}
/**
......@@ -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
*/
protected function findObjectByUid($uid) {
$repositoryClassName = $this->dataType . 'Repository';
if (class_exists($repositoryClassName)) {
$repository = t3lib_div::makeInstance($this->dataType . 'Repository');
$object = $repository->findOneByUid($uid);
}
return $object;
// TODO replace code as soon as the query object is available
// $query = $this->queryFactory->create($this->dataType);
// $query->matching('uid=' . intval($uid));
// $objects = $query->execute();
// if (count($objects) === 1 ) return current($objects);
// return FALSE;
$query = $this->queryFactory->create($this->dataType);
$object = current($query->matching($query->withUid($uid))->execute());
// TODO Check if the object is an Aggregate Root (this can be quite difficult because we have no Repository registration
if ($object !== NULL) {
$this->persistenceManager->getSession()->registerReconstitutedObject($object);
return $object;
} else {
return FALSE;
}
}
/**
......
......@@ -35,11 +35,42 @@
*/
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
*/
protected $argumentNames = array();
/**
* Injects the persistence manager
*
* @param Tx_Extbase_Persistence_ManagerInterface $persistenceManager
* @return void
*/
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;
}
/**
* Adds or replaces the argument specified by $value. The argument's name is taken from the
* argument object itself, therefore the $offset does not have any meaning in this context.
......@@ -122,9 +153,10 @@ class Tx_Extbase_MVC_Controller_Arguments extends ArrayObject {
*/
public function addNewArgument($name, $dataType = 'Text', $isRequired = FALSE, $defaultValue = NULL) {
$argument = new Tx_Extbase_MVC_Controller_Argument($name, $dataType);
$argument->injectPersistenceManager($this->persistenceManager);
$argument->injectQueryFactory($this->queryFactory);
$argument->setRequired($isRequired);
$argument->setDefaultValue($defaultValue);
$this->addArgument($argument);
return $argument;
}
......@@ -229,5 +261,18 @@ class Tx_Extbase_MVC_Controller_Arguments extends ArrayObject {
}
return '';
}
/**
* Remove all arguments and resets this object
*
* @return void
* @internal
*/
public function removeAll() {
foreach ($this->argumentNames as $argumentName => $booleanValue) {
parent::offsetUnset($argumentName);
}
$this->argumentNames = array();
}
}
?>
\ No newline at end of file
......@@ -66,7 +66,6 @@ class Tx_Extbase_MVC_Controller_ControllerContext {
*
* @param Tx_Extbase_MVC_Request $request
* @return void
* @author Christopher Hlubek <hlubek@networkteam.com>
* @internal
*/
public function setRequest(Tx_Extbase_MVC_Request $request) {
......@@ -77,7 +76,6 @@ class Tx_Extbase_MVC_Controller_ControllerContext {
* Get the request of the controller
*
* @return Tx_Extbase_MVC_Request
* @author Christopher Hlubek <hlubek@networkteam.com>
*/
public function getRequest() {
return $this->request;
......@@ -88,7 +86,6 @@ class Tx_Extbase_MVC_Controller_ControllerContext {
*
* @param Tx_Extbase_MVC_Response $request
* @return void
* @author Christopher Hlubek <hlubek@networkteam.com>
* @internal
*/
public function setResponse(Tx_Extbase_MVC_Response $response) {
......@@ -99,7 +96,6 @@ class Tx_Extbase_MVC_Controller_ControllerContext {
* Get the response of the controller
*
* @return Tx_Extbase_MVC_Request
* @author Christopher Hlubek <hlubek@networkteam.com>
*/
public function getResponse() {
return $this->response;
......@@ -110,7 +106,6 @@ class Tx_Extbase_MVC_Controller_ControllerContext {
*
* @param Tx_Extbase_MVC_Controller_Arguments $arguments
* @return void
* @author Christopher Hlubek <hlubek@networkteam.com>
* @internal
*/
public function setArguments(Tx_Extbase_MVC_Controller_Arguments $arguments) {
......@@ -121,7 +116,6 @@ class Tx_Extbase_MVC_Controller_ControllerContext {
* Get the arguments of the controller
*
* @return Tx_Extbase_MVC_Controller_Arguments
* @author Christopher Hlubek <hlubek@networkteam.com>
*/
public function getArguments() {
return $this->arguments;
......@@ -132,7 +126,6 @@ class Tx_Extbase_MVC_Controller_ControllerContext {
*
* @param Tx_Extbase_Property_MappingResults $argumentsMappingResults
* @return void
* @author Christopher Hlubek <hlubek@networkteam.com>
* @internal
*/
public function setArgumentsMappingResults(Tx_Extbase_Property_MappingResults $argumentsMappingResults) {
......@@ -143,7 +136,6 @@ class Tx_Extbase_MVC_Controller_ControllerContext {
* Get the arguments mapping results of the controller
*
* @return Tx_Extbase_Property_MappingResults
* @author Christopher Hlubek <hlubek@networkteam.com>
*/
public function getArgumentsMappingResults() {
return $this->argumentsMappingResults;
......@@ -152,7 +144,6 @@ class Tx_Extbase_MVC_Controller_ControllerContext {
/**
* Tx_Extbase_MVC_Web_Routing_URIBuilder $URIBuilder
* @return void
* @author Bastian Waidelich <bastian@typo3.org>
* @internal
*/
public function setURIBuilder(Tx_Extbase_MVC_Web_Routing_URIBuilder $URIBuilder) {
......@@ -161,7 +152,6 @@ class Tx_Extbase_MVC_Controller_ControllerContext {
/**
* @return Tx_Extbase_MVC_Web_Routing_URIBuilder
* @author Bastian Waidelich <bastian@typo3.org>
*/
public function getURIBuilder() {
return $this->URIBuilder;
......
<?php
/***************************************************************
* Copyright notice
*
* (c) 2009 Jochen Rau <jochen.rau@typoplanet.de>
* All rights reserved
*
* This class is a backport of the corresponding class of FLOW3.
* All credits go to the v5 team.
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.