Created
May 13, 2018 06:59
-
-
Save rotexdegba/99b0f120ddf4fe0a93b2a38dbff84a66 to your computer and use it in GitHub Desktop.
Old Collection Class
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* | |
* A collection of Cfs_Model_Base_Record records. | |
* @package Cfs_Model | |
* | |
*/ | |
class Cfs_Model_Base_Collection extends Solar_Sql_Model_Collection { | |
/** | |
* | |
* A list of valid php comparison operators. | |
* | |
* @var array A list of valid php comparison operators. | |
* | |
*/ | |
private static $_php_operators = array('==', '===', '!=', '<>', '!==', '<', '>', '<=', '>='); | |
/** | |
* | |
* Get 2 character locale code (e.g. en, fr) for the currently selected locale | |
* in a web app extending Cfs_App_Base. | |
* | |
* @return string 2 character locale code (e.g. en, fr) for the currently selected | |
* locale in a web app extending Cfs_App_Base. | |
* | |
* @see Cfs_Model_Base::get_Lang() | |
* | |
*/ | |
public static function get_Lang(){ return Cfs_Model_Base::get_Lang(); } | |
/** | |
* | |
* Get 2 character locale code (e.g. en, fr) for the currently selected locale | |
* in a web app extending Cfs_App_Base. | |
* | |
* @return string 2 character locale code (e.g. en, fr) for the currently selected | |
* locale in a web app extending Cfs_App_Base. | |
* | |
* @see Cfs_Model_Base_Collection::get_Lang() | |
* | |
*/ | |
public function getLang(){ return self::get_Lang(); } | |
/** | |
* | |
* Returns reference to the internal array being used to store data in this collection. | |
* | |
* Please use carefully! We are dealing with references here and we don't want to get burned! | |
* | |
* @return array A reference to the internal array being used to store data in | |
* this collection. | |
* | |
*/ | |
public function &getRefToInternalArray(){ return $this->_data; } | |
// Support methods taken from Solar Beta | |
/** | |
* | |
* Removes one record from the collection but <strong>does not</strong> delete it from the database. | |
* | |
* @param mixed $spec If a Solar_Sql_Model_Record, looks up the record in | |
* the collection and deletes it. Otherwise, is treated as an offset | |
* value (**not** a record primary key value) and that record is removed. | |
* | |
* @return void | |
* | |
* @see Cfs_Model_Base_Collection::getRecordOffset() | |
* | |
*/ | |
public function removeOne($spec) { | |
if ($spec instanceof Solar_Sql_Model_Record) { | |
$key = $this->getRecordOffset($spec); | |
if ($key === false) { | |
throw $this->_exception( | |
'ERR_NOT_IN_COLLECTION', | |
$spec->toArray() | |
); | |
} | |
} else { | |
$key = $spec; | |
} | |
unset($this->_data[$key]); | |
} | |
/** | |
* | |
* Given a record object, looks up its offset value in the collection. | |
* | |
* For this to work, the record primary key must exist in the collection, | |
* **and** the record looked up in the collection must have the same | |
* primary key and be of the same class. | |
* | |
* Note that the returned offset may be zero, indicating the first element | |
* in the collection. As such, you should check the return for boolean | |
* false to indicate failure. | |
* | |
* @param Solar_Sql_Model_Record $record The record to find in the collection. | |
* | |
* | |
* @return mixed The record offset (which may be zero), or boolean false | |
* if the same record was not found in the collection. | |
* | |
*/ | |
public function getRecordOffset($record) { | |
// the primary value of the record | |
$val = $record->getPrimaryVal(); | |
// mapping of primary-key values to offset values | |
$map = array_flip($this->getAllPrimaryOrOtherColsVals()); | |
// does the record primary value exist in the collection? | |
// use array_key_exists() instead of empty() so we can honor zeroes. | |
if (! array_key_exists($val, $map)) { | |
return false; | |
} | |
// retain the offset value | |
$offset = $map[$val]; | |
// look up the record inside the collection | |
$lookup = $this->__get($offset); | |
// the primary keys are already known to be the same from above. | |
// if the classes match as well, consider records to be "the same". | |
if (get_class($lookup) === get_class($record)) { | |
return $offset; | |
} else { | |
return false; | |
} | |
} | |
// End Support methods taken from Solar Beta | |
/** | |
* | |
* Fetch one or more record(s) from a collection whose value(s) match the value(s) | |
* of the column(s) and value(s) specified. | |
* | |
* If count($cols) != count($vals) a null value will be returned by this method. | |
* | |
* <br /> | |
* **Usage:** | |
* | |
* - $this->_findRecordByCols( array( 'col_1_name', 'col_2_name' ), array( 'col_1_val', 'col_2_val' ), true, array(), false, false ) | |
* | |
* **SQL Equivalent:** Select * from table where col_1_name = 'col_1_val' and col_2_name = 'col_2_val' | |
* | |
* <br /> | |
* - $this->_findRecordByCols( 'col_1_name', 'col_1_val', true, array(), false, false ) | |
* | |
* **SQL Equivalent:** Select * from table where col_1_name = 'col_1_val' | |
* | |
* <br /> | |
* - $this->_findRecordByCols( array( 'col_1_name', 'col_2_name' ), array( 'col_1_val', 'col_2_val' ), true, array( 'col_1_name'=>'!=' ), false, false ) | |
* | |
* **SQL Equivalent:** Select * from table where col_1_name <> 'col_1_val' and col_2_name = 'col_2_val' | |
* | |
* <br /> | |
* - $this->_findRecordByCols( array( 'col_1_name', 'col_2_name' ), array( 'col_1_val', 'col_2_val' ), true, array( 'col_1_name'=>'!=', 'col_2_name'=>'<=' ), false, false ) | |
* | |
* **SQL Equivalent:** Select * from table where col_1_name <> 'col_1_val' and col_2_name <= 'col_2_val' | |
* | |
* | |
* @param mixed $cols (string | array) An array of column/field name(s) or a single | |
* column/field name (string) to be checked in each record. | |
* | |
* @param mixed $vals (int | double | string | array) A list of corresponding value(s) | |
* or single value for the field(s) specified in $cols. | |
* | |
* @param boolean $return_all_matching_records **False** to determine if only the | |
* first matching record should be returned (default behaviour), or **True** if all matching | |
* records should be returned. | |
* | |
* @param array $comparision_operators_per_col An array of column_name=>php_comparison_operator | |
* pairs for the columns we are searching by (The php_comparison_operator should be a string | |
* containing one of the valid php comparison operators specified | |
* <a href="http://php.net/manual/en/language.operators.comparison.php">here</a>). | |
* | |
* @param boolean $return_cloned_records **False**, if the matching records in the collection | |
* should be directly returned (default behaviour) or **True** if a clone of each matching record | |
* should be returned. | |
* | |
* @param boolean $remove_matched_records_from_collection **True** if matched record(s) should be | |
* removed from the collection (**$this**) or **False** if not. | |
* | |
* | |
* @return mixed (Cfs_Model_Base_Record | Cfs_Model_Base_Collection | null) First matching record | |
* or a collection of all matching records. Null is returned if there are no matching record(s). | |
* | |
*/ | |
protected function _findRecordByCols($cols, $vals, $return_all_matching_records=false, $comparision_operators_per_col=array(), $return_cloned_records=false, $remove_matched_records_from_collection=false) { | |
$result = null; | |
$curr_rec_2_b_returned = 0; | |
$collection_keys_4_matched_records = array(); | |
if($return_all_matching_records){ $result = Solar::factory(get_class($this)); } | |
if(is_string($cols)){ $cols = array($cols); } | |
if(is_string($vals) || is_numeric($vals)){ $vals = array($vals); } | |
if(!(count($cols) < count($vals) ||count($cols) > count($vals))){ | |
$num_records = count($this); | |
$num_fields = count($cols); | |
$keys_2_dis_cllctn = array_keys($this->_data); | |
for($curr_record=0; $curr_record<$num_records; $curr_record++) { | |
$record = $this[$keys_2_dis_cllctn[$curr_record]]; | |
$matched = true; | |
for($curr_field_index=0; $curr_field_index < $num_fields && $matched; $curr_field_index++){ | |
$specified_cols_name = $cols[$curr_field_index]; | |
$specified_cols_value = $vals[$curr_field_index]; | |
if(empty($comparision_operators_per_col)){ | |
//use default comparison technique | |
$matched = $matched && $this->_doNormalOrStrictEqualityComparison($record[$specified_cols_name], $specified_cols_value); | |
}else{ | |
if(array_key_exists($specified_cols_name, $comparision_operators_per_col)){ | |
if(in_array($comparision_operators_per_col[$specified_cols_name], self::$_php_operators)){ | |
//we have a valid comparison operator for the current column | |
$matched = $matched && $this->_doComparison($comparision_operators_per_col[$specified_cols_name], $record[$specified_cols_name], $specified_cols_value); | |
}else{ | |
//use default comparison technique | |
$matched = $matched && $this->_doNormalOrStrictEqualityComparison($record[$specified_cols_name], $specified_cols_value); | |
} | |
}else{ | |
//use default comparison technique | |
$matched = $matched && $this->_doNormalOrStrictEqualityComparison($record[$specified_cols_name], $specified_cols_value); | |
} | |
} | |
} | |
if($matched){ | |
$curr_records_key_in_collection = $keys_2_dis_cllctn[$curr_record]; | |
$collection_keys_4_matched_records[] = $curr_records_key_in_collection; | |
if($return_all_matching_records){ | |
//don't remove record from collection here if $remove_matched_records_from_collection | |
//is True, because we are still in the loop and keys will get messed up. | |
$result[$curr_rec_2_b_returned++] = ($return_cloned_records)? clone $record: $record; | |
}else{ | |
//Remove record from collection here if $remove_matched_records_from_collection | |
//is True, because we are returning result anyways and exiting this method in | |
//this else block. | |
if($remove_matched_records_from_collection){ | |
unset($this->_data[$curr_records_key_in_collection]); | |
} | |
return ($return_cloned_records)? clone $record: $record; | |
} | |
} | |
} | |
} | |
if(count($collection_keys_4_matched_records) > 0 && $remove_matched_records_from_collection){ | |
foreach($collection_keys_4_matched_records as $key){ | |
unset($this->_data[$key]); | |
} | |
} | |
if(count($result)== 0){ $result = null; } | |
return $result; | |
} | |
/** | |
* | |
* Tests the equality of two values. | |
* Does regular ( == ) comparison if the values are both numeric. | |
* | |
* Otherwise, does a strict ( === ) comparison of the values casted to strings | |
* (i.e. ( (string) $value1 ) === ( (string) $value2 ) ). | |
* | |
* @param mixed $value1 A value to be compared with the second value. | |
* | |
* @param mixed $value2 A value to be compared with the first value. | |
* | |
* | |
* @return boolean True if both parameters are equal in value, else False. | |
* | |
*/ | |
private function _doNormalOrStrictEqualityComparison($value1, $value2){ | |
$both_numeric = is_numeric($value1) | |
&& is_numeric($value2); | |
if ($both_numeric) { | |
// use normal inequality | |
return ($value1 == $value2); | |
}else{ | |
// use strict inequality | |
return (((string)$value1) === ((string)$value2)); | |
} | |
} | |
/** | |
* | |
* Fetch first record in a collection whose value(s) for the specified cols ($cols), | |
* match the specified value(s) ($vals). | |
* | |
* If count( $cols ) != count( $vals ) a null value will be returned by this method. | |
* | |
* <br /> | |
* **Usage:** | |
* | |
* - $this->_findOneRecordByCols( array( 'col_1_name', 'col_2_name' ), array( 'col_1_val', 'col_2_val' ) ) | |
* | |
* **SQL Equivalent:** SELECT * FROM table WHERE col_1_name = 'col_1_val' AND col_2_name = 'col_2_val' LIMIT 1 | |
* | |
* <br /> | |
* - $this->_findOneRecordByCols( 'col_1_name', 'col_1_val' ) | |
* | |
* **SQL Equivalent:** SELECT * FROM table WHERE col_1_name = 'col_1_val' LIMIT 1 | |
* | |
* @param mixed $cols (string | array) A list of column / field name(s) to be | |
* checked in each record. | |
* | |
* @param mixed $vals (int | double | string | array) A list of corresponding value(s) for the | |
* field(s) specified in $cols. | |
* | |
* @param boolean $return_cloned_record **False** if the first matching record in the collection | |
* should be directly returned (default behaviour), else **True** if a **clone** of the first | |
* matching record should be returned. | |
* | |
* | |
* @return mixed (Cfs_Model_Base_Record | null) First record in this collection whose value(s) | |
* for the specified cols ($cols), match the specified value(s) ($vals), or null if there is no | |
* matching record. | |
* | |
*/ | |
protected function _findOneRecordByCols($cols, $vals, $return_cloned_record = false) { | |
$result = null; | |
$curr_rec_2_b_returned = 0; | |
if(is_string($cols)){ $cols = array($cols); } | |
if(is_string($vals) || is_numeric($vals)){ $vals = array($vals); } | |
if(!(count($cols) < count($vals) ||count($cols) > count($vals))){ | |
$num_records = $this->count(); | |
if($num_records > 0){ | |
$num_fields = count($cols); | |
$keys_2_dis_cllctn = array_keys($this->_data); | |
//only one record, no need to loop | |
if($num_records == 1){ | |
$record = $this->firstItem(); | |
$matched = true; | |
for($curr_field_index=0; $curr_field_index < $num_fields && $matched; $curr_field_index++){ | |
$specified_cols_name = $cols[$curr_field_index]; | |
$specified_cols_value = $vals[$curr_field_index]; | |
$matched = $matched && $this->_doNormalOrStrictEqualityComparison($record[$specified_cols_name], $specified_cols_value); | |
} | |
if($matched){ | |
return ($return_cloned_record)? clone $record : $record; | |
} | |
}else{ | |
//start from the middle to the first element | |
$start = floor($num_records/2); | |
for($curr_record_index = $start; $curr_record_index >=0; $curr_record_index--){ | |
$record = $this[$keys_2_dis_cllctn[$curr_record_index]]; | |
$matched = true; | |
for($curr_field_index=0; $curr_field_index < $num_fields && $matched; $curr_field_index++){ | |
$specified_cols_name = $cols[$curr_field_index]; | |
$specified_cols_value = $vals[$curr_field_index]; | |
$matched = $matched && $this->_doNormalOrStrictEqualityComparison($record[$specified_cols_name], $specified_cols_value); | |
} | |
if($matched){ | |
return ($return_cloned_record)? clone $record : $record; | |
} | |
} | |
//start from the middle + 1 to the last element | |
$start = (floor($num_records/2)) + 1; | |
for($curr_record_index = $start; $curr_record_index < $num_records; $curr_record_index++){ | |
$record = $this[$keys_2_dis_cllctn[$curr_record_index]]; | |
$matched = true; | |
for($curr_field_index=0; $curr_field_index < $num_fields && $matched; $curr_field_index++){ | |
$specified_cols_name = $cols[$curr_field_index]; | |
$specified_cols_value = $vals[$curr_field_index]; | |
$matched = $matched && $this->_doNormalOrStrictEqualityComparison($record[$specified_cols_name], $specified_cols_value); | |
} | |
if($matched){ | |
return ($return_cloned_record)? clone $record : $record; | |
} | |
} | |
} | |
} | |
} | |
return $result; | |
} | |
/** | |
* | |
* Fetch first record in a collection whose value(s) for the specified cols ($cols), | |
* match the specified value(s) ($vals). | |
* | |
* If count($cols) != count($vals) a null value will be returned by this method. | |
* | |
* @param mixed $cols (string | array) A list of column / field name(s) to be checked in each | |
* record. | |
* | |
* @param mixed $vals (int | double | string | array) A list of corresponding value(s) for the | |
* field(s) specified in $cols. | |
* | |
* @param boolean $return_cloned_record **False** if the first matching record in the collection | |
* should be directly returned (default behaviour), else **True** if a clone of the first matching | |
* record should be returned. | |
* | |
* | |
* @return mixed (Cfs_Model_Base_Record | null) First record in this collection whose value(s) | |
* for the specified cols ($cols), match the specified value(s) ($vals), or null if there is no | |
* matching record. | |
* | |
* @see Cfs_Model_Base_Collection::_findOneRecordByCols() | |
* | |
*/ | |
public function findRecordByCols($cols, $vals, $return_cloned_record=false) { | |
return $this->_findOneRecordByCols($cols, $vals, $return_cloned_record); | |
} | |
/** | |
* | |
* Fetch one or more record(s) from a collection whose value(s) match the value(s) of the | |
* column(s) and value(s) specified. | |
* | |
* If count( $cols ) != count( $vals ) a null value will be returned by this method. | |
* | |
* @param mixed $cols (string | array) An array of column/field name(s) or a single | |
* column/field name (string) to be checked in each record. | |
* | |
* @param mixed $vals (int | double | string | array) A list of corresponding value(s) or | |
* single value for the field(s) specified in $cols. | |
* | |
* @param array $comparision_operators_per_col An array of column_name=>php_comparison_operator | |
* pairs for the columns we are searching by (The php_comparison_operator should be a string | |
* containing one of the valid php comparison operators specified | |
* <a href="http://php.net/manual/en/language.operators.comparison.php">here</a>). | |
* | |
* @param boolean $return_cloned_records **False**, if the matching records in the collection | |
* should be directly returned (default behaviour) or **True** if a clone of each matching | |
* record should be returned. | |
* | |
* @param boolean $remove_matched_records_from_collection **True** if matched record(s) should | |
* be removed from the collection (**$this**) or **False** if not. | |
* | |
* | |
* @return mixed (Cfs_Model_Base_Collection | null) Collection of all matching record(s). Null | |
* is returned if there are no matching record(s). | |
* | |
* @see Cfs_Model_Base_Collection::_findRecordByCols() | |
* | |
*/ | |
public function findRecordsByCols($cols, $vals, $comparision_operators_per_col=array(), $return_cloned_records=false, $remove_matched_records_from_collection=false) { | |
return $this->_findRecordByCols($cols, $vals, true, $comparision_operators_per_col, $return_cloned_records, $remove_matched_records_from_collection); | |
} | |
/** | |
* | |
* Sorts this collection by one or more specified columns (including calculate_cols) in | |
* the Model associated with the records in this collection. | |
* | |
* **Usage:** | |
* | |
* - $this->orderByCols( 'column_to_sort_by', true ) | |
* | |
* **SQL Equivalent:** Select * from table **Order By column_to_sort_by ASC** | |
* | |
* <br /> | |
* - $this->orderByCols( array( '1st_column_to_sort_by', '2nd_column_to_sort_by' ), array( true, false ) ) | |
* | |
* **SQL Equivalent:** Select * from table **Order By 1st_column_to_sort_by ASC, 2nd_column_to_sort_by DESC** | |
* | |
* @param mixed $col_names (array | string) Specified column(s) we are sorting by. | |
* | |
* @param mixed $sort_asc_cols An array of booleans or a single boolean indicating whether or | |
* not the corresponding column(s) in $col_names should be sorted in ascending order. True to | |
* sort corresponding column in ascending order or false to sort in descending order. Default | |
* is sort ascending if the flag is not supplied for a column by the caller of this method. | |
* | |
* | |
* @return void | |
* | |
*/ | |
public function orderByCols($col_names, $sort_asc_cols=array(true)){ | |
$num_recs_in_dis_cllctn = count($this); | |
if($num_recs_in_dis_cllctn > 0){ | |
if(is_string($col_names)){ | |
$col_names = array($col_names); | |
}else if(!is_array($col_names)){ | |
throw new Exception('Invalid first parameter ['.print_r($col_names, true).'] supplied to '.__CLASS__.'::'.__FUNCTION__.' array of strings or single string excpected'); | |
} | |
if(is_bool($sort_asc_cols)){ | |
$sort_asc_cols = array($sort_asc_cols); | |
}else if(!is_array($sort_asc_cols)){ | |
throw new Exception('Invalid second parameter ['.print_r($sort_asc_cols, true).'] supplied to '.__CLASS__.'::'.__FUNCTION__.' array of booleans or a single boolean excpected'); | |
} | |
$num_cols = count($col_names); | |
$num_sort_flags = count($sort_asc_cols); | |
$sort_params = array(); | |
for($i=0; $i < $num_cols; $i++){ | |
if($i < $num_sort_flags){ | |
if((((boolean)$sort_asc_cols[$i]))){ | |
$sort_params[] = array('column'=>$col_names[$i]); | |
}else{ | |
$sort_params[] = array('column'=>$col_names[$i],'order_desc'=>true); | |
} | |
}else{ | |
//default to sort ASC | |
$sort_params[] = array('column'=>$col_names[$i]); | |
} | |
} | |
$array_to_sort = array(); | |
$keys_2_dis_cllctn = array_keys($this->_data); | |
for ($curr_record = 0; $curr_record < $num_recs_in_dis_cllctn; $curr_record++) { | |
$record = $this[$keys_2_dis_cllctn[$curr_record]]; | |
//I use a url as key since no valid database column can be named using the | |
//url scheme there by avoiding collision with a column name to sort by | |
$sortable_entry = array('http://Cfs_Model_Base_Record'=>$record); | |
for($i=0; $i < $num_cols; $i++){ | |
$sortable_entry[$col_names[$i]] = $record[$col_names[$i]]; | |
} | |
$array_to_sort[] = $sortable_entry; | |
} | |
//do the sort | |
Cfs_Lib_Array::orderAssocArrayByKeys($array_to_sort, $sort_params); | |
$sorted_array_of_records = array(); | |
$num_recs_in_collection = count($array_to_sort); | |
for($i=0; $i < $num_recs_in_collection; $i++){ | |
$sorted_array_of_records[] = $array_to_sort[$i]['http://Cfs_Model_Base_Record']; | |
} | |
//reorder this collection | |
$this->load($sorted_array_of_records); | |
} | |
} | |
/** | |
* | |
* Adds a new column to the collection or overwrites all existing values of an existing column | |
* with the value in the $default_val parameter if the $overwrite_existing_col parameter has a | |
* value of true. | |
* | |
* @param string $col_name Name of column to be added to the collection or existing column whose | |
* values are to be overwritten. | |
* | |
* @param mixed $default_val Default value of new column or value to overwite existing values in | |
* an already existent column. | |
* | |
* @param boolean $overwrite_existing_col Flag to allow overwriting the values of an existing | |
* column if the specified $col_name already exists. | |
* | |
* | |
* @return void | |
* | |
* @throws Exception This happens if you are trying to add a column that already exists and | |
* $overwrite_existing_col is false. | |
* | |
*/ | |
public function addColumn($col_name, $default_val='', $overwrite_existing_col=false){ | |
//Error: $col_name already exists in the collection | |
if(!$overwrite_existing_col | |
&& $this->getModel() instanceof Cfs_Model_Base | |
&& array_key_exists($col_name, $this->getModel()->getTableCols())){ | |
throw new Exception(__CLASS__."::".__FUNCTION__.": Column $col_name cannot be added to the collection, it already exists in the table ".$this->getModel()->getTableName()); | |
} | |
if(!empty($col_name)){ | |
for ($this->rewind(); $this->valid(); $this->next()) { | |
$record = $this->current(); | |
$record[$col_name] = $default_val; | |
} | |
} | |
} | |
/** | |
* | |
* Retrieves distinct values from a column in a collection. | |
* | |
* Similar to **Select Distinct ColumnName** in SQL. | |
* | |
* @param string $col_name Name of column in the collection from which distinct values are to | |
* be retreived. | |
* | |
* @param boolean $allow_empty_or_null_vals **True** if empty or null values should be returned | |
* in the result, else **False** to omit empty or null values in the result (default behaviour). | |
* 0, '0', false and array() are treated as non-empty values by this method. | |
* | |
* | |
* @return array An array of distinct values from the specified column in the collection. | |
* | |
*/ | |
public function distinct($col_name, $allow_empty_or_null_vals=false){ | |
$distinct_vals = array(); | |
for ($this->rewind(); $this->valid(); $this->next()) { | |
$record = $this->current(); | |
if(!in_array($record[$col_name], $distinct_vals)) { | |
if ( empty($record[$col_name]) && $allow_empty_or_null_vals ){ | |
$distinct_vals[] = $record[$col_name]; | |
}elseif( | |
!empty($record[$col_name]) | |
|| $record[$col_name] === false //treat false as non-empty | |
|| is_array($record[$col_name]) //treat the value: array() as non-empty | |
|| $record[$col_name] === 0 //treat 0 as non-empty | |
|| $record[$col_name] === '0' //treat '0' as non-empty | |
){ | |
$distinct_vals[] = $record[$col_name]; | |
} | |
} | |
} | |
sort($distinct_vals); | |
return $distinct_vals; | |
} | |
/** | |
* | |
* Retrieves values from a column in a collection. | |
* | |
* Similar to **Select ColumnName** in SQL. | |
* | |
* <br /> | |
* The keys to the array of values returned are the same as the keys the records | |
* containing the selected values have in the collection (**$this**). The returned values still | |
* maintain this key association even when $sort_vals == true. | |
* | |
* @param string $col_name Name of column in the collection from which values are to | |
* be retreived. If omitted, the primary key column is used. | |
* | |
* @param boolean $allow_empty_or_null_vals Flag to indicate whether or not empty or | |
* null values should be returned in the result (these values are omitted by default). | |
* 0, '0', false and array() are treated as non-empty values by this method. | |
* | |
* @param boolean $sort_vals Flag to indicate whether or not the returned values should | |
* be sorted (**true** to sort, **false** not to sort [default behaviour]). | |
* | |
* | |
* @return array An array of values from the specified column in the collection. | |
* | |
*/ | |
public function getAllPrimaryOrOtherColsVals($col_name=null, $allow_empty_or_null_vals=false, $sort_vals=false){ | |
// what key to look for? | |
$col_name = (empty($col_name))? $this->_model->primary_col : $col_name; | |
$results = array(); | |
for($this->rewind(); $this->valid(); $this->next()) { | |
$record = $this->current(); | |
$key = $this->key(); | |
if( (empty($record[$col_name]) ) && $allow_empty_or_null_vals ) { | |
$results[$key] = $record[$col_name]; | |
}elseif( | |
!empty($record[$col_name]) | |
|| $record[$col_name] === false //treat false as non-empty | |
|| is_array($record[$col_name]) //treat the value: array() as non-empty | |
|| $record[$col_name] === 0 //treat 0 as non-empty | |
|| $record[$col_name] === '0' //treat '0' as non-empty | |
){ | |
$results[$key] = $record[$col_name]; | |
} | |
} | |
if($sort_vals){ | |
asort($results); | |
} | |
return $results; | |
} | |
/** | |
* | |
* Calculates the average of all the values of the specified column in each record | |
* in this collection. | |
* Synonymous to **AVG( column_name )** in ANSII SQL. | |
* | |
* @param string $col_name The name of a column **(containing only numeric values)** in | |
* the table whose records are contained in this collection. | |
* | |
* | |
* @return mixed (float | int) The average of all the values of the specified column in | |
* each record in this collection. | |
* | |
*/ | |
public function averageColumnValue($col_name){ | |
return ($this->sumColumnValues($col_name) / $this->count()); | |
} | |
/** | |
* | |
* Finds the maximum numeric value in the specified column in all the records in this | |
* collection. | |
* Synonymous to **MAX( column_name )** in ANSII SQL. | |
* | |
* @param string $col_name The name of a column **(containing only numeric values)** | |
* in the table whose records are contained in this collection. | |
* | |
* | |
* @return mixed (float|int) The maximum numeric value in the specified column in all | |
* the records in this collection. | |
* | |
*/ | |
public function maxColumnValue($col_name){ | |
$keys = array_keys($this->_data); | |
$max = ($this->count() > 0) ? $this[$keys[0]][$col_name] : null; | |
for ($this->rewind(); $this->valid(); $this->next()) { | |
$record = $this->current(); | |
if(!empty($col_name)){ | |
if(is_numeric($record[$col_name]) && is_numeric($max) && $record[$col_name] > $max){ | |
$max = $record[$col_name]; | |
}else if(!is_numeric($max) && is_numeric($record[$col_name])){ | |
$max = $record[$col_name]; | |
} | |
} | |
} | |
return (!is_numeric($max))? $max : ($max * 1);//multiply by 1 to make it a truly numeric (float|int) data type rather than a string with a numeric value | |
} | |
/** | |
* | |
* Finds the minimum numeric value in the specified column in all the records in this | |
* collection. | |
* Synonymous to **MIN( column_name )** in ANSII SQL. | |
* | |
* @param string $col_name The name of a column **(containing only numeric values)** | |
* in the table whose records are contained in this collection. | |
* | |
* | |
* @return mixed (float|int) The minimum numeric value in the specified column in all | |
* the records in this collection. | |
* | |
*/ | |
public function minColumnValue($col_name){ | |
$keys = array_keys($this->_data); | |
$min = ($this->count() > 0) ? $this[$keys[0]][$col_name] : null; | |
for ($this->rewind(); $this->valid(); $this->next()) { | |
$record = $this->current(); | |
if(!empty($col_name)){ | |
if(is_numeric($record[$col_name]) && is_numeric($min) && $record[$col_name] < $min){ | |
$min = $record[$col_name]; | |
}else if(!is_numeric($min) && is_numeric($record[$col_name]) ){ | |
$min = $record[$col_name]; | |
} | |
} | |
} | |
return (!is_numeric($min))? $min : ($min * 1);//multiply by 1 to make it a truly numeric (float|int) data type rather than a string with a numeric value | |
} | |
/** | |
* | |
* Calculates the sum of all the values of the specified column in each record in this | |
* collection. | |
* Synonymous to **SUM( column_name )** in ANSII SQL. | |
* | |
* @param string $col_name The name of a column **(containing only numeric values)** in | |
* the table whose records are contained in this collection. | |
* | |
* | |
* @return mixed (float|int) The sum of all the values of the specified column in each | |
* record in this collection. | |
* | |
*/ | |
public function sumColumnValues($col_name){ | |
$sum = 0; | |
for ($this->rewind(); $this->valid(); $this->next()) { | |
$record = $this->current(); | |
if(!empty($col_name)){ | |
if(is_numeric($record[$col_name])){ | |
$sum += $record[$col_name]; | |
} | |
} | |
} | |
return $sum; | |
} | |
/** | |
* | |
* Divides a collection of records into an array of smaller collections based on the | |
* supplied list of columns ($col_names) to group by. | |
* | |
* **NOTE:** The collection is first grouped by the first column in $col_names, then | |
* each of the groups obtained via grouping by the first column in $col_names are then | |
* further grouped by the second column in $col_names... (this process continues until | |
* all the entries in $col_names have been used for grouping). | |
* | |
* <br /> | |
* **NOTE:** This method does the same thing as a basic SQL **GROUP BY**, but returns | |
* all the records for each group instead of one record per group (in the case of SQL's | |
* **GROUP BY**). | |
* | |
* <br /> | |
* **NOTE:** Records with a **NULL** or **empty string** value for any of the columns being | |
* grouped by, are grouped together. As oppossed to having one group for records with one or | |
* more group by column(s) having a **NULL** value and another group for records with one or | |
* group by column(s) having an **empty string** value. | |
* | |
* <br /> | |
* **Usage:** | |
* | |
* <pre> | |
* $this->groupByReturnAllRecordsPerGroup ( | |
* array('first_col_to_group_by', 'second_col_to_group_by'), | |
* array('col_1_name'=>'summed_col_1_value'), | |
* array('col_1_name'=>'min_col_1_value'), | |
* array('col_1_name'=>'max_col_1_value'), | |
* array('col_1_name'=>'avg_col_1_value') | |
* ); | |
* </pre> | |
* | |
* Similar to | |
* | |
* <pre> | |
* SELECT table.*, | |
* SUM(col_1_name) AS summed_col_1_value, | |
* MIN(col_1_name) AS min_col_1_value, | |
* MAX(col_1_name) AS max_col_1_value, | |
* AVG(col_1_name) AS avg_col_1_value | |
* FROM table | |
* GROUP BY first_col_to_group_by, second_col_to_group_by | |
* </pre> | |
* | |
* in SQL (but all the records in each group are returned as opposed to the first | |
* record in each group). | |
* | |
* @param array $col_names An array of column names (calculate cols and other special | |
* cols can be included) that we want to group by. | |
* | |
* @param array $sum_cols Associative array. Array keys are the column name(s) whose | |
* values are to be summed in each group of the final result and the corresponding | |
* values are the desired alias column name(s) for the summed value(s). | |
* Example **array('col_1_name'=>'summed_col_1_value')** equivalent to | |
* **SUM(col_1_name) AS summed_col_1_value** in SQL. | |
* | |
* @param array $min_cols Associative array. Array keys are the column name(s) whose | |
* min value(s) are to be found in each group of the final result and the corresponding | |
* values are the desired alias column name(s) for the min value(s). | |
* Example **array('col_1_name'=>'min_col_1_value')** equivalent to | |
* **MIN(col_1_name) AS min_col_1_value** in SQL. | |
* | |
* @param array $max_cols Associative array. Array keys are the column name(s) whose | |
* max value(s) are to be found in each group of the final result and the corresponding | |
* values are the desired alias column name(s) for the max value(s). | |
* Example **array('col_1_name'=>'max_col_1_value')** equivalent to | |
* **MAX(col_1_name) AS max_col_1_value** in SQL. | |
* | |
* @param array $avg_cols Associative array. Array keys are the column name(s) whose | |
* average value(s) are to be found in each group of the final result and the corresponding | |
* values are the desired alias column name(s) for the averaged value(s). | |
* Example **array('col_1_name'=>'avg_col_1_value')** equivalent to | |
* **AVG(col_1_name) AS avg_col_1_value** in SQL. | |
* | |
* | |
* @return array An array of collections (Cfs_Model_Base_Collection). Each collection | |
* contains records that have the same distinct values for the columns in $col_names. | |
* | |
*/ | |
public function groupByReturnAllRecordsPerGroup($col_names, | |
$sum_cols=array(), | |
$min_cols=array(), | |
$max_cols=array(), | |
$avg_cols=array()) { | |
$groups = array(); | |
$col_names = (array)$col_names; | |
//get the column we are going to group by | |
$col_name = array_shift($col_names); | |
$grouping_array = array();//holds an array of arrays where the | |
//each array element's key is a distinct | |
//value of the grouping column $col_name | |
//loop thru all the records in this collection | |
//and group them by $col_name | |
for ($this->rewind(); $this->valid(); $this->next()) { | |
$record = $this->current(); | |
if(!array_key_exists($record[$col_name], $grouping_array)){ | |
$grouping_array[$record[$col_name]] = array(); | |
} | |
//group | |
$grouping_array[$record[$col_name]][] = $record; | |
} | |
ksort($grouping_array); | |
$groups_4_this_iteration = array();//holds each group in collection form | |
reset($grouping_array); | |
while(list(, $items_in_group) = each($grouping_array)){ | |
//convert array of records in the current group | |
//to a collection of records | |
$current_group_4_key = Solar::factory(get_class($this)); | |
$current_group_4_key->load($items_in_group); | |
$groups_4_this_iteration[] = $current_group_4_key; | |
} | |
if(count($col_names) > 0){ | |
//we still have more column names to further group by | |
reset($groups_4_this_iteration); | |
while(list(, $group_4_this_iteration) = each($groups_4_this_iteration)){ | |
$returned_groups_4_current_group = | |
$group_4_this_iteration | |
->groupByReturnAllRecordsPerGroup($col_names, | |
$sum_cols, | |
$min_cols, | |
$max_cols, | |
$avg_cols); | |
reset($returned_groups_4_current_group); | |
while(list(, $sub_group) = each($returned_groups_4_current_group)){ | |
//store the sub-groups | |
$groups[] = $sub_group; | |
} | |
} | |
}else{ | |
//we have reached the last col_name in the list of | |
//column names initially passed to this method | |
//caluculate the SUM | |
self::_executeAggregateSqlFunctionForGroupBy('sumColumnValues', | |
$sum_cols, | |
$groups_4_this_iteration); | |
//caluculate the MIN | |
self::_executeAggregateSqlFunctionForGroupBy('minColumnValue', | |
$min_cols, | |
$groups_4_this_iteration); | |
//caluculate the MAX | |
self::_executeAggregateSqlFunctionForGroupBy('maxColumnValue', | |
$max_cols, | |
$groups_4_this_iteration); | |
//caluculate the AVG COLS | |
self::_executeAggregateSqlFunctionForGroupBy('averageColumnValue', | |
$avg_cols, | |
$groups_4_this_iteration); | |
return $groups_4_this_iteration; | |
} | |
//finally return merged sub-groups | |
return $groups; | |
} | |
/** | |
* | |
* Divides a collection of records into smaller collections based on the supplied | |
* list of columns ($col_names) to group by and then returns a collection containing | |
* the first record in each group. | |
* | |
* **NOTE:** The collection is first grouped by the first column in $col_names, | |
* then each of the groups obtained via grouping by the first column in $col_names | |
* are then further grouped by the second column in $col_names... (this process | |
* continues until all the entries in $col_names have been used for grouping). | |
* | |
* <br /> | |
* **NOTE:** This method does the same thing as a basic SQL **GROUP BY**. | |
* | |
* <br /> | |
* **NOTE:** Records with a **NULL** or **empty string** value for any of the columns being | |
* grouped by, are grouped together. As oppossed to having one group for records with one or | |
* more group by column(s) having a **NULL** value and another group for records with one or | |
* group by column(s) having an **empty string** value. | |
* | |
* <br /> | |
* **Usage:** | |
* | |
* <pre> | |
* $this->groupByReturnOneRecordPerGroup ( | |
* array('first_col_to_group_by', 'second_col_to_group_by'), | |
* array('col_1_name'=>'summed_col_1_value'), | |
* array('col_1_name'=>'min_col_1_value'), | |
* array('col_1_name'=>'max_col_1_value'), | |
* array('col_1_name'=>'avg_col_1_value') | |
* ); | |
* </pre> | |
* | |
* Identical to | |
* | |
* <pre> | |
* SELECT table.*, | |
* SUM(col_1_name) AS summed_col_1_value, | |
* MIN(col_1_name) AS min_col_1_value, | |
* MAX(col_1_name) AS max_col_1_value, | |
* AVG(col_1_name) AS avg_col_1_value | |
* FROM table | |
* GROUP BY first_col_to_group_by, second_col_to_group_by | |
* </pre> | |
* | |
* in SQL. | |
* | |
* @param array $col_names An array of column names (calculate cols and other special | |
* cols can be included) that we want to group by. | |
* | |
* @param array $sum_cols Associative array. Array keys are the column name(s) whose | |
* values are to be summed in each group of the final result and the corresponding | |
* values are desired alias column name(s) for the summed value(s). | |
* Example **array('col_1_name'=>'summed_col_1_value')** equivalent to | |
* **SUM(col_1_name) AS summed_col_1_value** in SQL. | |
* | |
* @param array $min_cols Associative array. Array keys are the column name(s) whose | |
* min value(s) are to be found in each group of the final result and the corresponding | |
* values are desired alias column name(s) for the min value(s). | |
* Example **array('col_1_name'=>'min_col_1_value')** equivalent to | |
* **MIN(col_1_name) AS min_col_1_value** in SQL. | |
* | |
* @param array $max_cols Associative array. Array keys are the column name(s) whose | |
* max value(s) are to be found in each group of the final result and the corresponding | |
* values are desired alias column name(s) for the max value(s). | |
* Example **array('col_1_name'=>'max_col_1_value')** equivalent to | |
* **MAX(col_1_name) AS max_col_1_value** in SQL. | |
* | |
* @param array $avg_cols Associative array. Array keys are the column name(s) whose | |
* average value(s) are to be found in each group of the final result and the corresponding | |
* values are desired alias column name(s) for the averaged value(s). | |
* Example **array('col_1_name'=>'avg_col_1_value')** equivalent to | |
* **AVG(col_1_name) AS avg_col_1_value** in SQL. | |
* | |
* | |
* @return Cfs_Model_Base_Collection A collection of records with unique values for all the | |
* columns in $col_names. Same as running an SQL GROUP BY on the original | |
* collection ($this). | |
* | |
*/ | |
public function groupByReturnOneRecordPerGroup($col_names, | |
$sum_cols=array(), | |
$min_cols=array(), | |
$max_cols=array(), | |
$avg_cols=array()) { | |
$groups = $this->groupByReturnAllRecordsPerGroup($col_names, $sum_cols, $min_cols, $max_cols, $avg_cols); | |
$first_record_per_group = array(); | |
reset($groups); | |
while (list(, $group) = each($groups)) { | |
$first_record_per_group[] = $group->firstItem(); | |
} | |
$collection = Solar::factory(get_class($this)); | |
$collection->load($first_record_per_group); | |
return $collection; | |
} | |
/** | |
* | |
* Performs a comparison between the two specified operands using the specified | |
* comparison operator. | |
* | |
* @param string $operator A comparison operator. The specified comparison operator | |
* should be a string containing one of the valid php comparison operators specified | |
* <a href="http://php.net/manual/en/language.operators.comparison.php">here</a>. | |
* | |
* @param mixed $operand1s_value The value to be compared with the value of $operand2s_value | |
* | |
* @param mixed $operand2s_value The value to be compared with the value of $operand1s_value | |
* | |
* | |
* @return boolean True or false based on the specified comparison operator. For example, | |
* true will be returned if the operator is '==' and the operands are equivalent in value. | |
* | |
*/ | |
private function _doComparison($operator, $operand1s_value, $operand2s_value){ | |
$result = false; | |
$operator = $this->_extractComparisonOperatorFromStr($operator); | |
if(!empty ($operator)){ | |
switch ($operator) { | |
case '==': | |
$result = ($operand1s_value == $operand2s_value); | |
break; | |
case '===': | |
$result = ($operand1s_value === $operand2s_value); | |
break; | |
case '!=': | |
$result = ($operand1s_value != $operand2s_value); | |
break; | |
case '<>': | |
$result = ($operand1s_value <> $operand2s_value); | |
break; | |
case '!==': | |
$result = ($operand1s_value !== $operand2s_value); | |
break; | |
case '<': | |
$result = ($operand1s_value < $operand2s_value); | |
break; | |
case '>': | |
$result = ($operand1s_value > $operand2s_value); | |
break; | |
case '<=': | |
$result = ($operand1s_value <= $operand2s_value); | |
break; | |
case '>=': | |
$result = ($operand1s_value >= $operand2s_value); | |
break; | |
default: | |
//do nothing | |
} | |
} | |
return $result; | |
} | |
/** | |
* | |
* Tries to extract a valid comparison operator from the specified string. | |
* | |
* @param string $potential_operator A string from which a valid comparison operator | |
* is to be extracted. | |
* See <a href="http://php.net/manual/en/language.operators.comparison.php">here</a> | |
* for list of valid comparison operators. | |
* | |
* | |
* @return string The extracted valid math operator. E.g. <=, !=, e.t.c. | |
* An empty string is returned if there is no valid math operator in $potential_operator. | |
* | |
*/ | |
private function _extractComparisonOperatorFromStr($potential_operator){ | |
$operator = ''; | |
$php_operator_parts = array('<','>', '=', '!'); | |
if(!empty($potential_operator) && is_string($potential_operator)){ | |
$len = strlen($potential_operator); | |
for($i=0; $i<$len; $i++){ | |
$char = $potential_operator[$i]; | |
if(in_array($char, $php_operator_parts)){ | |
$operator .= $char; | |
} | |
} | |
if(!in_array($operator, self::$_php_operators)){ | |
//the extracted operator is not a standard php comparison operator | |
//http://ca.php.net/manual/en/language.operators.comparison.php | |
$operator = ''; | |
} | |
} | |
return $operator; | |
} | |
/** | |
* | |
* A helper method used by Cfs_Model_Base_Collection::groupByReturnAllRecordsPerGroup() to call | |
* Cfs_Model_Base_Collection::sumColumnValues(), Cfs_Model_Base_Collection::minColumnValue(), | |
* Cfs_Model_Base_Collection::maxColumnValue() or Cfs_Model_Base_Collection::averageColumnValue(). | |
* | |
* @param string $function_alias_name Name of function to be executed ( sumColumnValues(), | |
* minColumnValue(), maxColumnValue(), averageColumnValue() ). | |
* | |
* @param array $columns_2_b_operated_on An array of column names (each of which is a | |
* parameter to be passed to the function represented by $function_alias_name). If an | |
* empty array is supplied this method will do nothing. | |
* | |
* @param array $array_of_collections An array of collections (Cfs_Model_Base_Collection). | |
* | |
* | |
* @return void | |
* | |
*/ | |
private static function _executeAggregateSqlFunctionForGroupBy( $function_alias_name, | |
$columns_2_b_operated_on, | |
&$array_of_collections){ | |
if(!empty($columns_2_b_operated_on)){ | |
$columns_2_b_operated_on = ((array)$columns_2_b_operated_on); | |
reset($columns_2_b_operated_on); | |
while (list($key) = each($columns_2_b_operated_on)) { | |
$results_col_name = $columns_2_b_operated_on[$key]; | |
//assume that the name of the column to be operated on | |
//is the current key for the current array element | |
$col_name_to_be_operated_on = $key; | |
if(is_numeric($key)){ | |
//The key is numeric and as a result cannot be the | |
//name of the column whose values are to be summed | |
$col_name_to_be_operated_on = $results_col_name; | |
} | |
reset($array_of_collections); | |
while (list($collection_key) = each($array_of_collections)) { | |
$result = $array_of_collections[$collection_key]->$function_alias_name($col_name_to_be_operated_on); | |
$array_of_collections[$collection_key]->addColumn($results_col_name, $result, true); | |
} | |
} | |
} | |
} | |
/** | |
* | |
* Retrieves and returns the first record in this collection. | |
* | |
* @return Cfs_Model_Base_Record The first record in this collection or null if | |
* collection is empty. | |
* | |
*/ | |
public function firstItem(){ | |
$keys = array_keys($this->_data); | |
return ($this->count() > 0) ? $this[$keys[0]] : null; | |
} | |
/** | |
* | |
* Retrieves and returns the last record in this collection. | |
* | |
* @return Cfs_Model_Base_Record The last record in this collection or null if | |
* collection is empty. | |
* | |
*/ | |
public function lastItem(){ | |
$keys = array_keys($this->_data); | |
$keys = array_reverse($keys); | |
return ($this->count() > 0) ? $this[$keys[0]] : null; | |
} | |
/** | |
* | |
* Left joins two collections (Cfs_Model_Base_Collection). | |
* | |
* If you are strictly using the Solar_Sql_Model for data access in your | |
* application, this method will allow you to do a left join between two | |
* different models (for this version of Solar (1.0.0alpha2), there is no | |
* left join implementation in the Solar_Sql_Model package. Inner joins | |
* are already taken care of by relationships in this version of Solar). | |
* Simply fetch collections of all the records you are interested in from | |
* both models and use this method to left join the two collections. All | |
* the records in the left hand side collection (i.e. the collection with | |
* which you are using to call this method) are always returned just like | |
* in SQL's Left Join where all records in the left table are always returned. | |
* | |
* <br /> | |
* **$this** is the left hand side collection and **$other_collection** is | |
* the right hand side collection. | |
* | |
* <br /> | |
* **Usage 1:** | |
* | |
* <pre> | |
* $this->leftJoin ( | |
* $other_collection, | |
* array('column_1', 'column_2'), | |
* array('column_3', 'column_4'), | |
* array('column_3'=>'NONE', 'column_4'=>'NONE'), | |
* array('column_x'=>array('foreign_key_to_tlefts_column_x'=>'==')) | |
* ); | |
* </pre> | |
* | |
* Assuming **$this** maps to a database table called **tleft** and | |
* **$other_collection** maps to another database table called **tright**. | |
* The SQL equivalent of this method would be: | |
* | |
* <pre> | |
* SELECT tleft.column_1, tleft.column_2, tright.column_3, tright.column_4 | |
* FROM tleft | |
* LEFT JOIN tright | |
* ON tleft.column_x = tright.foreign_key_to_tlefts_column_x | |
* </pre> | |
* | |
* **Usage 2:** | |
* | |
* <pre> | |
* $this->leftJoin ( | |
* $other_collection, | |
* array('column_1'=>'alias_1', 'column_2'=>'alias_2'), | |
* array('column_3'=>'alias_3', 'column_4'=>'alias_4'), | |
* array(), | |
* array('column_x'=>array('foreign_key_to_tlefts_column_x'=>'==')) | |
* ); | |
* </pre> | |
* | |
* Assuming **$this** maps to a database table called **tleft** and | |
* **$other_collection** maps to another database table called **tright**. | |
* The SQL equivalent of this method would be: | |
* | |
* <pre> | |
* SELECT tleft.column_1 AS alias_1, tleft.column_2 AS alias_2, | |
* tright.column_3 AS alias_3, tright.column_4 AS alias_4 | |
* FROM tleft | |
* LEFT JOIN tright | |
* ON tleft.column_x = tright.foreign_key_to_tlefts_column_x | |
* </pre> | |
* | |
* **NOTE:** You can supply default values for the columns you want in the | |
* result from **$other_collection** (the right hand side collection) via | |
* **$return_default_values_4_right_collection_columns**. These values will | |
* be used to populate the **$other_collection**'s columns in records in the | |
* result that do not have a matching right hand side collection record. This | |
* can be seen in the first usage example above where **'column_3'** and | |
* **'column_4'** from **$other_collection** records are assigned a default | |
* value of 'NONE' in the result where no record in **$other_collection** | |
* matches a record in **this** (the left hand side collection). | |
* Usually in SQL such values will be set to NULL, this method will set these columns | |
* to an empty string value if **$return_default_values_4_right_collection_columns** | |
* is an empty array (as can be seen in the second usage example above). | |
* | |
* @param Cfs_Model_Base_Collection $other_collection the right hand side collection to be joined to $this. | |
* | |
* @param array $return_column_names_4_left_collection Names of the columns in the | |
* records in **$this** that should be returned in the result of the left join. You | |
* can use this format: **array('column_name_1'=>'column_name_1_alias', ...)** | |
* (the **column_name_#**'s values will be accessed via **column_name_#_alias** | |
* in each record of the result. **column_name_#** is just a placeholder for a | |
* column name). | |
* **array('column_name_1',...)** is also a valid format (the **column_name_#**'s | |
* values will be accessed via **column_name_#** in each record of the result. | |
* **column_name_#** is also just a placeholder for a column name). | |
* | |
* @param array $return_column_names_4_right_collection Names of the columns in the | |
* records in **$other_collection** that should be returned in the result of the left | |
* join. You can use this format: **array('column_name_1'=>'column_name_1_alias', ...)** | |
* (the **column_name_#**'s values will be accessed via **column_name_#_alias** in each | |
* record of the result. **column_name_#** is just a placeholder for a column name). | |
* **array('column_name_1',...)** is also a valid format (the **column_name_#**'s | |
* values will be accessed via **column_name_#** in each record of the result. | |
* **column_name_#** is also just a placeholder for a column name). | |
* | |
* @param array $return_default_values_4_right_collection_columns An array of default values | |
* for **$other_collection**'s columns in the left join result. These value(s) will be applied | |
* to record(s) in the result where a record in **$this** (the left hand hand side collection) | |
* does not have a matching record in **$other_collection** (the right hand side collection). | |
* The key(s) in this array are the column names in **$other_collection** and the corresponding | |
* value(s) are the default value(s) for the respective columns. | |
* | |
* @param array $join_cols_map_with_comparison_operators An array of the left join criteria. It | |
* has the following structure: | |
* <br/> | |
* <code> | |
* array( | |
* <br/> | |
* 'join_col_1_in_left' => array( 'join_col_1_in_right' => 'comparison_operator_1' ), | |
* <br/> | |
* ......., | |
* <br/> | |
* ......., | |
* <br/> | |
* 'join_col_N_in_left' => array( 'join_col_N_in_right' => 'comparison_operator_N' ), | |
* <br/> | |
* ); | |
* </code> | |
* <br/> | |
* The key ( e.g. 'join_col_1_in_left',....,'join_col_N_in_left' ) to each element in this array | |
* is the name of a column in the left collection that will be part of the comparison check for | |
* the join. Each element in this array is an array containing only one element whose key ( e.g. | |
* 'join_col_1_in_right', .... ,'join_col_N_in_right' ) is the name of a column in the right | |
* collection that will be compared to the corresponding specified column in the left collection | |
* mentioned above. The value of this single element sub-array is a string containing a comparison | |
* operator (e.g. '<=' ) specified | |
* <a href="http://php.net/manual/en/language.operators.comparison.php">here</a>. | |
* | |
* @return Cfs_Model_Base_Collection A collection of records (the result of the left join operation). | |
* Uses the same logic / algorithm as the SQL LEFT JOIN operation. An empty collection will be returned | |
* if one or more of $this, $other_collection, $return_column_names_4_left_collection, | |
* $return_column_names_4_right_collection or $join_cols_map_with_comparison_operators | |
* is empty. | |
* | |
*/ | |
public function leftJoin(Cfs_Model_Base_Collection $other_collection, | |
array $return_column_names_4_left_collection, | |
array $return_column_names_4_right_collection, | |
array $return_default_values_4_right_collection_columns, | |
array $join_cols_map_with_comparison_operators) { | |
$records_to_be_returned = Solar::factory(get_class($this)); | |
$curr_key_in_collctn_2_b_returned = 0; | |
if($this->count() > 0 | |
&& $other_collection->count() > 0 | |
&& count($return_column_names_4_left_collection) > 0 | |
&& count($return_column_names_4_right_collection) > 0 | |
&& count($join_cols_map_with_comparison_operators) > 0){ | |
//generate params to be passed to findRecordsByCols | |
//for the $other_collection | |
$find_cols = array(); | |
$operators = array(); | |
reset($join_cols_map_with_comparison_operators); | |
while(list($current_left_collection_join_col) = each($join_cols_map_with_comparison_operators)){ | |
list($right_col_4_filtering, $operator) = | |
each($join_cols_map_with_comparison_operators[$current_left_collection_join_col]); | |
$find_cols[] = $right_col_4_filtering; | |
$operators[$right_col_4_filtering] = $operator; | |
} | |
$current_left_collection_join_col ='';//reset | |
$all_left_collections_join_cols = array_keys($join_cols_map_with_comparison_operators); | |
for($this->rewind(); $this->valid(); $this->next()) { | |
$left_collections_record = $this->current(); | |
//generate params to be passed to findRecordsByCols | |
//for the $other_collection | |
$find_vals = array(); | |
reset($all_left_collections_join_cols); | |
while(list($key) = each($all_left_collections_join_cols)){ | |
$current_left_collection_join_col = $all_left_collections_join_cols[$key]; | |
$find_vals[] = $left_collections_record[$current_left_collection_join_col]; | |
} | |
//get the matching right records | |
$matching_right_collection_records = | |
$other_collection | |
->findRecordsByCols($find_cols, $find_vals, $operators); | |
//since this a left join, we always need to return a record | |
//containing the details of the current left collection's | |
//record whether or not we find any matching right collection | |
//record(s) | |
//create a new record | |
$db_rec = Solar::factory('Solar_Struct'); | |
//fill record to be returned with values from | |
//the current left collection's record | |
reset($return_column_names_4_left_collection); | |
while(list($potential_left_col_name) = each($return_column_names_4_left_collection)){ | |
$left_col_name_alias = $return_column_names_4_left_collection[$potential_left_col_name]; | |
//user just supplied an array of column names and not a | |
//key value pair array of column_name=>column_name_alias | |
if(is_numeric($potential_left_col_name)){ | |
$potential_left_col_name = $left_col_name_alias; | |
} | |
$db_rec[$left_col_name_alias] = $left_collections_record[$potential_left_col_name]; | |
} | |
if($matching_right_collection_records != null){ | |
$keys_2_matching_right_collection = array_keys($matching_right_collection_records->getRefToInternalArray()); | |
$num_matching_records = count($keys_2_matching_right_collection); | |
for($curr_index_2_key_map=0; $curr_index_2_key_map < $num_matching_records; $curr_index_2_key_map++){ | |
$right_collections_record = | |
$matching_right_collection_records[$keys_2_matching_right_collection[$curr_index_2_key_map]]; | |
$current_record_2_b_returned = clone $db_rec; | |
//fill record to be returned with the approriate | |
//matching right collection record's values | |
reset($return_column_names_4_right_collection); | |
while(list($potential_right_col_name) = each($return_column_names_4_right_collection)){ | |
$right_col_name_alias = $return_column_names_4_right_collection[$potential_right_col_name]; | |
if(is_numeric($potential_right_col_name)){ | |
//user just supplied an array of column names and not a | |
//key value pair array of column_name=>column_name_alias | |
$potential_right_col_name = $right_col_name_alias; | |
} | |
$current_record_2_b_returned[$right_col_name_alias] = $right_collections_record[$potential_right_col_name]; | |
} | |
$records_to_be_returned[$curr_key_in_collctn_2_b_returned++] = $current_record_2_b_returned; | |
} | |
}else{ | |
$current_record_2_b_returned = clone $db_rec; | |
//fill record to be returned with default / empty values | |
//for the right collections return columns since there | |
//are no matching right collection records | |
reset($return_column_names_4_right_collection); | |
while(list($potential_right_col_name) = each($return_column_names_4_right_collection)){ | |
$right_col_name_alias = $return_column_names_4_right_collection[$potential_right_col_name]; | |
if(is_numeric($potential_right_col_name)){ | |
//user just supplied an array of column names and not a | |
//key value pair array of column_name=>column_name_alias | |
$potential_right_col_name = $right_col_name_alias; | |
} | |
if(array_key_exists($potential_right_col_name, $return_default_values_4_right_collection_columns)){ | |
//default value was specified for the current right column, so use it | |
$current_record_2_b_returned[$right_col_name_alias] = $return_default_values_4_right_collection_columns[$potential_right_col_name]; | |
}else{ | |
//no default value was specified for the current right column, | |
//so set it to an empty string | |
$current_record_2_b_returned[$right_col_name_alias] = ''; | |
} | |
} | |
$records_to_be_returned[$curr_key_in_collctn_2_b_returned++] = $current_record_2_b_returned; | |
} | |
} | |
} | |
return $records_to_be_returned; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment