* introduced a tca mapper
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Persistence / Mapper / TX_EXTMVC_Persistence_Mapper_TcaMapper.php
1 <?php
2 declare(ENCODING = 'utf-8');
3
4 /* *
5 * This script belongs to the FLOW3 framework. *
6 * *
7 * It is free software; you can redistribute it and/or modify it under *
8 * the terms of the GNU Lesser General Public License as published by the *
9 * Free Software Foundation, either version 3 of the License, or (at your *
10 * option) any later version. *
11 * *
12 * This script is distributed in the hope that it will be useful, but *
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- *
14 * TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser *
15 * General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU Lesser General Public *
18 * License along with the script. *
19 * If not, see http://www.gnu.org/licenses/lgpl.html *
20 * *
21 * The TYPO3 project - inspiring people to share! *
22 * */
23
24 require_once(PATH_t3lib . 'interfaces/interface.t3lib_singleton.php');
25 require_once(t3lib_extMgm::extPath('extmvc') . 'Classes/Utility/TX_EXTMVC_Utility_Strings.php');
26
27 /**
28 * A mapper to map database tables configured in $TCA onto domain objects.
29 *
30 * @version $Id:$
31 * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License, version 3 or later
32 */
33 class TX_EXTMVC_Persistence_Mapper_TcaMapper implements t3lib_singleton {
34
35 /**
36 * The content object
37 *
38 * @var tslib_cObj
39 **/
40 protected $cObj;
41
42 /**
43 * Constructs a new mapper
44 *
45 * @author Jochen Rau <jochen.rau@typoplanet.de>
46 */
47 public function __construct() {
48 $this->cObj = t3lib_div::makeInstance('tslib_cObj');
49 $GLOBALS['TSFE']->includeTCA();
50 }
51
52 /**
53 * Returns all objects of the given class name
54 *
55 * @return array An array of objects, empty if no objects found
56 * @author Jochen Rau <jochen.rau@typoplanet.de>
57 */
58 public function loadAll($className) {
59 return $this->reconstituteObjects($this->fetch($this->getTableName($className)));
60 }
61
62 /**
63 * Finds objects matching 'property=xyz'
64 *
65 * @param string $propertyName The name of the property (will be chekced by a white list)
66 * @param string $arguments The arguments of the magic findBy method
67 * @return void
68 * @author Jochen Rau <jochen.rau@typoplanet.de>
69 */
70 public function loadWhere($className, $propertyName, $arguments) {
71 $tableName = $this->getTableName($className);
72 $where = $propertyName . '=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($arguments[0], $tableName);
73 return $this->reconstituteObjects($className, $this->fetch($tableName, $where));
74 }
75
76 /**
77 * Fetches a rows from the database by given SQL statement snippets
78 *
79 * @param string $from FROM statement
80 * @param string $where WHERE statement
81 * @param string $groupBy GROUP BY statement
82 * @param string $orderBy ORDER BY statement
83 * @param string $limit LIMIT statement
84 * @return void
85 * @author Jochen Rau <jochen.rau@typoplanet.de>
86 */
87 private function fetch($tableName, $where = '1=1', $groupBy = NULL, $orderBy = NULL, $limit = NULL) {
88 $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
89 '*', // TODO limit fetched fields
90 $tableName,
91 $where . $this->cObj->enableFields($tableName) . $this->cObj->enableFields($tableName),
92 $groupBy,
93 $orderBy,
94 $limit
95 );
96 // TODO language overlay; workspace overlay
97 return $rows ? $rows : array();
98 }
99
100 /**
101 * Fetches a rows from the database by given SQL statement snippets
102 *
103 * @author Jochen Rau <jochen.rau@typoplanet.de>
104 */
105 private function fetchOneToMany($parentObject, $parentField, $tableName, $where = '', $groupBy = NULL, $orderBy = NULL, $limit = NULL) {
106 $where .= ' ' . $parentField . '=' . intval($parentObject->getUid());
107 return $this->fetch($tableName, $where, $groupBy, $orderBy, $limit);
108 }
109
110 /**
111 * Fetches a rows from the database by given SQL statement snippets
112 *
113 * @author Jochen Rau <jochen.rau@typoplanet.de>
114 */
115 private function fetchManyToMany($parentObject, $foreignTableName, $relationTableName, $where = '1=1', $groupBy = NULL, $orderBy = NULL, $limit = NULL) {
116 $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
117 $foreignTableName . '.*, ' . $relationTableName . '.*',
118 $foreignTableName . ' LEFT JOIN ' . $relationTableName . ' ON (' . $foreignTableName . '.uid=' . $relationTableName . '.uid_foreign)',
119 $where . ' AND ' . $relationTableName . '.uid_local=' . intval($parentObject->getUid()) . $this->cObj->enableFields($foreignTableName) . $this->cObj->enableFields($foreignTableName),
120 $groupBy,
121 $orderBy,
122 $limit
123 );
124 // TODO language overlay; workspace overlay
125 return $rows ? $rows : array();
126 }
127
128 /**
129 * Dispatches the reconstitution of a domain object to an appropriate method
130 *
131 * @param array $rows The rows array fetched from the database
132 * @throws TX_EXTMVC_Persistence_Exception_RecursionTooDeep
133 * @return array An array of reconstituted domain objects
134 * @author Jochen Rau <jochen.rau@typoplanet.de>
135 */
136 protected function reconstituteObjects($className, array $rows, $depth = 0) {
137 if ($depth > 10) throw new TX_EXTMVC_Persistence_Exception_RecursionTooDeep('The maximum depth of ' . $depth . ' recursions was reached.', 1233352348);
138 foreach ($rows as $row) {
139 $object = $this->reconstituteObject($className, $row);
140 foreach ($object->getOneToManyRelations() as $propertyName => $tcaColumnConfiguration) {
141 $relatedRows = $this->fetchOneToMany($object, $tcaColumnConfiguration['foreign_field'], $tcaColumnConfiguration['foreign_table']);
142 $relatedObjects = $this->reconstituteObjects($relatedRows, $tcaColumnConfiguration['foreign_class'], $depth++);
143 $object->_reconstituteProperty($propertyName, $relatedObjects);
144 }
145 foreach ($object->getManyToManyRelations() as $propertyName => $tcaColumnConfiguration) {
146 $relatedRows = $this->fetchManyToMany($object, $tcaColumnConfiguration['foreign_table'], $tcaColumnConfiguration['MM']);
147 $relatedObjects = $this->reconstituteObjects($relatedRows, $tcaColumnConfiguration['foreign_class'], $depth++);
148 $object->_reconstituteProperty($propertyName, $relatedObjects);
149 }
150 $objects[] = $object;
151 }
152 return $objects;
153 }
154
155 /**
156 * Reconstitutes the specified object and fills it with the given properties.
157 *
158 * @param string $objectName Name of the object to reconstitute
159 * @param array $properties The names of properties and their values which should be set during the reconstitution
160 * @return object The reconstituted object
161 * @author Robert Lemke <robert@typo3.org>
162 * @author Jochen Rau <jochen.rau@typoplanet.de>
163 */
164 protected function reconstituteObject($className, array $properties = array()) {
165 // those objects will be fetched from within the __wakeup() method of the object...
166 $GLOBALS['EXTMVC']['reconstituteObject']['properties'] = $properties;
167 $object = unserialize('O:' . strlen($className) . ':"' . $className . '":0:{};');
168 unset($GLOBALS['EXTMVC']['reconstituteObject']);
169 return $object;
170 }
171
172 /**
173 * Inserts an object in the database.
174 *
175 * @return void
176 * @author Jochen Rau <jochen.rau@typoplanet.de>
177 */
178 public function insert(TX_EXTMVC_DomainObject_AbstractDomainObject $object) {
179 $row = array(
180 'pid' => 0, // FIXME
181 'tstamp' => time(),
182 'crdate' => time(),
183 // FIXME static fields
184 'name' => $object->getName(),
185 'description' => $object->getDescription()
186 // 'logo' => $object->getLogo(),
187 );
188 $res = $GLOBALS['TYPO3_DB']->exec_INSERTquery(
189 $this->getTableName($this->getClassName($object)),
190 $row
191 );
192 }
193
194 /**
195 * Updates an object
196 *
197 * @return void
198 * @author Jochen Rau <jochen.rau@typoplanet.de>
199 */
200 public function update(TX_EXTMVC_DomainObject_AbstractDomainObject $object) {
201
202 }
203
204 /**
205 * Deletes an object
206 *
207 * @return void
208 * @author Jochen Rau <jochen.rau@typoplanet.de>
209 */
210 public function delete(TX_EXTMVC_DomainObject_AbstractDomainObject $object, $onlyMarkAsDeleted = TRUE) {
211 $tableName = $this->getTableName($this->getClassName($object));
212 if ($onlyMarkAsDeleted) {
213 $deletedColumnName = $this->getDeletedColumnName($tableName);
214 if (empty($deletedColumnName)) throw new Exception('Could not mark object as deleted in table "' . $tableName . '"');
215 $res = $GLOBALS['TYPO3_DB']->exec_UPDATEquery(
216 $this->getTableName($object),
217 'uid = ' . intval($object->getUid()),
218 array($deletedColumnName => 1)
219 );
220 } else {
221 // TODO remove associated objects
222
223 $res = $GLOBALS['TYPO3_DB']->exec_DELETEquery(
224 $this->getTableName($object),
225 'uid=' . intval($object->getUid())
226 );
227 }
228 }
229
230 public function getColumns(TX_EXTMVC_DomainObject_AbstractDomainObject $object) {
231 t3lib_div::loadTCA($this->getTableName($this->getClassName($object)));
232 return $GLOBALS['TCA'][$this->getTableName($this->getClassName($object))]['columns'];
233 }
234
235 protected function getClassName(TX_EXTMVC_DomainObject_AbstractDomainObject $object) {
236 return get_class($object);
237 }
238
239 protected function getTableName($className) {
240 return strtolower($className);
241 }
242
243 protected function getDeletedColumnName($tableName) {
244 return $GLOBALS['TCA'][$tableName]['ctrl']['delete'];
245 }
246
247 protected function getHiddenColumnName($tableName) {
248 return $GLOBALS['TCA'][$tableNAme]['ctrl']['enablecolumns']['disabled'];
249 }
250
251 public function isPersistable(TX_EXTMVC_DomainObject_AbstractDomainObject $object, $propertyName) {
252 $columns = $this->getColumns($object);
253 foreach ($columns as $columnName => $columnConfiguration) {
254 if (TX_EXTMVC_Utility_Strings::camelCaseToLowerCaseUnderscored($columnName) == $propertyName) return TRUE;
255 }
256 return FALSE;
257 }
258
259 }
260 ?>