Location: PHPKode > projects > phpBurn > PhpBURN-phpburn-f2eec6a/app/libs/Mapping/Map.php
<?php
PhpBURN::load('Mapping.IMap');

/**
 * @package PhpBURN
 * @subpackage Mapping
 * 
 * @author Kléderson Bueno <hide@address.com>
 */
class PhpBURN_Map implements IMap {
	
	//Relationship types
	const ONE_TO_ONE 						= 100101;
	const ONE_TO_MANY 						= 100102;
	const MANY_TO_ONE 						= 100103;
	const MANY_TO_MANY 					= 100104;
	
	/**
	 * This attribute carries all mapping information such as relationships, witch field is witch column, etc.
	 * This is mapped only once and is cached for all the objects from the same type.
	 * It means all Teste() Objects only will load the xmlmapping once and not for each as in the others ORMs
	 * 
	 * @example 
	 * $mapping = array(
	 * 								'name' => array( 
	 * 									['field'] => array(type:string, column:string , dataType:string , notnull:bool = null, autoincrement:bool = null, defaultvalue = null),
	 * 									*['retroRelationship'] => array( fieldHere:string, fieldThere:string, class:string, type:const ),
	 * 									**['isRelationship'] => array( type:const, class:string, fieldThere:string, fieldHere:string, lazy:bool )
	 * 									***['isExternal'] => false;
	 * 									***['classReference'] => ( parentPackage, class, table, column )
	 * 
	 * 								) 
	 * 							)
	 * ['field'] defines all field information ( when it is a relationship it just set type = "relationship" and null for all rest )
	 * TODO ['retroRelationship'] Its when you config in the child Class a relationship information, for example Users agregates Albums you only must have to config a relationship in Parent Class. This is for other uses or informations.
	 * ['isRelationship'] defines the kind of relationship it is, if the field is not a relationship this = false else field is an array. This is important for lazy mode use and carries the array of objects from that child class
	 * ['isExternal'] Says to the Map Object that field will be used from another PhpBURN_Core Object ( parent object )
	 * @var array
	 */	
	public $fields = array();
	
	public $parentFieldReferences = array();
	
	/**
	 * Our reference to $modelObj
	 *
	 * @var unknown_type
	 */
	public $modelObj = null;
	
	public function __construct(PhpBURN_Core &$modelObj){
		$this->modelObj = $modelObj;

		//$this->mapThis($this->modelObj);
	}
	
	public function reset() {
		foreach($this->fields as $index => $value) {
			$this->setFieldValue($index,'');
		}

                return $this;
	}
	
	/**
	 * Starts a mapping for the model checking if has a XML mapping or if it uses
	 * a coded mapping.
	 *
	 * @param PhpBURN_Core $modelObj
	 */
	public function mapThis() {
		$xmlMap = $this->getXmlMap($this->modelObj);
		
		if($xmlMap == true) {
			$this->mapFromXML($xmlMap);
		} else {
			$this->mapFromCode();
		}

                return $this;
	}
	
	public function cloneAttributes() {
		return $this->fields;
	}
	
	public function setAttributes(array $fields, $incremental = true) {
		if($incremental == true) {
			foreach($fields as $index => $fieldData) {
				$this->fields[$index] = $fieldData;
			}
		} else {
			$this->fields = $fields;
		}
	}
	
	public function cloneReferences() {
		return $this->parentFieldsReferences;
	}
	
	public function setReferences($fieldsArray) {
		if(count($fieldsArray) > 0) {
			foreach($fieldsArray as $index => $value) {
				$this->parentFieldsReferences[$index] = $value;
			}
		}

                return $this;
	}
	
	/**
	 * This maps the model based on its XML Mapping
	 * @TODO Change it to DOMElement instead SimpleXML
	 * @param PhpBURN_Core $modelObj
	 * @param SimpleXMLElement $xmlMap
	 */
	public function mapFromXML(SimpleXMLElement $xmlMap) {
		//Setting up non-relationship fields
		foreach($xmlMap->class->attribute as $indexAttribute => $xmlAttribute) {
			switch($xmlAttribute->attributes()->type) {
				//Defines a column
				case 'column':
					$options = array();
					$options['notnull'] = (string)$xmlAttribute->attributes()->notnull;
					$options['autoincrement'] = (string)$xmlAttribute->attributes()->autoincrement;
					$options['primary'] = (string)$xmlAttribute->attributes()->primary;
					$options['defaultvalue'] = (string)$xmlAttribute->attributes()->defaultvalue;
					
					$this->addField((string)$xmlAttribute->attributes()->name,(string)$xmlAttribute->attributes()->column,(string)$xmlAttribute->attributes()->datatype,(string)$xmlAttribute->attributes()->lenght,$options);
				break;
				//Setup a relationship
				case 'relationship':
					
					/* Setup relationShip type */
					/* FIXME Create a way to call a Model constant */
					switch((string)$xmlAttribute->relationship[0]->attributes()->type) {
						case 'ONE_TO_ONE':
							$relType = self::ONE_TO_ONE;
						break;
						case 'ONE_TO_MANY':
							$relType = self::ONE_TO_MANY;
						break;
						case 'MANY_TO_MANY':
							$relType = self::ONE_TO_MANY;
						break;
					}
					$relType = !is_numeric($relType) ? 1 : $relType;
					/* /Setup relationShip type */
					
					//Setup relationShip informations
					$relName = (string)$xmlAttribute->attributes()->name;
					$foreignClass = (string)$xmlAttribute->relationship[0]->attributes()->class;
					$thisKey = (string)$xmlAttribute->relationship[0]->attributes()->thisKey;
					$relKey = (string)$xmlAttribute->relationship[0]->attributes()->relKey;
					$outKey = (string)$xmlAttribute->relationship[0]->attributes()->outKey;
					$relOutKey = (string)$xmlAttribute->relationship[0]->attributes()->relOutKey;
					$relTable = (string)$xmlAttribute->relationship[0]->attributes()->relTable;
					$lazy = (string)$xmlAttribute->relationship[0]->attributes()->lazy;
					
					//Creating the relationship
					$this->addRelationship($relName,$relType,$foreignClass,$thisKey,$relKey,$outKey,$relOutKey,$relTable,$lazy);
				break;
				/**
				 * Setup a child relationship
				 * 
				 * A child relationship is when you want your child class to "know" about its parent class relationship
				 */
				case 'child':
					
				break;
			}
		}
	}
	
	public function fillModel(array $data) {
		if ($data) {
//			Clean old data
			$this->reset();
			foreach ($data as $key => $value) {
				$this->setFieldValue($key,$value);
			}
		}
		
		return $data;
	}
	
	/**
	 * This maps the model based on its _objectMap() function
	 * @TODO Discutir a redundância aqui e analisar os métodos mágicos que podem vir a ser aplicados
	 */
	public function mapFromCode() {
		$this->modelObj->_mapping();
	}
	
	/**
	 * This method creates a relationship field into the mapping
	 * Relationship fields has different behavior than the column fields
	 * they comunicate with another PhpBURN models connecting them by some
	 * kind of reference.
	 * 
	 * A relationship can be:
	 * - ONE_TO_ONE
	 * - ONE_TO_MANY
	 * - MANY_TO_MAY
	 *
	 * @param String $relName
	 * @param int $relType
	 * @param String $foreignClass
	 * @param String $thisKey
	 * @param String $relKey
	 * @param String $outKey
	 * @param String $relOutKey
	 * @param String $relTable
	 * @param Boolean $lazy
	 */
	public function addRelationship($relName,$relType,$foreignClass, $thisKey, $relKey = null, $outKey = null, $relOutKey = null, $relTable = null, $lazy = false) {

		//Setup a simple field as a relationship field, just for double check
		$this->fields[$relName]['field']['type'] = false;
		$this->fields[$relName]['field']['column'] = false;
		$this->fields[$relName]['field']['type'] = false;
		$this->fields[$relName]['field']['length'] = false;
		
		//Setup the relationship
		$this->fields[$relName]['isRelationship'] = array();
			$this->fields[$relName]['isRelationship']['alias'] = $relName;
			$this->fields[$relName]['isRelationship']['type'] = $relType;
			$this->fields[$relName]['isRelationship']['foreignClass'] = $foreignClass;
			$this->fields[$relName]['isRelationship']['thisKey'] = $thisKey;
			$this->fields[$relName]['isRelationship']['relKey'] = $relKey;
			$this->fields[$relName]['isRelationship']['outKey'] = $outKey;
			$this->fields[$relName]['isRelationship']['relOutKey'] = $relOutKey;
			$this->fields[$relName]['isRelationship']['relTable'] = $relTable;
			$this->fields[$relName]['isRelationship']['lazy'] = $lazy;
			$this->fields[$relName]['isRelationship']['#value'] = $lazy;
		
		//For child relationships ONLY
		$this->fields[$relName]['isExternal'] = false;
		
		//For multipMap ONLY
		$this->fields[$relName]['classReference'] = !isset($this->fields[$relName]['classReference']) ? get_class($this->modelObj) : $this->fields[$relName]['classReference'];
		//$this->fields[$relName]['parentLinkField'] = 'false'; 
		//Setup defaultvalue for this field
		$this->setFieldValue($relName,null);

                return $this;
	}
	
	public function isRelationship($index) {
		if($this->fields[$index]['isRelationship'] == false) {
			return false;
		} else {
			return true;
		}
	}
	
	public function isField($index, $onlyTableFields = false) {
//		Check for relationship
		if($this->isRelationship($index)) {
			return false;
		} else if($this->isParent($index)) {
//			Check for parent fields that have to be in the current model table
			if(is_array($this->parentFieldsReferences))
				foreach($this->parentFieldsReferences as $refIndex => $refContent) {
					if($refContent == $index) {
						return true;
					}
				}
		} else if($this->fields[$index]['field']['name'] = $index) {
			return true;
		} else {
			return false;
		}
	} 
	
	public function isParent($index) {
		if($this->fields[$index]['classReference'] != get_class($this->getModel())) {
			return true;
		} else {
			return false;
		}
	}
	
	public function isParentKey($index) {
		if(is_array($this->parentFieldsReferences))
			foreach($this->parentFieldsReferences as $fieldIndex => $fieldName) {
				if($fieldName == $index) {
					return true;
				}
			}
		
		return false;
	}
	
	public function addParentRelationShip() {
		
	}

        /**
         * Create a reference to a parent field
         *
         * @param String $name
         * @return PhpBURN_Map
         */
	public function addParentField($name) {
		$parentClass = get_parent_class($this->modelObj);
		$relName = '__PhpBURN_Extended_'.$parentClass;
				
		$this->addRelationship($relName, PhpBURN_Map::ONE_TO_ONE, $parentClass, $name, $name, null, null, null);
		
		$parentVars = get_class_vars($parentClass);
		$parentTable = $parentVars['_tablename'];
		
		$this->parentFieldsReferences[$parentTable] = $name;
		
		//NOT WOKING YET
		$this->fields[$name]['classReference'] = get_class($this->getModel());
		$this->fields[$name]['parentLinkField'] = true;
		
		return $this;
	}
	
	public function getParentFields() {
		if(count($this->parentFieldsReferences) > 0 ) {
			foreach($this->parentFieldsReferences as $name) {
				$fields[] = $this->getField($name);
			}
		} else {
			$fields = array();
		}
		
		return $fields;
	}
	
	public function getTableParentField($tableName) {
		if(array_key_exists($tableName, $this->parentFieldsReferences) == true) {
			return $this->fields[$this->parentFieldsReferences[$tableName]];
		} else {
			return false;
		}
	}
	
	public function getClassParentField($className) {
		if (count($this->parentFieldsReferences) > 0)
		foreach($this->parentFieldsReferences as $index => $value) {
                    if($this->fields[$value]['classReference'] == $className) {
                        return $this->fields[$value];
                    }
		}
	}
	
	public function getTableChildClass($tableName) {
            foreach($this->fields as $index => $field) {
                if($field['field']['tableReference'] == $tableName) {
                    $classReference = $field['classReference'];

                    foreach($this->fields as $index => $value) {
                        if(is_subclass_of($value['classReference'], $classReference) == true && get_parent_class($value['classReference']) == $classReference) {
                            return $value['classReference'];
                        }
                    }
                }
            }
	}
	
	public function getTableParentClass($tableName) {
		foreach($this->fields as $index => $field) {
			if($field['field']['tableReference'] == $tableName) {
				return get_parent_class($field['classReference']);
			}
		}
	}
	
	public function getTrueFields($className) {
		return PhpBURN_Mapping::$mapping[$className];
	}
	
	/**
	 * This method creates a field into the model mapping info
	 * It can be called anytime anywhere by the Map Object
	 *
	 * @param String $name
	 * @param String $column
	 * @param String $type
	 * @param String $range
	 * @param Array $options
         *
         * @return PhpBURN_Map
	 */
	public function addField($name, $column, $type, $length, array $options) {
			$parentClass = get_parent_class($this->modelObj);
		
			//Check for duplicated columns in this map
			array_walk_recursive($this->fields,array($this, 'checkColumns'),$name);
			
			//Setup a simple field		
			$this->fields[$name]['field']['type'] = $type;
			$this->fields[$name]['field']['alias'] = $name;
			$this->fields[$name]['field']['column'] = $column;
			$this->fields[$name]['field']['type'] = $type;
			$this->fields[$name]['field']['length'] = $length;
			$this->fields[$name]['field']['options'] = count($options) > 0 ? $options : array();
			$this->fields[$name]['field']['tableReference'] = $this->modelObj->_tablename;
			
			//Just for double check it sets false to other kinds of field
			$this->fields[$name]['isRelationship'] = false;
			
			//When it belongs to a parent class
			$this->fields[$name]['isExternal'] = false;
			
			//For multipMap use ONLY
			$this->fields[$name]['classReference'] = get_class($this->modelObj);
			//$this->fields[$name]['parentLinkField'] = 'false';
			
			//Setup defaultvalue for this field
			$options['defaultvalue'] = $options['defaultvalue'] != null ? $options['defaultvalue'] : null;
			$this->setFieldValue($name,$options['defaultvalue']);

                        return $this;
	}
	
	public function getPrimaryKey($firstOnly = TRUE) {
		//Check for a PK field
		foreach($this->fields as $index => $content) {
			if($content['field']['options']['primary'] == true) {
        if($firstOnly == TRUE) {
          return $content;
        } else {
          $pks[$index] = $content;
        }
			}
		}
    
    if(count($pks) > 0) {
      return $pks;
    }
		
		$modelName = get_class($this->modelObj);
		PhpBURN_Message::output("<b>$modelName</b> [!has no Primary Key. How did you did it?!]", PhpBURN_Message::EXCEPTION);
	}
	
	public function getRelationShip($name, $returnData = false) {
		if(!is_array($this->fields[$name]['isRelationship']) || count($this->fields[$name]['isRelationship']) <= 0) {
			$modelName = get_class($this->getModel());
			return false;
		}
		
		if($returnData == true) {
			return $this->getRelationShipData($name);
		} else {
			return true;
		}
	}
	
	private function getRelationShipData($name) {
		return $this->fields[$name]['isRelationship'];
	}
	
	/**
	 * This function checks for duplicated columns in this map
	 *
	 * @param String $value
	 * @param String $index
	 * @param String $myCompare
	 */
	public function checkColumns($value,$index,$myCompare) {
		if($index == 'column' && $value == $myCompare) {
			PhpBURN_Message::output("[!Duplicated Column!]: $myCompare",PhpBURN_Message::EXCEPTION);
		}

                return $this;
	}
	
	/**
	 * This gets all mapinfo from a filed
	 *
	 * @param String $fieldName
	 */
	public function getField($fieldName){
		return $this->fields[$fieldName];
	}
	
	/**
	 * This gets all mapinfo from a relationship
	 *
	 * @param unknown_type $relationshipName
	 */
	public function getRelationshipInfo($relationshipName) {
		
	}
	
	/**
	 * This gets the value from the specified field
	 *
	 * @param String $field
	 */
	public function getFieldValue($field) {
		return $this->modelObj->$field = $this->fields[$field]['#value'];
	}
	
	public function getModel() {
		return $this->modelObj;
	}
  
  public function fetchFieldValue($field, $value) {
    $fields = new ArrayObject($this->fields);
		$test = $fields->offsetExists($field);
		if($test === false) {
			PhpBURN_Message::output("[!This field doesn't exist in the Mapping!]: <strong>". get_class($this->modelObj) ."->$field </strong>", PhpBURN_Message::WARNING);
		}
		
		return $this->fields[$field]['#fetch_value'] = $value;
  }
	
	/**
	 * This sets the value in a field based in name, value and Mapping Info
	 *
	 * @param String $field
	 * @param unknown_type $value
	 * @access public
	 */
	public function setFieldValue($field,$value) {
		$fields = new ArrayObject($this->fields);
		$test = $fields->offsetExists($field);
		if($test === false) {
			PhpBURN_Message::output("[!This field doesn't exist in the Mapping!]: <strong>". get_class($this->modelObj) ."->$field </strong>", PhpBURN_Message::WARNING);
		}
		
		$this->fields[$field]['#value'] = $this->getModel()->$field = $value;
		$this->getFieldValue($field);
		return $this;
	}
	
	/**
	 * Validate a field based in its rules in Dialect Type
	 * @param String $fieldName
	 * @return unknown_type
	 */
	public function validateField($fieldName) {
		$keyExist = array_key_exists($fieldName, $this->fields);
		
		if($keyExist == true && $this->fields[$fieldName]['isRelationship'] == false) {
			return $this->modelObj->_dialectObj->validateValue($this->fields[$fieldName]['#value'],$this->fields[$fieldName]['type'], $this->fields[$fieldName]['length']);
		} else {
			PhpBURN_Message::output("[!This field doesn't exist or is a Relationship!]: <strong>". get_class($this->modelObj) ."->$fieldName </strong>",PhpBURN_Message::WARNING);
			return false;
		}
		
		return false;
	}
	
	/**
	 * This method removes a field from model mapping info
	 *
	 * @param String $name
	 */
	public function removeField($fieldName) {
		
	}
	
	/**
	 * Maps the model based on its XML
	 *
	 * @param PhpBURN_Core $modelObj
	 * @return SimpleXMLElement $configXML
	 */
	private function getXmlMap() {
		
		$xmlMapping = PhpBURN::loadXMLMapping($this->modelObj->_package .'.'. get_class($this->modelObj) );
		
		if($xmlMapping == 'error') {
			$xmlMapping = false;
		} else {
			$configXML = new SimpleXMLElement($xmlMapping);
		}
		
		return $configXML;
	}
}
?>
Return current item: phpBurn