Location: PHPKode > projects > Porte > porte-0.2.2/src/plugins/associations/ManyToManyJoin.php
<?php

/**
 * Copyright (c) 2008, SARL Adaltas. All rights reserved.
 * Code licensed under the BSD License:
 * http://porte.adaltas.com/en/developer/license.html
 */

/**
 * PorteOneToManyBelongsTo
 * 
 * @package    Porte
 * @subpackage plugin
 * @author     David Worms info(at)adaltas.com
 * @copyright  2008 Adaltas
 */
class PorteManyToManyJoin{
	
	public static function setRecord($property,$record,$assocRecords){
		return self::setRecords($property,$record,$assocRecords);
	}
	
	public static function setRecords($property,$record,$assocRecords){
		$config = $record->porte->models->{$record->type}['properties'][$property];
		$assocRecords = new PorteIterator($record->porte->tables->{$config['has_many']['type']},$assocRecords);
		
		if(!isset($record->associations[$property])){
			$record->associations[$property] = new PorteIterator(
				$record->table,
				// Preload PorteIterator with primary_keys
				(isset($record->attributes[$property]))?explode(',',$record->attributes[$property]):array()
			);
		}else{
			// Keep the same instance of PorteIterator but remove its content
			foreach($record->associations[$property]->array as $k=>$v){
				unset($record->associations[$property]->array[$k]);
			}
		}
		//$record->associations[$property]->mode = 'set';
		$record->associations[$property]->remove_existing_associations = true;
		if(empty($assocRecords)) return $record;
		foreach($assocRecords as $assocRecord){
			$record->associations[$property]->array[] = $assocRecord;
		}
		return $record;
	}
	
	/**
	 * many-to-many with a join table
	 * 
	 * @return PorteIterator Iterator containing the associated records
	 * @param $property String Name of the property being accessed
	 * @param $record PorteRecord
	 * @param $assocRecord PorteIterator Record to be associated with the current record
	 */
	public static function addRecord($property,$record,$assocRecord){
		return PorteManyToManyListPersisted::addRecord($property,$record,$assocRecord);
	}
	
	/**
	 * many-to-many with a join table
	 * 
	 * @return PorteIterator Iterator containing the associated records
	 * @param $property String Name of the property being accessed
	 * @param $record PorteRecord
	 * @param $assocRecords PorteIterator Records to be associated with the current record
	 */
	public static function addRecords($property,$record,$assocRecords){
		// @todo This descently can't work, it call the load(force) method based on the
		// existence of attributes[property] and it at some other places
		return PorteManyToManyListPersisted::addRecords($property,$record,$assocRecords);
	}
	
	/**
	 * many-to-many with a join table
	 * 
	 * @return PorteIterator Iterator containing the associated records
	 * @param $property String Name of the property being accessed
	 * @param $record PorteRecord
	 * @param $options array[optional] Optional array
	 */
	public static function getRecords($property,$record,$options=array()){
		$config = $record->porte->models->{$record->type}['properties'][$property];
		$assocModel = $record->porte->models->{$config['has_many']['type']};
		$assocConfig = $assocModel['properties'][$config['has_many']['property']];
		// Load record from db if not already done
		//if(!array_key_exists($property,$record->associations)&&!$record->isNew()){
			//$record->load($record->getIdentifier(),array('force'=>true));
		//}
		// todo: move this in else condition
		$options = array_merge(array(
			'select'=>'`'.$assocModel['table'].'`.*',
			'where'=>'1 ',
		),$options);
		if(isset($record->associations[$property])){
			$assocRecords = $record->associations[$property];
			foreach($assocRecords as $key=>$assocRecord){
				if(is_int($assocRecord)){
					$record->associations[$property]->array[$key] = 
						$record->porte->tables->{$config['has_many']['type']}->load($assocRecord);
				}
			}
			return $assocRecords;
		}else if($record->isNew()){
			return $record->associations[$property] = new PorteIterator($record->table,array());
		}else{
			if(is_array($config['has_many']['join']['field'])){
				$options['from'] = ' `'.$assocModel['database'].'`.`'.$assocModel['table'].'`, `'.$config['has_many']['join']['database'].'`.`'.$config['has_many']['join']['table'].'` ';
				$options['where'] .= ' AND (`'.$config['has_many']['join']['database'].'`.`'.$config['has_many']['join']['table'].'`.`'.$config['has_many']['join']['field'][0].'` = `'.$assocModel['database'].'`.`'.$assocModel['table'].'`.`'.$assocModel['primary_key'].'` AND `'.$config['has_many']['join']['database'].'`.`'.$config['has_many']['join']['table'].'`.`'.$config['has_many']['join']['field'][1].'` = \''.$record->getIdentifier().'\') OR (`'.$config['has_many']['join']['database'].'`.`'.$config['has_many']['join']['table'].'`.`'.$config['has_many']['join']['field'][1].'` = `'.$assocModel['database'].'`.`'.$assocModel['table'].'`.`'.$assocModel['primary_key'].'` AND `'.$config['has_many']['join']['database'].'`.`'.$config['has_many']['join']['table'].'`.`'.$config['has_many']['join']['field'][0].'` = \''.$record->getIdentifier().'\')';
			}else{
				$options['from'] = ' `'.$assocModel['database'].'`.`'.$assocModel['table'].'` INNER JOIN `'.$config['has_many']['join']['database'].'`.`'.$config['has_many']['join']['table'].'` ON `'.$assocConfig['has_many']['join']['database'].'`.`'.$assocConfig['has_many']['join']['table'].'`.`'.$assocConfig['has_many']['join']['field'].'` = `'.$assocModel['database'].'`.`'.$assocModel['table'].'`.`'.$assocModel['primary_key'].'`';
				$options['where'] .= ' AND `'.$config['has_many']['join']['database'].'`.`'.$config['has_many']['join']['table'].'`.`'.$config['has_many']['join']['field'].'` = \''.$record->getIdentifier().'\' ';
			}
			return $record->associations[$property] = $record->porte->tables->get($config['has_many']['type'])->find($options);
		}
	}
	
	public static function deleteRecord($property,$record,$id){
		self::deleteRecords($property,$record,$id);
	}
	
	public static function deleteRecords($property,$record,$assocRecords=array()){
		$model = $record->porte->models->{$record->type};
		$config = $model['properties'][$property];
		/*
		if(is_string($assocRecords)){
			$assocRecords = explode(',',$assocRecords);
		}else if(is_int($assocRecords)){
			$assocRecords = array(strval($assocRecords));
		}else if(is_array($assocRecords)){
			foreach($assocRecords as $k=>$assocRecord){
				if(is_object($assocRecord)){
					$assocRecords[$k] = strval($assocRecord->getIdentifier());
				}else if(is_int($assocRecord)||ctype_digit($assocRecord)){
					$assocRecords[$k] = strval($assocRecord);
				}
			}
		}else if($assocRecords instanceof PorteRecord){
			$assocRecords = array($assocRecords->getIdentifier());
		}
		*/
		$assocRecords = new PorteIterator($record->porte->tables->{$config['has_many']['type']},$assocRecords);
		$assocRecords = $assocRecords->getIdentifier();
		$primaryKey = $model['primary_key'];
		$primaryKeyValue = $record->getIdentifier();
		$joinDb = $config['has_many']['join']['database'];
		$joinTable = $config['has_many']['join']['table'];
		$joinField = $config['has_many']['join']['field'];
		$assocConfig = $record->porte->models->{$config['has_many']['type']}['properties'][$config['has_many']['property']];
		$joinRelatedField = $assocConfig['has_many']['join']['field'];
		$query = 'DELETE `'.$joinDb.'`.`'.$joinTable.'`.* FROM `'.$joinDb.'`.`'.$joinTable.'` INNER JOIN `'.$model['database'].'`.`'.$model['table'].'` ON `'.$model['database'].'`.`'.$model['table'].'`.`'.$primaryKey.'` = `'.$joinDb.'`.`'.$joinTable.'`.`'.$joinField.'` WHERE `'.$model['database'].'`.`'.$model['table'].'`.`'.$primaryKey.'` = \''.$primaryKeyValue.'\'';
		if(!empty($assocRecords)){
			$query .= 'AND FIND_IN_SET(`'.$joinDb.'`.`'.$joinTable.'`.`'.$joinRelatedField.'`,\''.implode(',',$assocRecords).'\')';
		}
		$query .= ';';
		$record->porte->exec($query);
		if(empty($assocRecords)){
			$record->associations[$property] = new PorteIterator($record->table);
		}else{
			$hum = $record->associations[$property]->array;
			foreach($hum as $key=>$association){
				if(in_array($association->getIdentifier(),$assocRecords)){
					unset($hum[$key]);
				}
			}
			// Re-index the array
			$record->associations[$property]->array = array_values($hum);
			$record->associations[$property]->rewind();
		}
		return $assocRecords;
		//throw new PorteException('Error while deleting associations: '.$query);
	}
	
	public static function countRecords($property,$record,$options=array()){
		$config = $record->porte->models->{$record->type}['properties'][$property];
		$assocModel = $record->porte->models->{$config['has_many']['type']};
		$options = array_merge(array(
			'select'=>'count(*)',
			'from'=>'`'.$config['has_many']['join']['database'].'`.`'.$config['has_many']['join']['table'].'`',
			'where'=>'1',
		),$options);
		$options['where'] .= ' AND `'.$config['has_many']['join']['database'].'`.`'.$config['has_many']['join']['table'].'`.`'.$config['has_many']['join']['field'].'` = \''.$record->getIdentifier().'\'';
		return intval($record->porte->query($record->table->find(array_merge($options,array('return_sql'=>true,'limit'=>null))))->fetchColumn());
	}
	
	public static function save($property,$record,&$circular){
		// @todo this is very slow since we loop through each record an make 2 queries for each
		// (one select count and on insert).
		//http://www.xaprb.com/blog/2005/09/25/insert-if-not-exists-queries-in-mysql/
		//http://forums.mysql.com/read.php?97,164551,164551
		$config = $record->porte->models->{$record->type}['properties'][$property];
		$assocConfig = $record->porte->models->{$config['has_many']['type']}['properties'][$config['has_many']['property']];
		$assocRecords = $record->associations[$property];
		$pks = array();
		foreach($assocRecords as $assocRecord){
			if($assocRecord instanceof PorteRecord && !$assocRecord->isNew()){
				$pks[] = $assocRecord->save($circular)->getIdentifier();
			}else if($assocRecord instanceof PorteRecord){
				$pks[] = $assocRecord->save($circular)->getIdentifier();
			}else if(is_int($assocRecord)||(is_string($assocRecord)&&(int)$assocRecord!=0)){
				$pks[] = $assocRecord;
			}
		}
		$assocRecords->rewind();
		if(!empty($assocRecords->remove_existing_associations)){
			$assocRecords->remove_existing_associations = false;
			if(is_array($config['has_many']['join']['field'])){
				$query = 'DELETE FROM `'.$config['has_many']['join']['database'].'`.`'.$config['has_many']['join']['table'].'` WHERE (`'.$config['has_many']['join']['field'][0].'`=\''.$record->getIdentifier().'\') OR (`'.$config['has_many']['join']['field'][1].'`=\''.$record->getIdentifier().'\') ';
			}else{
				$query = 'DELETE FROM `'.$config['has_many']['join']['database'].'`.`'.$config['has_many']['join']['table'].'` WHERE `'.$config['has_many']['join']['field'].'`=\''.$record->getIdentifier().'\'; ';
			}
			$record->porte->exec($query);
		}
		if(!empty($pks)){
			if(is_array($config['has_many']['join']['field'])){
				$query = 'SELECT `'.$config['has_many']['join']['field'][0].'`,`'.$config['has_many']['join']['field'][0].'` FROM `'.$config['has_many']['join']['database'].'`.`'.$config['has_many']['join']['table'].'` as `join_table` WHERE (`join_table`.`'.$config['has_many']['join']['field'][0].'`=\''.$record->getIdentifier().'\' AND FIND_IN_SET(`join_table`.`'.$config['has_many']['join']['field'][1].'`,\''.implode(',',$pks).'\')) OR (FIND_IN_SET(`join_table`.`'.$config['has_many']['join']['field'][0].'`,\''.implode(',',$pks).'\') AND `join_table`.`'.$config['has_many']['join']['field'][1].'`=\''.$record->getIdentifier().'\') ';
			}else{
				$query = 'SELECT `'.$assocConfig['has_many']['join']['field'].'` FROM `'.$config['has_many']['join']['database'].'`.`'.$config['has_many']['join']['table'].'` as `join_table` WHERE `join_table`.`'.$config['has_many']['join']['field'].'`=\''.$record->getIdentifier().'\' AND FIND_IN_SET(`join_table`.`'.$assocConfig['has_many']['join']['field'].'`,\''.implode(',',$pks).'\'); ';
			}
			$existingPks = array();
			$statement = $record->porte->query($query);
			while($row = $statement->fetch(Porte::FETCH_NUM)){
				$existingPks[] = $row[0];
				if(is_array($config['has_many']['join']['field'])){
					$existingPks[] = $row[1];
				}
			}
			$statement->closeCursor();
			if(is_array($config['has_many']['join']['field'])){
				$pks = array_unique($pks);
			}
			$pks = array_diff($pks,$existingPks);
			if(!empty($pks)){
				foreach($pks as $pk){
					if(is_array($config['has_many']['join']['field'])){
						$query = 'INSERT INTO `'.$config['has_many']['join']['database'].'`.`'.$config['has_many']['join']['table'].'` (`'.$config['has_many']['join']['field'][0].'`,`'.$config['has_many']['join']['field'][1].'`) VALUES (\''.$record->getIdentifier().'\',\''.$pk.'\');';
					}else{
						$query = 'INSERT INTO `'.$config['has_many']['join']['database'].'`.`'.$config['has_many']['join']['table'].'` (`'.$config['has_many']['join']['database'].'`.`'.$config['has_many']['join']['table'].'`.`'.$config['has_many']['join']['field'].'`,`'.$assocConfig['has_many']['join']['database'].'`.`'.$assocConfig['has_many']['join']['table'].'`.`'.$assocConfig['has_many']['join']['field'].'`) VALUES (\''.$record->getIdentifier().'\',\''.$pk.'\');';
					}
					$record->porte->exec($query);
					//throw new PorteException('Error saving on join table: '.$query);
				}
			}
		}
	}
	
}

?>
Return current item: Porte