菜单

双向MM表对应同一个表的同一个字段.

2010年09月1日 - 我的心情

config.添加配置:’MM_own_field’ => true,

添加t3lib_loaddbgroup的XCLASS:

 * @package TYPO3
 * @subpackage t3lib
 */
class ux_t3lib_loadDBGroup extends t3lib_loadDBGroup{
		// External, static:
	var $fromTC = 1;					// Means that only uid and the label-field is returned
	var $registerNonTableValues=0;		// If set, values that are not ids in tables are normally discarded. By this options they will be preserved.

		// Internal, dynamic:
	var $tableArray=Array();			// Contains the table names as keys. The values are the id-values for each table. Should ONLY contain proper table names.
	var $itemArray=Array();				// Contains items in an numeric array (table/id for each). Tablenames here might be "_NO_TABLE"
	var $nonTableArray=array();			// Array for NON-table elements
	var $additionalWhere=array();
	var $checkIfDeleted = 1;			// deleted-column is added to additionalWhere... if this is set...
	var $dbPaths=Array();
	var $firstTable = '';				// Will contain the first table name in the $tablelist (for positive ids)
	var $secondTable = '';				// Will contain the second table name in the $tablelist (for negative ids)
		// private
	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"
	var $MM_oppositeField = '';	// field name at the "local" side of the MM relation
	var $MM_oppositeTable = ''; // only set if MM_is_foreign is set
	var $MM_oppositeFieldConf = ''; // only set if MM_is_foreign is set
	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)
	var $currentTable;	// current table => Only needed for reverse relations
	var $undeleteRecord;		// if a record should be undeleted (so do not use the $useDeleteClause on t3lib_BEfunc)
	var $MM_ownField = false;	//add by bobo

	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
	var $MM_insert_fields = array();	// array of fields and value pairs used for insert in MM table
	var $MM_table_where = ''; // extra MM table where

	/**
	 * Initialization of the class.
	 *
	 * @param	string		List of group/select items
	 * @param	string		Comma list of tables, first table takes priority if no table is set for an entry in the list.
	 * @param	string		Name of a MM table.
	 * @param	integer		Local UID for MM lookup
	 * @param	string		current table name
	 * @param	integer		TCA configuration for current field
	 * @return	void
	 */
	function start($itemlist, $tablelist, $MMtable='', $MMuid=0, $currentTable='', $conf=array())	{
			// SECTION: MM reverse relations
		$this->MM_is_foreign = ($conf['MM_opposite_field']?1:0);
		$this->MM_oppositeField = $conf['MM_opposite_field'];
		$this->MM_table_where = $conf['MM_table_where'];
		$this->MM_hasUidField = $conf['MM_hasUidField'];
		$this->MM_match_fields = is_array($conf['MM_match_fields']) ? $conf['MM_match_fields'] : array();
		$this->MM_insert_fields = is_array($conf['MM_insert_fields']) ? $conf['MM_insert_fields'] : $this->MM_match_fields;
		$this->MM_ownField = $conf['MM_own_field'];		//add by bobo

		$this->currentTable = $currentTable;
		if ($this->MM_is_foreign)	{
			$tmp = ($conf['type']==='group'?$conf['allowed']:$conf['foreign_table']);
				// 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']
			$tmp = t3lib_div::trimExplode(',', $tmp);
			$this->MM_oppositeTable = $tmp[0];
			unset($tmp);

				// only add the current table name if there is more than one allowed field
			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.
			$this->MM_oppositeFieldConf = $GLOBALS['TCA'][$this->MM_oppositeTable]['columns'][$this->MM_oppositeField]['config'];

			if ($this->MM_oppositeFieldConf['allowed'])	{
				$oppositeFieldConf_allowed = explode(',', $this->MM_oppositeFieldConf['allowed']);
				if (count($oppositeFieldConf_allowed) > 1)	{
					$this->MM_isMultiTableRelationship = $oppositeFieldConf_allowed[0];
				}
			}
		}

			// SECTION:	normal MM relations

			// If the table list is "*" then all tables are used in the list:
		if (!strcmp(trim($tablelist),'*'))	{
			$tablelist = implode(',',array_keys($GLOBALS['TCA']));
		}

			// The tables are traversed and internal arrays are initialized:
		$tempTableArray = t3lib_div::trimExplode(',',$tablelist,1);
		foreach($tempTableArray as $key => $val)	{
			$tName = trim($val);
			$this->tableArray[$tName] = Array();
			if ($this->checkIfDeleted && $GLOBALS['TCA'][$tName]['ctrl']['delete'])	{
				$fieldN = $tName.'.'.$GLOBALS['TCA'][$tName]['ctrl']['delete'];
				$this->additionalWhere[$tName].=' AND '.$fieldN.'=0';
			}
		}

		if (is_array($this->tableArray))	{
			reset($this->tableArray);
		} else {return 'No tables!';}

			// Set first and second tables:
		$this->firstTable = key($this->tableArray);		// Is the first table
		next($this->tableArray);
		$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...

			// Now, populate the internal itemArray and tableArray arrays:
		if ($MMtable)	{	// If MM, then call this function to do that:
			if ($MMuid)	{
				$this->readMM($MMtable, $MMuid);
			} else	{ // Revert to readList() for new records in order to load possible default values from $itemlist
				$this->readList($itemlist);
			}
		} elseif ($MMuid && $conf['foreign_field']) {
				// If not MM but foreign_field, the read the records by the foreign_field
			$this->readForeignField($MMuid, $conf);
		} else {
				// If not MM, then explode the itemlist by "," and traverse the list:
			$this->readList($itemlist);
				// do automatic default_sortby, if any
			if ($conf['foreign_default_sortby']) {
				$this->sortList($conf['foreign_default_sortby']);
			}
		}
	}

	/**
	 * Writes the internal itemArray to MM table:
	 *
	 * @param	string		MM table name
	 * @param	integer		Local UID
	 * @param	boolean		If set, then table names will always be written.
	 * @return	void
	 */
	function writeMM($MM_tableName,$uid,$prependTableName=0)	{

		if ($this->MM_is_foreign)	{	// in case of a reverse relation
			$uidLocal_field = 'uid_foreign';
			$uidForeign_field = 'uid_local';
			$sorting_field = 'sorting_foreign';
		} else {	// default
			$uidLocal_field = 'uid_local';
			$uidForeign_field = 'uid_foreign';
			$sorting_field = 'sorting';
		}

			// If there are tables...
		$tableC = count($this->tableArray);
		if ($tableC)	{
			$prep = ($tableC>1||$prependTableName||$this->MM_isMultiTableRelationship) ? 1 : 0;	// boolean: does the field "tablename" need to be filled?
			$c=0;

			$additionalWhere_tablenames = '';
			if ($this->MM_is_foreign && $prep)	{
				$additionalWhere_tablenames = ' AND tablenames="'.$this->currentTable.'"';
			}

			$additionalWhere = '';
				// add WHERE clause if configured
			if ($this->MM_table_where) {
				$additionalWhere.= "\n".str_replace('###THIS_UID###', intval($uid), $this->MM_table_where);
			}
				// Select, update or delete only those relations that match the configured fields
			foreach ($this->MM_match_fields as $field => $value) {
				$additionalWhere.= ' AND '.$field.'='.$GLOBALS['TYPO3_DB']->fullQuoteStr($value, $MM_tableName);
			}

			$res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
				$uidForeign_field.($prep?', tablenames':'').($this->MM_hasUidField?', uid':''),
				$MM_tableName,
				$uidLocal_field.'='.$uid.$additionalWhere_tablenames.$additionalWhere,
				'',
				$sorting_field
			);

			$oldMMs = array();
			$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
			while($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
				if (!$this->MM_is_foreign && $prep)	{
					$oldMMs[] = array($row['tablenames'], $row[$uidForeign_field]);
				} else {
					$oldMMs[] = $row[$uidForeign_field];
				}
				$oldMMs_inclUid[] = array($row['tablenames'], $row[$uidForeign_field], $row['uid']);
			}

				// For each item, insert it:
			foreach($this->itemArray as $val)	{
				$c++;

				if ($prep || $val['table']=='_NO_TABLE')	{
					if ($this->MM_is_foreign)	{	// insert current table if needed
						$tablename = $this->currentTable;
					} else {
						$tablename = $val['table'];
					}
				} else {
					$tablename = '';
				}

				if(!$this->MM_is_foreign && $prep) {
					$item = array($val['table'], $val['id']);
				} else {
					$item = $val['id'];
				}

				if (in_array($item, $oldMMs))	{
					$oldMMs_index = array_search($item, $oldMMs);

					$whereClause = $uidLocal_field.'='.$uid.' AND '.$uidForeign_field.'='.$val['id'].
									($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.
					if ($tablename) {
						$whereClause .= ' AND tablenames="'.$tablename.'"';
					}
					$GLOBALS['TYPO3_DB']->exec_UPDATEquery($MM_tableName, $whereClause.$additionalWhere, array($sorting_field => $c));

					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.
					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.
				} else {

					$insertFields = $this->MM_insert_fields;
					$insertFields[$uidLocal_field] = $uid;
					$insertFields[$uidForeign_field] = $val['id'];
					$insertFields[$sorting_field] = $c;
					if($tablename)	{
						$insertFields['tablenames'] = $tablename;
					}

					$GLOBALS['TYPO3_DB']->exec_INSERTquery($MM_tableName, $insertFields);

					//add by bobo
					if ($this->MM_ownField) {
						$c++;
						$insertFields[$uidLocal_field] = $val['id'];
						$insertFields[$uidForeign_field] = $uid;
						$insertFields[$sorting_field] = $c;
						$GLOBALS['TYPO3_DB']->exec_INSERTquery($MM_tableName, $insertFields);
					}

					if ($this->MM_is_foreign)	{
						$this->updateRefIndex($val['table'], $val['id']);
					}
				}
			}

				// Delete all not-used relations:
			if(is_array($oldMMs) && count($oldMMs) > 0) {
				$removeClauses = array();
				$updateRefIndex_records = array();
				foreach($oldMMs as $oldMM_key => $mmItem) {
					if ($this->MM_hasUidField)	{	// If UID field is present, of course we need only use that for deleting...:
						$removeClauses[] = 'uid='.intval($oldMMs_inclUid[$oldMM_key][2]);
						$elDelete = $oldMMs_inclUid[$oldMM_key];
					} else {
						if(is_array($mmItem)) {
							$removeClauses[] = 'tablenames="'.$mmItem[0].'" AND '.$uidForeign_field.'='.$mmItem[1];
						} else {
							$removeClauses[] = $uidForeign_field.'='.$mmItem;
						}
					}
					if ($this->MM_is_foreign)	{
						if(is_array($mmItem)) {
							$updateRefIndex_records[] = array($mmItem[0],$mmItem[1]);
						} else {
							$updateRefIndex_records[] = array($this->firstTable,$mmItem);
						}
					}
				}
				$deleteAddWhere = ' AND ('.implode(' OR ', $removeClauses).')';
				$GLOBALS['TYPO3_DB']->exec_DELETEquery($MM_tableName, $uidLocal_field.'='.intval($uid).$deleteAddWhere.$additionalWhere_tablenames.$additionalWhere);

				//add by bobo
				if ($this->MM_ownField) {
					$removeClauses = str_replace($uidForeign_field,$uidLocal_field,$removeClauses);
					$deleteAddWhere = '('.implode(' OR ', $removeClauses).')';
					$GLOBALS['TYPO3_DB']->exec_DELETEquery($MM_tableName, $deleteAddWhere.' and '.$uidForeign_field.'='.intval($uid).$additionalWhere_tablenames.$additionalWhere);
				}

					// Update ref index:
				foreach($updateRefIndex_records as $pair)	{
					$this->updateRefIndex($pair[0],$pair[1]);
				}
			}

				// 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 ...
			$this->updateRefIndex($this->currentTable,$uid);
		}
	}
}

?>

发表评论

电子邮件地址不会被公开。 必填项已用*标注