2 /***************************************************************
5 * (c) 1999-2009 Kasper Skaarhoj (kasperYYYY@typo3.com)
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 * A copy is found in the textfile GPL.txt and important notices to the license
17 * from the author is found in LICENSE.txt distributed with these scripts.
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
28 * Contains class for loading database groups
31 * Revised for TYPO3 3.6 September/2003 by Kasper Skaarhoj
33 * @author Kasper Skaarhoj <kasperYYYY@typo3.com>
36 * [CLASS/FUNCTION INDEX of SCRIPT]
40 * 76: class t3lib_loadDBGroup
41 * 111: function start($itemlist, $tablelist, $MMtable='', $MMuid=0, $currentTable='', $conf=array())
42 * 179: function readList($itemlist)
43 * 225: function readMM($tableName,$uid)
44 * 276: function writeMM($tableName,$uid,$prependTableName=0)
45 * 352: function readForeignField($uid, $conf)
46 * 435: function writeForeignField($conf, $parentUid, $updateToUid=0)
47 * 510: function getValueArray($prependTableName='')
48 * 538: function convertPosNeg($valueArray,$fTable,$nfTable)
49 * 560: function getFromDB()
50 * 595: function readyForInterface()
51 * 621: function countItems($returnAsArray = true)
52 * 636: function updateRefIndex($table,$id)
55 * (This index is automatically created/updated by the extension "extdeveval")
69 * Load database groups (relations)
70 * Used to process the relations created by the TCA element types "group" and "select" for database records. Manages MM-relations as well.
72 * @author Kasper Skaarhoj <kasperYYYY@typo3.com>
76 class t3lib_loadDBGroup
{
78 var $fromTC = 1; // Means that only uid and the label-field is returned
79 var $registerNonTableValues=0; // If set, values that are not ids in tables are normally discarded. By this options they will be preserved.
82 var $tableArray=Array(); // Contains the table names as keys. The values are the id-values for each table. Should ONLY contain proper table names.
83 var $itemArray=Array(); // Contains items in an numeric array (table/id for each). Tablenames here might be "_NO_TABLE"
84 var $nonTableArray=array(); // Array for NON-table elements
85 var $additionalWhere=array();
86 var $checkIfDeleted = 1; // deleted-column is added to additionalWhere... if this is set...
88 var $firstTable = ''; // Will contain the first table name in the $tablelist (for positive ids)
89 var $secondTable = ''; // Will contain the second table name in the $tablelist (for negative ids)
91 var $MM_is_foreign = 0; // boolean - if 1, uid_local and uid_foreign are switched, and the current table is inserted as tablename - this means you display a foreign relation "from the opposite side"
92 var $MM_oppositeField = ''; // field name at the "local" side of the MM relation
93 var $MM_oppositeTable = ''; // only set if MM_is_foreign is set
94 var $MM_oppositeFieldConf = ''; // only set if MM_is_foreign is set
95 var $MM_isMultiTableRelationship = 0; // is empty by default; if MM_is_foreign is set and there is more than one table allowed (on the "local" side), then it contains the first table (as a fallback)
96 var $currentTable; // current table => Only needed for reverse relations
97 var $undeleteRecord; // if a record should be undeleted (so do not use the $useDeleteClause on t3lib_BEfunc)
100 var $MM_match_fields = array(); // array of fields value pairs that should match while SELECT and will be written into MM table if $MM_insert_fields is not set
101 var $MM_insert_fields = array(); // array of fields and value pairs used for insert in MM table
102 var $MM_table_where = ''; // extra MM table where
106 * Initialization of the class.
108 * @param string List of group/select items
109 * @param string Comma list of tables, first table takes priority if no table is set for an entry in the list.
110 * @param string Name of a MM table.
111 * @param integer Local UID for MM lookup
112 * @param string current table name
113 * @param integer TCA configuration for current field
116 function start($itemlist, $tablelist, $MMtable='', $MMuid=0, $currentTable='', $conf=array()) {
117 // SECTION: MM reverse relations
118 $this->MM_is_foreign
= ($conf['MM_opposite_field']?
1:0);
119 $this->MM_oppositeField
= $conf['MM_opposite_field'];
120 $this->MM_table_where
= $conf['MM_table_where'];
121 $this->MM_hasUidField
= $conf['MM_hasUidField'];
122 $this->MM_match_fields
= is_array($conf['MM_match_fields']) ?
$conf['MM_match_fields'] : array();
123 $this->MM_insert_fields
= is_array($conf['MM_insert_fields']) ?
$conf['MM_insert_fields'] : $this->MM_match_fields
;
125 $this->currentTable
= $currentTable;
126 if ($this->MM_is_foreign
) {
127 $tmp = ($conf['type']==='group'?
$conf['allowed']:$conf['foreign_table']);
128 // normally, $conf['allowed'] can contain a list of tables, but as we are looking at a MM relation from the foreign side, it only makes sense to allow one one table in $conf['allowed']
129 $tmp = t3lib_div
::trimExplode(',', $tmp);
130 $this->MM_oppositeTable
= $tmp[0];
133 // only add the current table name if there is more than one allowed field
134 t3lib_div
::loadTCA($this->MM_oppositeTable
); // We must be sure this has been done at least once before accessing the "columns" part of TCA for a table.
135 $this->MM_oppositeFieldConf
= $GLOBALS['TCA'][$this->MM_oppositeTable
]['columns'][$this->MM_oppositeField
]['config'];
137 if ($this->MM_oppositeFieldConf
['allowed']) {
138 $oppositeFieldConf_allowed = explode(',', $this->MM_oppositeFieldConf
['allowed']);
139 if (count($oppositeFieldConf_allowed) > 1) {
140 $this->MM_isMultiTableRelationship
= $oppositeFieldConf_allowed[0];
145 // SECTION: normal MM relations
147 // If the table list is "*" then all tables are used in the list:
148 if (!strcmp(trim($tablelist),'*')) {
149 $tablelist = implode(',',array_keys($GLOBALS['TCA']));
152 // The tables are traversed and internal arrays are initialized:
153 $tempTableArray = t3lib_div
::trimExplode(',',$tablelist,1);
154 foreach($tempTableArray as $key => $val) {
156 $this->tableArray
[$tName] = Array();
157 if ($this->checkIfDeleted
&& $GLOBALS['TCA'][$tName]['ctrl']['delete']) {
158 $fieldN = $tName.'.'.$GLOBALS['TCA'][$tName]['ctrl']['delete'];
159 $this->additionalWhere
[$tName].=' AND '.$fieldN.'=0';
163 if (is_array($this->tableArray
)) {
164 reset($this->tableArray
);
165 } else {return 'No tables!';}
167 // Set first and second tables:
168 $this->firstTable
= key($this->tableArray
); // Is the first table
169 next($this->tableArray
);
170 $this->secondTable
= key($this->tableArray
); // If the second table is set and the ID number is less than zero (later) then the record is regarded to come from the second table...
172 // Now, populate the internal itemArray and tableArray arrays:
173 if ($MMtable) { // If MM, then call this function to do that:
175 $this->readMM($MMtable, $MMuid);
176 } else { // Revert to readList() for new records in order to load possible default values from $itemlist
177 $this->readList($itemlist);
179 } elseif ($MMuid && $conf['foreign_field']) {
180 // If not MM but foreign_field, the read the records by the foreign_field
181 $this->readForeignField($MMuid, $conf);
183 // If not MM, then explode the itemlist by "," and traverse the list:
184 $this->readList($itemlist);
185 // do automatic default_sortby, if any
186 if ($conf['foreign_default_sortby']) {
187 $this->sortList($conf['foreign_default_sortby']);
193 * Explodes the item list and stores the parts in the internal arrays itemArray and tableArray from MM records.
195 * @param string Item list
198 function readList($itemlist) {
199 if ((string)trim($itemlist)!='') {
200 $tempItemArray = t3lib_div
::trimExplode(',', $itemlist); // Changed to trimExplode 31/3 04; HMENU special type "list" didn't work if there were spaces in the list... I suppose this is better overall...
201 foreach($tempItemArray as $key => $val) {
202 $isSet = 0; // Will be set to "1" if the entry was a real table/id:
204 // Extract table name and id. This is un the formular [tablename]_[id] where table name MIGHT contain "_", hence the reversion of the string!
206 $parts = explode('_',$val,2);
207 $theID = strrev($parts[0]);
209 // Check that the id IS an integer:
210 if (t3lib_div
::testInt($theID)) {
211 // Get the table name: If a part of the exploded string, use that. Otherwise if the id number is LESS than zero, use the second table, otherwise the first table
212 $theTable = trim($parts[1]) ?
strrev(trim($parts[1])) : ($this->secondTable
&& $theID<0 ?
$this->secondTable
: $this->firstTable
);
213 // If the ID is not blank and the table name is among the names in the inputted tableList, then proceed:
214 if ((string)$theID!='' && $theID && $theTable && isset($this->tableArray
[$theTable])) {
215 // Get ID as the right value:
216 $theID = $this->secondTable ?
abs(intval($theID)) : intval($theID);
217 // Register ID/table name in internal arrays:
218 $this->itemArray
[$key]['id'] = $theID;
219 $this->itemArray
[$key]['table'] = $theTable;
220 $this->tableArray
[$theTable][] = $theID;
226 // If it turns out that the value from the list was NOT a valid reference to a table-record, then we might still set it as a NO_TABLE value:
227 if (!$isSet && $this->registerNonTableValues
) {
228 $this->itemArray
[$key]['id'] = $tempItemArray[$key];
229 $this->itemArray
[$key]['table'] = '_NO_TABLE';
230 $this->nonTableArray
[] = $tempItemArray[$key];
237 * Does a sorting on $this->itemArray depending on a default sortby field.
238 * This is only used for automatic sorting of comma separated lists.
239 * This function is only relevant for data that is stored in comma separated lists!
241 * @param string $sortby: The default_sortby field/command (e.g. 'price DESC')
244 function sortList($sortby) {
245 // sort directly without fetching addional data
246 if ($sortby == 'uid') {
247 usort($this->itemArray
, create_function('$a,$b', 'return $a["id"] < $b["id"] ? -1 : 1;'));
248 // only useful if working on the same table
249 } elseif (count($this->tableArray
) == 1) {
250 reset($this->tableArray
);
251 $table = key($this->tableArray
);
252 $uidList = implode(',', current($this->tableArray
));
255 $this->itemArray
= array();
256 $this->tableArray
= array();
258 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, 'uid IN ('.$uidList.')', '', $sortby);
259 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
260 $this->itemArray
[] = array('id' => $row['uid'], 'table' => $table);
261 $this->tableArray
[$table][] = $row['uid'];
263 $GLOBALS['TYPO3_DB']->sql_free_result($res);
269 * Reads the record tablename/id into the internal arrays itemArray and tableArray from MM records.
270 * You can call this function after start if you supply no list to start()
272 * @param string MM Tablename
273 * @param integer Local UID
276 function readMM($tableName,$uid) {
278 $additionalWhere = '';
280 if ($this->MM_is_foreign
) { // in case of a reverse relation
281 $uidLocal_field = 'uid_foreign';
282 $uidForeign_field = 'uid_local';
283 $sorting_field = 'sorting_foreign';
285 if ($this->MM_isMultiTableRelationship
) {
286 $additionalWhere .= ' AND ( tablenames=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($this->currentTable
, $tableName);
287 if ($this->currentTable
== $this->MM_isMultiTableRelationship
) { // be backwards compatible! When allowing more than one table after having previously allowed only one table, this case applies.
288 $additionalWhere .= ' OR tablenames=\'\'';
290 $additionalWhere .= ' ) ';
292 $theTable = $this->MM_oppositeTable
;
294 $uidLocal_field = 'uid_local';
295 $uidForeign_field = 'uid_foreign';
296 $sorting_field = 'sorting';
300 if ($this->MM_table_where
) {
301 $additionalWhere.= LF
.str_replace('###THIS_UID###', intval($uid), $this->MM_table_where
);
303 foreach ($this->MM_match_fields
as $field => $value) {
304 $additionalWhere.= ' AND '.$field.'='.$GLOBALS['TYPO3_DB']->fullQuoteStr($value, $tableName);
307 // Select all MM relations:
308 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', $tableName, $uidLocal_field.'='.intval($uid).$additionalWhere, '', $sorting_field);
309 while($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
310 if (!$this->MM_is_foreign
) { // default
311 $theTable = $row['tablenames'] ?
$row['tablenames'] : $this->firstTable
; // If tablesnames columns exists and contain a name, then this value is the table, else it's the firstTable...
313 if (($row[$uidForeign_field] ||
$theTable=='pages') && $theTable && isset($this->tableArray
[$theTable])) {
315 $this->itemArray
[$key]['id'] = $row[$uidForeign_field];
316 $this->itemArray
[$key]['table'] = $theTable;
317 $this->tableArray
[$theTable][]= $row[$uidForeign_field];
318 } elseif ($this->registerNonTableValues
) {
319 $this->itemArray
[$key]['id'] = $row[$uidForeign_field];
320 $this->itemArray
[$key]['table'] = '_NO_TABLE';
321 $this->nonTableArray
[] = $row[$uidForeign_field];
325 $GLOBALS['TYPO3_DB']->sql_free_result($res);
329 * Writes the internal itemArray to MM table:
331 * @param string MM table name
332 * @param integer Local UID
333 * @param boolean If set, then table names will always be written.
336 function writeMM($MM_tableName,$uid,$prependTableName=0) {
338 if ($this->MM_is_foreign
) { // in case of a reverse relation
339 $uidLocal_field = 'uid_foreign';
340 $uidForeign_field = 'uid_local';
341 $sorting_field = 'sorting_foreign';
343 $uidLocal_field = 'uid_local';
344 $uidForeign_field = 'uid_foreign';
345 $sorting_field = 'sorting';
348 // If there are tables...
349 $tableC = count($this->tableArray
);
351 $prep = ($tableC>1||
$prependTableName||
$this->MM_isMultiTableRelationship
) ?
1 : 0; // boolean: does the field "tablename" need to be filled?
354 $additionalWhere_tablenames = '';
355 if ($this->MM_is_foreign
&& $prep) {
356 $additionalWhere_tablenames = ' AND tablenames=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($this->currentTable
, $MM_tableName);
359 $additionalWhere = '';
360 // add WHERE clause if configured
361 if ($this->MM_table_where
) {
362 $additionalWhere.= LF
.str_replace('###THIS_UID###', intval($uid), $this->MM_table_where
);
364 // Select, update or delete only those relations that match the configured fields
365 foreach ($this->MM_match_fields
as $field => $value) {
366 $additionalWhere.= ' AND '.$field.'='.$GLOBALS['TYPO3_DB']->fullQuoteStr($value, $MM_tableName);
369 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
370 $uidForeign_field.($prep?
', tablenames':'').($this->MM_hasUidField?
', uid':''),
372 $uidLocal_field.'='.$uid.$additionalWhere_tablenames.$additionalWhere,
378 $oldMMs_inclUid = array(); // This array is similar to $oldMMs but also holds the uid of the MM-records, if any (configured by MM_hasUidField). If the UID is present it will be used to update sorting and delete MM-records. This is necessary if the "multiple" feature is used for the MM relations. $oldMMs is still needed for the in_array() search used to look if an item from $this->itemArray is in $oldMMs
379 while($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
380 if (!$this->MM_is_foreign
&& $prep) {
381 $oldMMs[] = array($row['tablenames'], $row[$uidForeign_field]);
383 $oldMMs[] = $row[$uidForeign_field];
385 $oldMMs_inclUid[] = array($row['tablenames'], $row[$uidForeign_field], $row['uid']);
388 // For each item, insert it:
389 foreach($this->itemArray
as $val) {
392 if ($prep ||
$val['table']=='_NO_TABLE') {
393 if ($this->MM_is_foreign
) { // insert current table if needed
394 $tablename = $this->currentTable
;
396 $tablename = $val['table'];
402 if(!$this->MM_is_foreign
&& $prep) {
403 $item = array($val['table'], $val['id']);
408 if (in_array($item, $oldMMs)) {
409 $oldMMs_index = array_search($item, $oldMMs);
411 $whereClause = $uidLocal_field.'='.$uid.' AND '.$uidForeign_field.'='.$val['id'].
412 ($this->MM_hasUidField ?
' AND uid='.intval($oldMMs_inclUid[$oldMMs_index][2]) : ''); // In principle, selecting on the UID is all we need to do if a uid field is available since that is unique! But as long as it "doesn't hurt" we just add it to the where clause. It should all match up.
414 $whereClause .= ' AND tablenames=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($tablename, $MM_tableName);
416 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($MM_tableName, $whereClause.$additionalWhere, array($sorting_field => $c));
418 unset($oldMMs[$oldMMs_index]); // remove the item from the $oldMMs array so after this foreach loop only the ones that need to be deleted are in there.
419 unset($oldMMs_inclUid[$oldMMs_index]); // remove the item from the $oldMMs array so after this foreach loop only the ones that need to be deleted are in there.
422 $insertFields = $this->MM_insert_fields
;
423 $insertFields[$uidLocal_field] = $uid;
424 $insertFields[$uidForeign_field] = $val['id'];
425 $insertFields[$sorting_field] = $c;
427 $insertFields['tablenames'] = $tablename;
430 $GLOBALS['TYPO3_DB']->exec_INSERTquery($MM_tableName, $insertFields);
432 if ($this->MM_is_foreign
) {
433 $this->updateRefIndex($val['table'], $val['id']);
438 // Delete all not-used relations:
439 if(is_array($oldMMs) && count($oldMMs) > 0) {
440 $removeClauses = array();
441 $updateRefIndex_records = array();
442 foreach($oldMMs as $oldMM_key => $mmItem) {
443 if ($this->MM_hasUidField
) { // If UID field is present, of course we need only use that for deleting...:
444 $removeClauses[] = 'uid='.intval($oldMMs_inclUid[$oldMM_key][2]);
445 $elDelete = $oldMMs_inclUid[$oldMM_key];
447 if(is_array($mmItem)) {
448 $removeClauses[] = 'tablenames=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($mmItem[0], $MM_tableName) . ' AND ' . $uidForeign_field . '=' . $mmItem[1];
450 $removeClauses[] = $uidForeign_field.'='.$mmItem;
453 if ($this->MM_is_foreign
) {
454 if(is_array($mmItem)) {
455 $updateRefIndex_records[] = array($mmItem[0],$mmItem[1]);
457 $updateRefIndex_records[] = array($this->firstTable
,$mmItem);
461 $deleteAddWhere = ' AND ('.implode(' OR ', $removeClauses).')';
462 $GLOBALS['TYPO3_DB']->exec_DELETEquery($MM_tableName, $uidLocal_field.'='.intval($uid).$deleteAddWhere.$additionalWhere_tablenames.$additionalWhere);
465 foreach($updateRefIndex_records as $pair) {
466 $this->updateRefIndex($pair[0],$pair[1]);
470 // Update ref index; In tcemain it is not certain that this will happen because if only the MM field is changed the record itself is not updated and so the ref-index is not either. This could also have been fixed in updateDB in tcemain, however I decided to do it here ...
471 $this->updateRefIndex($this->currentTable
,$uid);
476 * Remaps MM table elements from one local uid to another
477 * Does NOT update the reference index for you, must be called subsequently to do that!
479 * @param string MM table name
480 * @param integer Local, current UID
481 * @param integer Local, new UID
482 * @param boolean If set, then table names will always be written.
485 function remapMM($MM_tableName,$uid,$newUid,$prependTableName=0) {
487 if ($this->MM_is_foreign
) { // in case of a reverse relation
488 $uidLocal_field = 'uid_foreign';
490 $uidLocal_field = 'uid_local';
493 // If there are tables...
494 $tableC = count($this->tableArray
);
496 $prep = ($tableC>1||
$prependTableName||
$this->MM_isMultiTableRelationship
) ?
1 : 0; // boolean: does the field "tablename" need to be filled?
499 $additionalWhere_tablenames = '';
500 if ($this->MM_is_foreign
&& $prep) {
501 $additionalWhere_tablenames = ' AND tablenames=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($this->currentTable
, $MM_tableName);
504 $additionalWhere = '';
505 // add WHERE clause if configured
506 if ($this->MM_table_where
) {
507 $additionalWhere.= LF
.str_replace('###THIS_UID###', intval($uid), $this->MM_table_where
);
509 // Select, update or delete only those relations that match the configured fields
510 foreach ($this->MM_match_fields
as $field => $value) {
511 $additionalWhere.= ' AND '.$field.'='.$GLOBALS['TYPO3_DB']->fullQuoteStr($value, $MM_tableName);
514 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($MM_tableName, $uidLocal_field.'='.intval($uid).$additionalWhere_tablenames.$additionalWhere, array($uidLocal_field => $newUid));
519 * Reads items from a foreign_table, that has a foreign_field (uid of the parent record) and
520 * stores the parts in the internal array itemArray and tableArray.
522 * @param integer $uid: The uid of the parent record (this value is also on the foreign_table in the foreign_field)
523 * @param array $conf: TCA configuration for current field
526 function readForeignField($uid, $conf) {
530 $foreign_table = $conf['foreign_table'];
531 $foreign_table_field = $conf['foreign_table_field'];
532 $useDeleteClause = $this->undeleteRecord ? false
: true
;
534 // search for $uid in foreign_field, and if we have symmetric relations, do this also on symmetric_field
535 if ($conf['symmetric_field']) {
536 $whereClause = '('.$conf['foreign_field'].'='.$uid.' OR '.$conf['symmetric_field'].'='.$uid.')';
538 $whereClause = $conf['foreign_field'].'='.$uid;
540 // use the deleteClause (e.g. "deleted=0") on this table
541 if ($useDeleteClause) {
542 $whereClause .= t3lib_BEfunc
::deleteClause($foreign_table);
544 // if it's requested to look for the parent uid AND the parent table,
545 // add an additional SQL-WHERE clause
546 if ($foreign_table_field && $this->currentTable
) {
547 $whereClause .= ' AND '.$foreign_table_field.'='.$GLOBALS['TYPO3_DB']->fullQuoteStr($this->currentTable
, $foreign_table);
550 // get the correct sorting field
551 if ($conf['foreign_sortby']) { // specific manual sortby for data handled by this field
552 if ($conf['symmetric_sortby'] && $conf['symmetric_field']) {
553 // sorting depends on, from which side of the relation we're looking at it
556 WHEN '.$conf['foreign_field'].'='.$uid.'
557 THEN '.$conf['foreign_sortby'].'
558 ELSE '.$conf['symmetric_sortby'].'
561 // regular single-side behaviour
562 $sortby = $conf['foreign_sortby'];
564 } elseif ($conf['foreign_default_sortby']) { // specific default sortby for data handled by this field
565 $sortby = $conf['foreign_default_sortby'];
566 } elseif ($GLOBALS['TCA'][$foreign_table]['ctrl']['sortby']) { // manual sortby for all table records
567 $sortby = $GLOBALS['TCA'][$foreign_table]['ctrl']['sortby'];
568 } elseif ($GLOBALS['TCA'][$foreign_table]['ctrl']['default_sortby']) { // default sortby for all table records
569 $sortby = $GLOBALS['TCA'][$foreign_table]['ctrl']['default_sortby'];
572 // strip a possible "ORDER BY" in front of the $sortby value
573 $sortby = $GLOBALS['TYPO3_DB']->stripOrderBy($sortby);
574 // get the rows from storage
575 $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid', $foreign_table, $whereClause, '', $sortby);
578 foreach ($rows as $row) {
579 $this->itemArray
[$key]['id'] = $row['uid'];
580 $this->itemArray
[$key]['table'] = $foreign_table;
581 $this->tableArray
[$foreign_table][]= $row['uid'];
588 * Write the sorting values to a foreign_table, that has a foreign_field (uid of the parent record)
590 * @param array $conf: TCA configuration for current field
591 * @param integer $parentUid: The uid of the parent record
592 * @param boolean $updateToUid: Whether to update the foreign field with the $parentUid (on Copy)
593 * @param boolean $skipSorting: Do not update the sorting columns, this could happen for imported values
596 function writeForeignField($conf, $parentUid, $updateToUid=0, $skipSorting=false
) {
598 $foreign_table = $conf['foreign_table'];
599 $foreign_field = $conf['foreign_field'];
600 $symmetric_field = $conf['symmetric_field'];
601 $foreign_table_field = $conf['foreign_table_field'];
603 // if there are table items and we have a proper $parentUid
604 if (t3lib_div
::testInt($parentUid) && count($this->tableArray
)) {
605 // if updateToUid is not a positive integer, set it to '0', so it will be ignored
606 if (!(t3lib_div
::testInt($updateToUid) && $updateToUid > 0)) {
609 $fields = 'uid,'.$foreign_field.($symmetric_field ?
','.$symmetric_field : '');
612 foreach ($this->itemArray
as $val) {
614 $table = $val['table'];
616 // fetch the current (not overwritten) relation record if we should handle symmetric relations
617 if ($conf['symmetric_field']) {
618 $row = t3lib_BEfunc
::getRecord($table,$uid,$fields,'',false
);
619 $isOnSymmetricSide = t3lib_loadDBGroup
::isOnSymmetricSide($parentUid, $conf, $row);
622 $updateValues = array();
624 // no update to the uid is requested, so this is the normal behaviour
625 // just update the fields and care about sorting
627 // Always add the pointer to the parent uid
628 if ($isOnSymmetricSide) {
629 $updateValues[$symmetric_field] = $parentUid;
631 $updateValues[$foreign_field] = $parentUid;
634 // if it is configured in TCA also to store the parent table in the child record, just do it
635 if ($foreign_table_field && $this->currentTable
) {
636 $updateValues[$foreign_table_field] = $this->currentTable
;
639 // update sorting columns if not to be skipped
641 // get the correct sorting field
642 if ($conf['foreign_sortby']) { // specific manual sortby for data handled by this field
643 $sortby = $conf['foreign_sortby'];
644 } elseif ($GLOBALS['TCA'][$foreign_table]['ctrl']['sortby']) { // manual sortby for all table records
645 $sortby = $GLOBALS['TCA'][$foreign_table]['ctrl']['sortby'];
647 // strip a possible "ORDER BY" in front of the $sortby value
648 $sortby = $GLOBALS['TYPO3_DB']->stripOrderBy($sortby);
649 $symSortby = $conf['symmetric_sortby'];
651 // set the sorting on the right side, it depends on who created the relation, so what uid is in the symmetric_field
652 if ($isOnSymmetricSide && $symSortby) {
653 $updateValues[$symSortby] = ++
$c;
655 $updateValues[$sortby] = ++
$c;
659 // update to a foreign_field/symmetric_field pointer is requested, normally used on record copies
660 // only update the fields, if the old uid is found somewhere - for select fields, TCEmain is doing this already!
662 if ($isOnSymmetricSide) {
663 $updateValues[$symmetric_field] = $updateToUid;
665 $updateValues[$foreign_field] = $updateToUid;
669 if (count($updateValues)) {
670 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid=' . intval($uid), $updateValues);
671 $this->updateRefIndex($table, $uid);
678 * After initialization you can extract an array of the elements from the object. Use this function for that.
680 * @param boolean If set, then table names will ALWAYS be prepended (unless its a _NO_TABLE value)
681 * @return array A numeric array.
683 function getValueArray($prependTableName='') {
686 $tableC = count($this->tableArray
);
688 // If there are tables in the table array:
690 // If there are more than ONE table in the table array, then always prepend table names:
691 $prep = ($tableC>1||
$prependTableName) ?
1 : 0;
693 // Traverse the array of items:
694 foreach($this->itemArray
as $val) {
695 $valueArray[]=(($prep && $val['table']!='_NO_TABLE') ?
$val['table'].'_' : '').
704 * Converts id numbers from negative to positive.
706 * @param array Array of [table]_[id] pairs.
707 * @param string Foreign table (the one used for positive numbers)
708 * @param string NEGative foreign table
709 * @return array The array with ID integer values, converted to positive for those where the table name was set but did NOT match the positive foreign table.
711 function convertPosNeg($valueArray,$fTable,$nfTable) {
712 if (is_array($valueArray) && $fTable) {
713 foreach($valueArray as $key => $val) {
715 $parts = explode('_',$val,2);
716 $theID = strrev($parts[0]);
717 $theTable = strrev($parts[1]);
719 if ( t3lib_div
::testInt($theID) && (!$theTable ||
!strcmp($theTable,$fTable) ||
!strcmp($theTable,$nfTable)) ) {
720 $valueArray[$key]= $theTable && strcmp($theTable,$fTable) ?
$theID*-1 : $theID;
728 * Reads all records from internal tableArray into the internal ->results array where keys are table names and for each table, records are stored with uids as their keys.
729 * If $this->fromTC is set you can save a little memory since only uid,pid and a few other fields are selected.
733 function getFromDB() {
734 // Traverses the tables listed:
735 foreach($this->tableArray
as $key => $val) {
736 if (is_array($val)) {
737 $itemList = implode(',',$val);
742 if ($GLOBALS['TCA'][$key]['ctrl']['label']) {
743 $from.= ','.$GLOBALS['TCA'][$key]['ctrl']['label']; // Titel
745 if ($GLOBALS['TCA'][$key]['ctrl']['label_alt']) {
746 $from.= ','.$GLOBALS['TCA'][$key]['ctrl']['label_alt']; // Alternative Title-Fields
748 if ($GLOBALS['TCA'][$key]['ctrl']['thumbnail']) {
749 $from.= ','.$GLOBALS['TCA'][$key]['ctrl']['thumbnail']; // Thumbnail
752 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery($from, $key, 'uid IN ('.$itemList.')'.$this->additionalWhere
[$key]);
753 while($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
754 $this->results
[$key][$row['uid']]=$row;
759 return $this->results
;
763 * Prepare items from itemArray to be transferred to the TCEforms interface (as a comma list)
766 * @see t3lib_transferdata::renderRecord()
768 function readyForInterface() {
771 if (!is_array($this->itemArray
)) {return false
;}
774 $perms_clause = $GLOBALS['BE_USER']->getPagePermsClause(1); // For use when getting the paths....
775 $titleLen=intval($GLOBALS['BE_USER']->uc
['titleLen']);
777 foreach($this->itemArray
as $key => $val) {
778 $theRow = $this->results
[$val['table']][$val['id']];
779 if ($theRow && is_array($TCA[$val['table']])) {
780 $label = t3lib_div
::fixed_lgd_cs(strip_tags(t3lib_BEfunc
::getRecordTitle($val['table'], $theRow)),$titleLen);
781 $label = ($label)?
$label:'[...]';
782 $output[]=str_replace(',','',$val['table'].'_'.$val['id'].'|'.rawurlencode($label));
785 return implode(',',$output);
789 * Counts the items in $this->itemArray and puts this value in an array by default.
791 * @param boolean Whether to put the count value in an array
792 * @return mixed The plain count as integer or the same inside an array
794 function countItems($returnAsArray = true
) {
795 $count = count($this->itemArray
);
796 if ($returnAsArray) $count = array($count);
801 * Update Reference Index (sys_refindex) for a record
802 * Should be called any almost any update to a record which could affect references inside the record.
803 * (copied from TCEmain)
805 * @param string Table name
806 * @param integer Record UID
809 function updateRefIndex($table,$id) {
810 $refIndexObj = t3lib_div
::makeInstance('t3lib_refindex');
811 $result = $refIndexObj->updateRefIndexTable($table,$id);
815 * Checks, if we're looking from the "other" side, the symmetric side, to a symmetric relation.
817 * @param string $parentUid: The uid of the parent record
818 * @param array $parentConf: The TCA configuration of the parent field embedding the child records
819 * @param array $childRec: The record row of the child record
820 * @return boolean Returns true if looking from the symmetric ("other") side to the relation.
822 function isOnSymmetricSide($parentUid, $parentConf, $childRec) {
823 return t3lib_div
::testInt($childRec['uid']) && $parentConf['symmetric_field'] && $parentUid == $childRec[$parentConf['symmetric_field']]
830 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE
]['XCLASS']['t3lib/class.t3lib_loaddbgroup.php']) {
831 include_once($TYPO3_CONF_VARS[TYPO3_MODE
]['XCLASS']['t3lib/class.t3lib_loaddbgroup.php']);