About Yii 1.1.x and future plan

One more thing :)

I think it would be great if as much as possible could be stored in config files

for example layout, forms

so that we can create web based formbuiders, or a webbased page builder

im sure there are other examples.

I suppose what im looking for is

$this->form($config_file);

$this->layout($config_file);

apologies if something like this is already pssible!

Thank you for your suggestions. FYI, the form builder is already available in 1.1.

I think feed management (RSS, Atom ecc.), OpenID, payment wrappers (for PayPal etc.) and maybe GMaps are a must-have.

Hello,

Here is the content of a mail i’ve sent you a few days ago :

Hello Qiang, my name is thibaut. I’m a new member since yesterday. I’m french and i’m very interested in using Yii for professional developpments interacting with mssql server 2005. For that i have made some changes, especially in cactiverecords. Here is in a few worlds what i’ve done creating a new class that extend CActiveRecord (all my models extend my class) :

  • automatic conversion for string values from cp1252 (mssql charset) to utf-8 when retrieving data from the server and when updating a record

  • automatic conversion for date and datetime values retrieving datas from the server (according to the special mssql dateformat)

  • new method safe() that allows to do update and insert in cascade

  • CActiveRecords::$map : an array that stores all objects retrieved by a find method in order to always retrieve and manipulate the same object with the same primary key

  • CActiveRecords : every object has a property : $dirty, in order to know wich object has been modified (and wich attribute). When calling method save() or method safe() only records that are dirty are saved (and only their attributes dirty)

  • new method that allow to save all objects stored in the map

  • and file CMSsqlSchema : a bug returning an array for the primary key when it is not a composite key.

If you are interested in publishing these changes in your framework i can give you the sources. Maybe you will have to check and validate them but, but they are very usefull for me.

I hope i will have soon some news from you.

Tibo

Here is the implementation of my class CActiveRecords. (if you want to make it works you must also need some others classes like CUtils that i should send you later, and just a few changes for class CActiveRecord) :

<?php

/**

  • CActiveRecord class file.

*/

/**

  • CActiveRecords overrides methods of CActiveRecord and add new ones providing :

    • automatic conversion for string values from cp1252 (mssql charset) to utf-8 when retrieving data from the server and when updating a record,
  • according to new parameters values defined in params.php

  •  	//wether to perform or not charset conversions   		
    
  •  'needCharsetConversion'=&gt;true,
    
  •  	//charset used in database
    
  •  	'dbCharset'=&gt;'cp1252',
    
  •  	//charset used in web interface
    
  •  	'webCharset'=&gt;'UTF-8',
    
    • automatic conversion for date and datetime values retrieving datas from the server (according to the special mssql dateformat defined in params.php)
  •  	//the server format date 
    
  •  	'dbFormatDate'=&gt;'j/M/Y',
    
  •  	//the server format dateTime 
    
  •  	'dbFormatDateTime'=&gt;'j/M/Y G:i:s',
    
    • method safe() that allows to do update and insert in cascade. Parameter safeBelongs (params.php) indicate wether we want to
  • cascade for object parent. All type of primary keys are supported (single and composute). All type of relation are supported (belongsTo, has_one, has_many, many_many).

  • here some examples of what is possible to do :

  •  &#036;groupe = new Groupe;
    
  •  &#036;groupe-&gt;name = 'xxx';
    
  •  &#036;person = new Person;
    
  •  &#036;person-&gt;name = 'xxx';
    
  •  &#036;person-&gt;groupe = &#036;groupe;
    
  •  &#036;person-&gt;safe();
    
  •  &#036;person and &#036;groupe are inserted into the database
    
  • $person = Person::model()->findByPk(3);

  •  &#036;one = new Person;
    
  •  &#036;one-&gt;name = 'XXX';		
    
  •  &#036;person-&gt;groupe-&gt;addPerson(&#036;one);
    
  •  &#036;person-&gt;groupe-&gt;name = 'XXX';
    
  •  &#036;person-&gt;name='xxx';
    
  •  &#036;person-&gt;safe();	
    
  •  &#036;person and &#036;groupe are updated into the database, &#036;one is inserted			
    
  • $groupe = Groupe::model()->findByPk(3);

  •  &#036;person = new Person;
    
  •  &#036;person-&gt;name = 'xxx';				
    
  •  &#036;groupe-&gt;addPerson(&#036;person);				
    
  •  &#036;groupe-&gt;name = 'xxx';
    
  •  &#036;groupe-&gt;safe();
    
  •  &#036;person is inserted and &#036;groupe is updated			     
    
    • CActiveRecords::$map : an array that stores all objects retrieved by a find method in order to always retrieve and manipulate the same object with the same primary key
  • for example :

  • $person = Person::model()->findByPk(1);

  • $groupe_1 = $person->groupe;

  • if primary key of $groupe_1 == 1 then

  • $groupe_2 = Groupe::model()->findByPk(1) will be the same object than $groupe_1

  • also :

  • $groupe = Groupe::model()->findByPk(1);

  • $persons = $groupe->persons;

  • foreach ($persons as $person){

  •  &#036;person-&gt;groupe : will retrieve the same object than &#036;groupe	 	 
    
  • }

    • every object has a property : $dirty, in order to know wich object has been modified (and wich attribute). When calling method save() or method safe() only records that are dirty are saved (and only their attributes dirty)
  • $person = Person::model()->findByPk(1);

  • $person->save() : nothing is done

  • $person = Person::model()->findByPk(1);

  • $person->name = ‘xxx’;

  • $person->save() : only the attribute name is updated in the database

    • method safeMap() method that allows to save all objects dirty stored in the map
  • $one_person = Person::model()->findByPk(1);

  • $one_person->name = ‘xxx’;

  • $another_person = Person::model()->findByPk(1);

  • $another_person->name = ‘xxx’;

  • CActiveRecords::safeMap() -> will save $one_person and $another_person

*/

class CActiveRecords extends CActiveRecord

{

private &#036;dirty = array();


private static &#036;safes; 


public static &#036;map;


//private &#036;original_attributes;








/**


 * @return boolean whether the record is dirty : one or more attributes have changed  


 */ 	 


public function getIsDirty()


{		


	return (count(&#036;this-&gt;dirty) &gt; 0) ? true : false;


}	





/**


 * @return array attributes that have been modified   


 */ 	 	


public function getDirty()


{


	return &#036;this-&gt;dirty;


}





/**


 * Adds a new dirty value


 * @param string the attribute name


 * @param mixed the new value for the attribute


 */ 	 	


public function addDirty(&#036;name,&#036;value)


{


	&#036;this-&gt;dirty[&#036;name] = &#036;value;


}


		


/**


 * Adds a new entry for an array attribute 


 * @param property : the array property


 * @param mixed the value to add for the attribute


 * example :


 * &#036;groupe = Groupe::findByPk(1);


 * &#036;person = new Person;


 * &#036;person-&gt;name = 'xxx';


 * &#036;groupe-&gt;addArrayValue('persons',&#036;person);	 	 	 	 	  	 


 */ 	 		


public function addArrayValue(&#036;property,&#036;value)


{


	&#036;array = &#036;this-&gt;&#036;property;


	&#036;array[] = &#036;value;


	&#036;this-&gt;&#036;property = &#036;array;								


}


			


/**


 * Performs charset and date conversions for a record  


 * @param CActiveRecord the record


 * @param string column name;


 * @param mixed value, the value to be converted


 * @return the value converted (if necessary, according to parameters in params.php) 	 


 */	


protected function performConversions(&#036;record,&#036;name,&#036;value)


{


		&#036;column = &#036;record-&gt;getMetaData()-&gt;columns[&#036;name];


		&#036;type = &#036;column-&gt;dbType;


		


		if (strpos(&#036;type,'char') &#33;== false){


			&#036;value = CUtils::convertToWebCharset(&#036;value);


		}else if (strpos(&#036;type,'date') &#33;== false){


			&#036;value = CUtils::convertDbDateToString(&#036;value);


		}


		


		return &#036;value;							  					


}	





/**


 * This method is overridden from CActiveRecord   	   	 	


 * Calls parent::save method only if the record is dirty, otherwise do nothing


 * @param boolean whether to perform validation before saving the record. see CActiveRecord::save


 * @param array list of attributes that need to be saved. Defaults to null. see CActiveRecord::save	 


 * @return true if the record is not new and not dirty, else return parent::save() to execute save 	  	 


 */		


public function save(&#036;runValidation=true,&#036;attributes=null)


{


	if (&#036;this-&gt;getIsNewRecord() == false){


		if (&#036;this-&gt;getIsDirty() == false)


			return true;						


	}


	


	return parent::save(&#036;runValidation,&#036;attributes);


}





/**


 * Allows update and insert in cascade


 * @param boolean. Do not set it to false when you call safe method. &quot;true&quot; initialize the array of safed records.


 * it is set to false for recursive calls.	  	 


 * @return true if the record is not new and not dirty, else return parent::save() to execute save 	  	 


 */		


public function safe(&#036;first_call = true)


{						


	//Yii::log('safe : '.get_class(&#036;this),'debug');


			


	if (&#036;first_call == true)


		self::&#036;safes = array(); 


			


	&#036;class = get_class(&#036;this);





	//Check wether we entered this method for THIS record 


	//Example : 


	//&#036;person = Person::model()-&gt;findByPk(x);


	//&#036;all = &#036;person-&gt;groupe-&gt;persons;


	//do something with &#036;all...


	//foreach(&#036;all as &#036;p){


	//	&#036;p-&gt;xxx = 'xxx';	


	//}


	//&#036;person-&gt;name='xxx';


	//&#036;person-&gt;safe();


	//when saving &#036;person we first save &#036;groupe.But when saving &#036;groupe we save all persons from this groupe.


	//We do not want when saving a person of this groupe calling another time the safe method for this groupe because


	//it has already been done. Otherwise we do the same twice and it is without ending : from person we call groupe and


	//from groupe we call person and from person we call groupe...


	//Also : when saving all persons of the groupe it prevents from saving the person : &quot;&#036;person&quot; for wich we already entered


	//the safe method. So this record will be saved after the recursive call of the safe method for its group


	if (&#33;&#036;this-&gt;getIsNewRecord()){    


		if (isset(self::&#036;safes[&#036;class][serialize(&#036;this-&gt;getPrimaryKey())])){


		  //Yii::log(&#036;class.' will be saved after save '.&#036;class,'debug');


			return;


		}


    	       


    self::&#036;safes[&#036;class][serialize(&#036;this-&gt;getPrimaryKey())] = 'safe update';				


	}	


			


	&#036;relations = &#036;this-&gt;relations();


			


	//Must save parent records (CBelongsToRelation), in order to retrieve its primary key and to affect it to its child


	//before saving it				


	if (Yii::app()-&gt;getParams()-&gt;safeBelongsTo == true){															


		foreach(&#036;relations as &#036;name=&gt;&#036;relation){			


			if (&#036;this-&gt;hasRelated(&#036;name)){


				&#036;related = &#036;this-&gt;&#036;name;


																														


				if (&#036;relation[0] == CActiveRecord::BELONGS_TO){


					//Yii::log('from '.&#036;class.' safe belongsTo '.get_class(&#036;related),'debug');																											


					&#036;related-&gt;safe(false);						


					&#036;this-&gt;setForeignValueFromRelation(&#036;relation,&#036;related);


				}					   					


			}		


		}		


	}		


	


	/*Has been put at the beginning of the method 


	if (&#33;&#036;this-&gt;getIsNewRecord()){    


		if (isset(self::&#036;safes[&#036;class][serialize(&#036;this-&gt;getPrimaryKey())])){


		  //Yii::log(&#036;class.' already saved','debug');


			return;


		}


    	       


    self::&#036;safes[&#036;class][serialize(&#036;this-&gt;getPrimaryKey())] = 'safe update';				


	}


	*/										


	


	//Yii::log('save '.&#036;class,'debug');		


	if (&#036;this-&gt;save() === false){


		throw new Exception('save error');


	}		


	 							


	foreach(&#036;relations as &#036;name=&gt;&#036;relation){									


		if (&#036;this-&gt;hasRelated(&#036;name)){				


			&#036;related = &#036;this-&gt;&#036;name;


							


			if (&#036;relation[0] == CActiveRecord::HAS_ONE){


				//Yii::log('from '.&#036;class.' safe toOne '.get_class(&#036;obj),'debug');


				&#036;related-&gt;setForeignValueFromRelation(&#036;relation,&#036;this);										


				&#036;related-&gt;safe(false);


			}else if (&#036;relation[0] == CActiveRecord::HAS_MANY){															


				foreach (&#036;related as &#036;k=&gt;&#036;obj){


					if (&#036;obj instanceof CActiveRecord){							


						//Yii::log('from '.&#036;class.' safe oneToMany '.get_class(&#036;obj),'debug');


						&#036;obj-&gt;setForeignValueFromRelation(&#036;relation,&#036;this);																																								


						&#036;obj-&gt;safe(false);


					}


				}					


			}else if (&#036;relation[0] == CActiveRecord::MANY_MANY){


				//throw new Exception('method CActiveRecords::safe not implemented for many_to_many relation');


				foreach (&#036;related as &#036;k=&gt;&#036;obj){


					if (&#036;obj instanceof CActiveRecord){							


						//Yii::log('from '.&#036;class.' safe manyToMany '.get_class(&#036;obj),'debug');


						&#036;wasNew = &#036;obj-&gt;getIsNewRecord();


						


						&#036;obj-&gt;safe(false);


						


						if (&#036;wasNew){																					


							&#036;clazz = get_class(&#036;obj).&#036;class;								


							if (&#33;class_exists(&#036;clazz,false)) {


 						   &#036;clazz = &#036;class.get_class(&#036;obj);


 						  }


															


							&#036;linker = new &#036;clazz;


							&#036;linker-&gt;initFromRelated(&#036;this,&#036;obj);


							&#036;linker-&gt;safe();								


						}																																																													


					}


				} 


			}// end if																				 					


		}// end hasRelated		


	}// end foreach			


		


}





/**


 * Initializes foreign keys values from a relation and a related record


 * @param array relation the relation array record


 * @param CActiveRecord the related record	 	  	  	  	 


 */		


public function setForeignValueFromRelation(&#036;relation,&#036;related)


{		


	&#036;pk = &#036;related-&gt;getPrimaryKey();


	&#036;fk = &#036;relation[2];


	&#036;foreign = &#036;this-&gt;getMetaData()-&gt;tableSchema-&gt;foreignKeys;		


			


	if (&#33;is_array(&#036;pk)){


		&#036;this-&gt;&#036;fk = &#036;pk;


	}else{


		&#036;tab = split(',',&#036;fk);


		foreach(&#036;tab as &#036;column){


			&#036;index = &#036;foreign[&#036;column][1];


			&#036;this-&gt;&#036;column = &#036;pk[&#036;index];


			//Yii::log(get_class(&#036;this).' set '.&#036;column.' = '.&#036;pk[&#036;index],'debug');				


		}


	}


}





/**


 * This method is overridden from CActiveRecord   	   	 	


 * Called from CActiveFinder, which performs lazy loading


 * method afterFind() will be called later


 * @return parent::afterFindInternal()	 


 */			


public function afterFindInternal()


{


	return parent::afterFindInternal();


}





/**


 * This method is overridden from CActiveRecord   	   	 	


 * Called after a find method. Add record to the map (cache)


 * @return parent::afterFind() 	 	 


 */			


public function afterFind()


{		


	//&#036;this-&gt;original_attributes = &#036;this-&gt;getAttributes();


									


	//add it to the cache


	self::addMap(&#036;this);


	


	return parent::afterFind();


}








/**


 * This method is overridden from CActiveRecord	


 * Calls parent::update only if the record is dirty, and only for dirty values


 * @return parent::update only for dirty records and dirty values, else return nothing (and do nothing)	 	 


 */				


public function update(&#036;attributes=null)


{	


	if (&#036;this-&gt;getIsDirty() == false)


		return;


	


	&#036;dirty = &#036;this-&gt;getDirty();


	


	//&#036;new = &#036;this-&gt;getAttributes();		


	//&#036;old = &#036;this-&gt;original_attributes;


	//&#036;dirty = array_diff_assoc(&#036;new,&#036;old);								


	


	if (count(&#036;dirty) &gt; 0){


		return parent::update(array_keys(&#036;dirty));


	} 


				


} 





/**


 * Clears map (cache) for a couple class - primary keys


 * @param string : the record class name


 * @param mixed : string or array of primary keys


 */					


static function clearMapClass ( &#036;clazz, &#036;pk ) {


	if (isset(self::&#036;map[&#036;clazz])) unset(self::&#036;map[&#036;clazz]);


}





/**


 * Clears all map (cache)


 */						


static function clearMap () {


	self::&#036;map = array();


}





/**


 * Checks if the record is not in map and replace it if true


 * @param CActiveRecord : the record to check


 * @return CActiveRecord : the record in map if found, else the record param	 	 


 */							


static function replaceRecordMap(&#036;record)


{


	&#036;mapped = self::getRecordMap(&#036;record);


	


	if (&#036;mapped &#33;== null){


		//Yii::log('replaceRecordMap '.get_class(&#036;record),'debug');


		return &#036;mapped;


	}		


		


	return &#036;record;


}





/**


 * Checks if the record is in map and return it


 * @param CActiveRecord : the record to check


 * @return CActiveRecord : the record in map if found	 	 


 */								


static function getRecordMap(&#036;record)


{


	return self::getMap(get_class(&#036;record),&#036;record-&gt;getPrimaryKey());		


}





/**


 * Checks if a couple CActiveRecord class name - primary keys exist in map


 * @param string CActiveRecord class name


 * @param mixed : string or array, the primary keys 	 


 * @return CActiveRecord if found couple CActiveRecord class name - primary keys, or null if not found	 	 


 */									


static function getMap (&#036;clazz,&#036;pk) {				


	if (is_object(&#036;clazz)){


		&#036;class = get_class(&#036;clazz);


	}else{


		&#036;class = &#036;clazz;


	}


					


	&#036;pk = self::pkMap(&#036;pk);


	


	//Yii::log('getMap class '.&#036;class.' pk '.&#036;pk,'debug');		


			


	if (isset(self::&#036;map[&#036;class]) &amp;&amp; isset(self::&#036;map[&#036;class][serialize(&#036;pk)])) {


		//Yii::log('getMap found '.&#036;class.' pk '.&#036;pk,'debug');


		return self::&#036;map[&#036;class][serialize(&#036;pk)];


	}


	


	return null;


}





/**


 * Converts all primary key in string before putting it in map 


 * @param mixed : string or array, primary keys


 * @return primary keys converted	 	 


 */									


private static function pkMap(&#036;pk)	


{		


	// if there's only one pk, use instead of the array


	if (is_array(&#036;pk) &amp;&amp; count(&#036;pk)==1) &#036;pk = array_shift(&#036;pk);


	


	if (&#33;is_array(&#036;pk)){


		&#036;pk = (string) &#036;pk;


	}else{


		foreach(&#036;pk as &#036;key=&gt;&#036;val){


			&#036;val = (string) &#036;val;


		}


	}


	


	return &#036;pk;


}





/**


 * Adds a record to the map 


 * @param CActiveRecord the record to add	 	 


 */										


static function addMap (&#036;record) {


	&#036;class = get_class(&#036;record);


	&#036;pk = self::pkMap(&#036;record-&gt;getPrimaryKey());


	


	//Yii::log('addMap : '.&#036;class.' pk '.&#036;pk,'debug');		


	


	// initialize map for this class


	if (&#33;isset(self::&#036;map[&#036;class])) self::&#036;map[&#036;class] = array();


	


	self::&#036;map[&#036;class][serialize(&#036;pk)] = &#036;record;


}	





/**


 * Saves all records stored in the map. Within a transaction 	 	 


 */												


public static function safeMap()


{


	&#036;transaction = Yii::app()-&gt;getDb()-&gt;beginTransaction();


	


	&#036;init = true;


	


	try{


		foreach(self::&#036;map as &#036;class){


			foreach(&#036;class as &#036;ref){


				&#036;ref-&gt;safe(&#036;init);


				&#036;init = false;				


			}


		}


		


		&#036;transaction-&gt;commit();						


	}catch (Exception &#036;e) {


		//Yii::log('rollBack()','debug');


		&#036;transaction-&gt;rollBack();		


	}							


}	





/**


 * This method is overridden from CActiveRecord


 * Sets an attribute value. If the value has changed, add the attribute to the array of dirty attributes


 * @param string : the name of the attribute to be set


 * @param mixed the attribute value


 * @return parent::setAttribute	  	 	  	 	 


 */													


public function setAttribute(&#036;name,&#036;value)


{


	if((property_exists(&#036;this,&#036;name))||(isset(&#036;this-&gt;getMetaData()-&gt;columns[&#036;name]))){


		if (&#036;this-&gt;getIsNewRecord() == true){


			&#036;this-&gt;addDirty(&#036;name,&#036;value);	


		}else{


			&#036;oldValue = &#036;this-&gt;&#036;name;	


			


			if ((string)&#036;oldValue &#33;== (string)&#036;value){


				&#036;this-&gt;addDirty(&#036;name,&#036;value);


			}			


		}				


	}								


	


	return parent::setAttribute(&#036;name,&#036;value);		


}





/**


 * Gets the value of foreign keys from a relation object


 * @param CBaseActiveRelation


 * @return mixed : string or array values	  	 	  	 	 


 */														


private function getForeignValueFromRelation(&#036;relation)	


{		


	&#036;property = &#036;relation-&gt;foreignKey;


	


	if (strlen(&#036;property) == 0)


		return false;


				


	if (strpos(&#036;property,',') &#33;== false){


		&#036;tab = split(',',&#036;property);


		&#036;foreign = &#036;this-&gt;getMetaData()-&gt;tableSchema-&gt;foreignKeys;


		//&#036;keys = CActiveRecord::model(&#036;relation-&gt;className)-&gt;primaryKey;			


		&#036;keys = array();						


																	


		foreach(&#036;tab as &#036;prop){


			&#036;value = &#036;this-&gt;&#036;prop;


			&#036;column = &#036;foreign[&#036;prop][1];


			&#036;keys[&#036;column] = &#036;value;			


		}


					


		return &#036;keys;			


	}else{			


		return &#036;this-&gt;&#036;property;


	}							


}





/**


 * This method is overridden from CActiveRecord


 * PHP getter magic method. 	 


 * If the attribute we want to retrieve is a related CActiveRecord, first check if it is in the map	  	 


 * @param string : the name of the attribute


 * @return parent::get() if the attribute is not in the map, else return nothing but add the record found in the map


 * to the current record. So there is one query less. 	 


 */


public function __get(&#036;name)


{		


	//Yii::log(get_class(&#036;this).' get '.&#036;name,'debug');


			


	if (isset(&#036;this-&gt;getMetaData()-&gt;relations[&#036;name])){			


		if (&#33;&#036;this-&gt;hasRelated(&#036;name)){


			//Yii::log(get_class(&#036;this).' has related '.&#036;name,'debug');


			&#036;relation=&#036;this-&gt;getMetaData()-&gt;relations[&#036;name];


			if (&#036;relation instanceof CBelongsToRelation){


				//Yii::log('related '.&#036;name.' is instance of CBelongsToRelation','debug');						


				&#036;pk = &#036;this-&gt;getForeignValueFromRelation(&#036;relation);


					


				if (&#036;pk &#33;== false){


					&#036;related = self::getMap(&#036;relation-&gt;className,&#036;pk);


												


					if (&#036;related &#33;== null){


						//Yii::log(get_class(&#036;this).' found related '.&#036;name,'debug');							


						&#036;this-&gt;addRelatedRecord(&#036;name,&#036;related,false);


					}				


				}										


			}													


		}									


	}


		


	return parent::__get(&#036;name);				


}








/**


 * Retrieves primary key values from an array of couple attribute name - values	  	 


 * @param array : the array of couple attribute name - values


 * @return mixed : string or array of values if primary key name found in &#036;attributes, otherwise null


 */	


private function getPkValuesFromAttributes(&#036;attributes)


{


	&#036;table=&#036;this-&gt;getMetaData()-&gt;tableSchema;				


	&#036;pk = &#036;table-&gt;primaryKey;					


	&#036;values = null;


				


	if(is_string(&#036;pk)){


		if(isset(&#036;attributes[&#036;pk])){


			&#036;values=&#036;attributes[&#036;pk];


		}			


	}else if(is_array(&#036;pk)){


		&#036;values=array();


		foreach(&#036;pk as &#036;name=&gt;&#036;alias){


			if(isset(&#036;attributes[&#036;alias])){


				&#036;values[&#036;name]=&#036;attributes[&#036;alias];


			}				


		}			


	}


	


	return &#036;values;		


}








/**


 * Retrieves a record stored in the map (if found) from a given array of attribute name - values	


 * The primary key name and values must be in the given array  	   	 


 * @param array : the array of attribute name - values


 * @return CActiveRecord the record found in the map, or null if not found


 */	


private function getRecordMapFromAttributes(&#036;attributes)


{


	&#036;values = &#036;this-&gt;getPkValuesFromAttributes(&#036;attributes);


	


	//Yii::log('getRecordMapFromAttributes pk '.&#036;values,'debug');		


			


	if (&#33;is_null(&#036;values)){


		&#036;mapped = self::getMap(get_class(&#036;this),&#036;values);


		if (&#33;is_null(&#036;mapped)){


			return &#036;mapped;			 


		}


	}	


	


	return null;	


}





/**


 * This method is overridden from CActiveRecord   	   	 


 * Populates a record from an array of attribute name - values or return the record if it is stored in the map


 * For that we check the primary key values found in the given array attributes	 


 * @param array : the array of attribute name - values, from wich to populate the record


 * @param boolean : wether the method is invoked after a find method


 * @param boolean : wether the method is invoked after a lazy loading (from a CActiveFinder). Not used actually, must change all calls from CActiveFinder class


 * @return CActiveRecord the record found in the map, or parent::populateRecord if not found


 */	


public function populateRecord(&#036;attributes,&#036;callAfterFind=true,&#036;lazyload=false)


{


	//if (&#036;lazyload==false)


	//	return parent::populateRecord(&#036;attributes,&#036;callAfterFind);


	


	&#036;record = &#036;this-&gt;getRecordMapFromAttributes(&#036;attributes);


	


	if (&#036;record &#33;== null)


		return &#036;record;


						


	return parent::populateRecord(&#036;attributes,&#036;callAfterFind);		


			


}








/**


 * This method is overridden from CActiveRecord   	   	 


 * Populates records from an array of attribute name - values or return the records if they are stored in the map


 * For that we check the primary key values found in the given array attributes	 


 * @param array : the array (of array) of attribute name - values, from wich to populate the records


 * @param boolean : wether the method is invoked after a find method


 * @param boolean : wether the method is invoked after a lazy loading (from a CActiveFinder). Not used actually, must change all calls from CActiveFinder class 	  	 


 * @return array the records found in the map, or parent::populateRecords if not found


 */	


public function populateRecords(&#036;data,&#036;callAfterFind=true,&#036;lazyload=false)


{


	//Yii::log('populateRecords','debug');


	


	//if (&#036;lazyload==false)


	//	return parent::populateRecords(&#036;data,&#036;callAfterFind);


	


	&#036;records=array();


	


	foreach(&#036;data as &#036;attributes){


		&#036;record = &#036;this-&gt;populateRecord(&#036;attributes,&#036;callAfterFind,&#036;lazyload);


		&#036;records[]=&#036;record;


	}


	


	return &#036;records;		


}





/**


 * This method is overridden from CActiveRecord   	   	 


 * Finds a record from a given primary key. Check before if the record is not stored in the map.


 * @param mixed : string or array of primary keys values


 * @param mixed query condition or criteria.


 * @param array parameters to be bound to an SQL statement.


 * @return CActiveRecords the record found in the map, or parent::findByPk() to do a query


 */	


public function findByPk(&#036;pk,&#036;condition='',&#036;params=array())


{


	//Yii::log('CActiveRecords findByPk '.get_class(&#036;this).' pk '.&#036;pk,'debug');


									


	&#036;mapped = self::getMap(get_class(&#036;this),&#036;pk);


	if (&#036;mapped &#33;== null){


		//Yii::log('findByPk return mapped','debug');


		return &#036;mapped;


	}else{


		return parent::findByPk(&#036;pk,&#036;condition,&#036;params);


	}		


}








/**


 * This method is overridden from CActiveRecord   	   	 


 * Finds all records that matches given primary keys. Check before if the records are not stored in the map.


 * @param array : array of primary keys values


 * @param mixed query condition or criteria.


 * @param array parameters to be bound to an SQL statement.


 * @return CActiveRecords the records found in the map, or parent::findByPk() to do a query


 */	


public function findAllByPk(&#036;pk,&#036;condition='',&#036;params=array())


{


	//Yii::log('findAllByPk','debug');


	


	&#036;added = array();


	&#036;searched = array();


	&#036;records = array();


	


	//Pour chaque &#036;pk il faut regarder si on ne l'a pas en cache et si c'est le cas la retirer de l'array


	foreach(&#036;pk as &#036;i=&gt;&#036;key){


		&#036;mapped = self::getMap(get_class(&#036;this),&#036;key);			


		if (&#036;mapped &#33;== null){


			&#036;added[] = &#036;mapped;


		}else{


			&#036;searched[] = &#036;key;   


		}			


	}


	


	//foreach(&#036;searched as &#036;key){


	//	Yii::log('pk left '.&#036;key,'debug');


	//}


			


	if (count(&#036;pk)&gt;0)		


		&#036;records = parent::findAllByPk(&#036;searched,&#036;condition,&#036;params);					


	


	foreach(&#036;added as &#036;to_add){


		&#036;records[] = &#036;to_add;


	}


	


	return &#036;records;


}

}

?>

Any changes that this issue with relation saving will be handled in nearby releases?

See: http://www.yiiframework.com/forum/index.php?/topic/5073-saving-relation/

Now, in times of PHP 5.3 and above, we could modernize the naming System and remove the Prefix C.

In Yii2.0 (or somewhere in the future, when >PHP5.3 is standard) we could use a namespace "Yii" or "YiiHtml". Functions then would just be called Widget() or InputForm()…

Is this a good Idea?

I think a simple web-based RAD like phpmaker is useful.

I second this as well. Qiang, please take a look at phpmaker for some ideas. It is a Desktop PHP RAD app for Windows that has been the basis for all my web development projects for the last 10 years. If yiic web version can implement some of what phpmaker has, it will be a big winner.

After 10 years, I’ve recently decided to get out of my PHPMaker comfort zone to try other frameworks to learn some fresh ideas (learn MVC and a new langauge). My choices were to join the Ruby/Rails crowd, go with ASP.NET MVC or choose a MVC based PHP framework (finally narrowed it down to Akelos or Yii - since I registered/posted here, you already know which one I picked ;).

Based on your previous project: PRADOS, I’m sure you will be more experience this time to build PRADOS MVC version.

In analogy PRADOS = ASP.NET Then Yii = ASP.NET MVC… (I’m sure it will be even better than ASP.NET MVC in the future ;)

A new prospective user of Yii

Qiang, do you have any idea when the 1.1 version is expanded and stable enough to release it for a production environment?

In the announcement forum he stated it would be released this month. See :

http://www.yiiframework.com/forum/index.php?/topic/6067-yii-1-0-11-and-1-1rc-are-released/

nz

Congratulations!

Yii1.1 has been released!

When 1.1.1 will be released? I’m waiting for Active Form model and ajax validation :)

They are pretty stable, try using SVN.

Did you try Qcodo php framework which share some phpmaker philosophy, goals and features ?

I would be a fan of this approach if it were for NoSQL DB like Neo4j or MongoDB:)