| [ Index ] |
PHP Cross Reference of Akelos Framework |
[Summary view] [Print] [Text view]
1 <?php 2 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ 3 4 // +----------------------------------------------------------------------+ 5 // | Akelos Framework - http://www.akelos.org | 6 // +----------------------------------------------------------------------+ 7 // | Copyright (c) 2002-2006, Akelos Media, S.L. & Bermi Ferrer Martinez | 8 // | Released under the GNU Lesser General Public License, see LICENSE.txt| 9 // +----------------------------------------------------------------------+ 10 11 /** 12 * @package ActiveRecord 13 * @subpackage Associations 14 * @author Bermi Ferrer <bermi a.t akelos c.om> 15 * @copyright Copyright (c) 2002-2006, Akelos Media, S.L. http://www.akelos.org 16 * @license GNU Lesser General Public License <http://www.gnu.org/copyleft/lesser.html> 17 */ 18 19 require_once (AK_LIB_DIR.DS.'AkActiveRecord'.DS.'AkAssociation.php'); 20 21 /** 22 * Adds the following methods for retrieval and query of a single associated object. 23 * $association is replaced with the symbol passed as the first argument, so 24 * <tt>hasOne('manager')</tt> would add among others <tt>$this->manager->getAttributes()</tt>. 25 * 26 * Example: An Account class declares <tt>hasOne('beneficiary');</tt>, which will add: 27 * * <tt>$Account->beneficiary->load()</tt> (similar to <tt>$Beneficiary->find('first', array('conditions' => "account_id = $id"))</tt>) 28 * * <tt>$Account->beneficiary->assign($Beneficiary);</tt> (similar to <tt>$Beneficiary->account_id = $Account->id; $Beneficiary->save()</tt>) 29 * * <tt>$Account->beneficiary->build();</tt> (similar to <tt>$Beneficiary = new Beneficiary("account_id->", $Account->id)</tt>) 30 * * <tt>$Account->beneficiary->create();</tt> (similar to <tt>$b = new Beneficiary("account_id->", $Account->id); $b->save(); $b</tt>) 31 * 32 * The declaration can also include an options array to specialize the behavior of the association. 33 * 34 * Options are: 35 * * <tt>class_name</tt> - specify the class name of the association. Use it only if that name can't be inferred 36 * from the association name. So <tt>hasOne('manager')</tt> will by default be linked to the "Manager" class, but 37 * if the real class name is "Person", you'll have to specify it with this option. 38 * * <tt>conditions</tt> - specify the conditions that the associated object must meet in order to be included as a "WHERE" 39 * sql fragment, such as "rank = 5". 40 * * <tt>order</tt> - specify the order from which the associated object will be picked at the top. Specified as 41 * an "ORDER BY" sql fragment, such as "last_name, first_name DESC" 42 * * <tt>dependent</tt> - if set to true, the associated object is destroyed when this object is. It's also destroyed if another 43 * association is assigned. 44 * * <tt>foreign_key</tt> - specify the foreign key used for the association. By default this is guessed to be the name 45 * of this class in lower-case and "_id" suffixed. So a "Person" class that makes a hasOne association will use "person_id" 46 * as the default foreign_key. 47 * 48 * Option examples: 49 * var $hasOne = array( 50 * 'credit_card' => array('dependent' => true), 51 * 'last_comment' => array('class_name' => "Comment", 'order' => "posted_on"), 52 * 'project_manager' => array('class_name' => "Person", 'conditions' => "role = 'project_manager'") 53 * ); 54 */ 55 class AkHasOne extends AkAssociation 56 { 57 var $associated_ids = array(); 58 59 function &addAssociated($association_id, $options = array()) 60 { 61 $default_options = array( 62 'class_name' => empty($options['class_name']) ? AkInflector::camelize($association_id) : $options['class_name'], 63 'foreign_key' => empty($options['foreign_key']) ? AkInflector::singularize($this->Owner->getTableName()).'_id' : $options['foreign_key'], 64 'remote'=>false, 65 'instantiate'=>false, 66 'conditions'=>false, 67 'include_conditions_when_included'=>true, 68 'order'=>false, 69 'include_order_when_included'=>true, 70 'dependent'=>false, 71 'counter_cache'=>false 72 ); 73 74 $options = array_merge($default_options, $options); 75 76 $options['table_name'] = empty($options['table_name']) ? AkInflector::tableize($options['class_name']) : $options['table_name']; 77 78 $this->setOptions($association_id, $options); 79 80 $this->addModel($association_id, new AkAssociatedActiveRecord()); 81 82 $associated =& $this->getModel($association_id); 83 $this->setAssociatedId($association_id, $associated->getId()); 84 85 $associated =& $this->_build($association_id, &$associated, false); 86 87 $this->_saveLoadedHandler($association_id, $associated); 88 89 if($options['instantiate']){ 90 $associated =& $this->addModel($association_id, new $options['class_name']($options['foreign_key'].' = '.$this->Owner->quotedId())); 91 } 92 93 return $associated; 94 } 95 96 97 /** 98 * Assigns the associate object, extracts the primary key, sets it as the foreign key, and saves the associate object. 99 */ 100 function &assign($association_id, &$Associated) 101 { 102 if(!$this->Owner->isNewRecord()){ 103 $Associated->set($this->Owner->$association_id->getAssociationOption('foreign_key'), $this->Owner->getId()); 104 $Associated->save(); 105 } 106 107 $this->_build($association_id, &$Associated); 108 $this->Owner->$association_id->_loaded = true; 109 return $Associated; 110 } 111 112 function getAssociatedId($association_id) 113 { 114 return isset($this->associated_ids[$association_id]) ? $this->associated_ids[$association_id] : false; 115 } 116 117 118 function getType() 119 { 120 return 'hasOne'; 121 } 122 123 124 function getAssociatedFinderSqlOptions($association_id, $options = array()) 125 { 126 $default_options = array( 127 'conditions' => $this->Owner->$association_id->getAssociationOption('include_conditions_when_included'), 128 'order' => $this->Owner->$association_id->getAssociationOption('include_order_when_included') 129 ); 130 131 if(empty($this->Owner->$association_id->__activeRecordObject)){ 132 $this->build($association_id, array(), false); 133 } 134 135 $table_name = $this->Owner->$association_id->getTableName(); 136 $options = array_merge($default_options, $options); 137 138 $finder_options = array(); 139 140 foreach ($options as $option=>$available) { 141 if($available){ 142 $value = $this->Owner->$association_id->getAssociationOption($option); 143 empty($value) ? null : ($finder_options[$option] = trim($this->Owner->$association_id->_addTableAliasesToAssociatedSql('_'.$association_id, $value))); 144 } 145 } 146 147 $finder_options['joins'] = $this->Owner->$association_id->constructSqlForInclusion(); 148 149 $finder_options['selection'] = ''; 150 foreach (array_keys($this->Owner->$association_id->getColumns()) as $column_name){ 151 $finder_options['selection'] .= '_'.$association_id.'.'.$column_name.' AS _'.$association_id.'_'.$column_name.', '; 152 } 153 $finder_options['selection'] = trim($finder_options['selection'], ', '); 154 155 return $finder_options; 156 } 157 158 function constructSqlForInclusion($association_id) 159 { 160 return ' LEFT OUTER JOIN '. 161 $this->Owner->$association_id->getTableName().' AS _'.$association_id. 162 ' ON '. 163 '__owner.'.$this->Owner->getPrimaryKey(). 164 ' = '. 165 '_'.$association_id.'.'.$this->Owner->$association_id->getAssociationOption('foreign_key').' '; 166 } 167 168 function &build($association_id, $attributes = array(), $replace_existing = true) 169 { 170 $class_name = $this->Owner->$association_id->getAssociationOption('class_name'); 171 $foreign_key = $this->Owner->$association_id->getAssociationOption('foreign_key'); 172 Ak::import($class_name); 173 $record =& new $class_name($attributes); 174 if ($replace_existing){ 175 $record =& $this->replace($association_id, $record, true); 176 } 177 if(!$this->Owner->isNewRecord()){ 178 $record->set($foreign_key, $this->Owner->getId()); 179 } 180 181 $record =& $this->_build($association_id, &$record); 182 183 return $record; 184 } 185 186 187 /** 188 * Returns a new object of the associated type that has been instantiated with attributes 189 * and linked to this object through a foreign key and that has already been 190 * saved (if it passed the validation) 191 */ 192 function &create($association_id, $attributes = array(), $replace_existing = true) 193 { 194 $this->build($association_id, $attributes, $replace_existing); 195 $this->Owner->$association_id->save(); 196 $this->Owner->$association_id->_loaded = true; 197 return $this->Owner->$association_id; 198 } 199 200 function &replace($association_id, &$NewAssociated, $dont_save = false) 201 { 202 $Associated =& $this->loadAssociated($association_id); 203 if(!empty($Associated->__activeRecordObject) && !empty($NewAssociated->__activeRecordObject) && $Associated->getId() == $NewAssociated->getId()){ 204 return $NewAssociated; 205 } 206 207 if(!empty($Associated->__activeRecordObject)){ 208 if ($Associated->getAssociationOption('dependent') && !$dont_save){ 209 if(!$Associated->isNewRecord()){ 210 $Associated->destroy(); 211 } 212 }elseif(!$dont_save){ 213 $Associated->set($Associated->getAssociationOption('foreign_key'), null); 214 if($Associated->isNewRecord()){ 215 $Associated->save(); 216 } 217 } 218 } 219 220 $result = false; 221 222 if (!empty($NewAssociated->__activeRecordObject)){ 223 if(!$this->Owner->isNewRecord()){ 224 $NewAssociated->set($Associated->getAssociationOption('foreign_key'), $this->Owner->getId()); 225 } 226 227 $NewAssociated =& $this->_build($association_id, &$NewAssociated); 228 229 $NewAssociated->_loaded = true; 230 if(!$NewAssociated->isNewRecord() || !$dont_save){ 231 if($NewAssociated->save()){ 232 return $NewAssociated; 233 } 234 }else{ 235 return $NewAssociated; 236 } 237 } 238 return $result; 239 } 240 241 function &findAssociated($association_id) 242 { 243 $false = false; 244 if(!$this->Owner->getId()){ 245 return $false; 246 } 247 if(empty($this->Owner->$association_id->__activeRecordObject)){ 248 $this->build($association_id, array(), false); 249 } 250 251 $table_name = $this->Owner->$association_id->getAssociationOption('table_name'); 252 253 $finder_options = array( 254 'conditions' => trim($this->Owner->$association_id->_addTableAliasesToAssociatedSql($table_name, $this->constructSqlConditions($association_id))), 255 'selection' => $table_name, 256 'joins' => trim($this->Owner->$association_id->_addTableAliasesToAssociatedSql($table_name, $this->constructSql($association_id))), 257 'order' => trim($this->Owner->$association_id->_addTableAliasesToAssociatedSql($table_name, $this->Owner->$association_id->getAssociationOption('order'))) 258 ); 259 260 /** 261 * todo we will use a select statement later 262 */ 263 $sql = $this->Owner->constructFinderSqlWithAssociations($finder_options, false);//.' LIMIT 1'; 264 if($results =& $this->Owner->$association_id->findBySql($sql)){ 265 $result =& $results[0]; 266 } 267 268 return $result; 269 } 270 271 272 function constructSqlConditions($association_id) 273 { 274 $foreign_key = $this->Owner->$association_id->getAssociationOption('foreign_key'); 275 $conditions = $this->Owner->$association_id->getAssociationOption('conditions'); 276 277 $foreign_key_value = $this->Owner->getId(); 278 if(empty($foreign_key_value)){ 279 return $conditions; 280 } 281 return (empty($conditions) ? '' : $conditions.' AND ').$foreign_key.' = '.$this->Owner->castAttributeForDatabase($foreign_key, $foreign_key_value); 282 } 283 284 function constructSql($association_id) 285 { 286 $foreign_key = $this->Owner->$association_id->getAssociationOption('foreign_key'); 287 $table_name = $this->Owner->$association_id->getAssociationOption('table_name'); 288 $owner_table = $this->Owner->getTableName(); 289 290 return ' LEFT OUTER JOIN '.$owner_table.' ON '.$owner_table.'.'.$this->Owner->getPrimaryKey().' = '.$table_name.'.'.$foreign_key; 291 } 292 293 294 /** 295 * Triggers 296 */ 297 function afterSave(&$object) 298 { 299 $success = true; 300 $associated_ids = $object->getAssociatedIds(); 301 302 foreach ($associated_ids as $associated_id){ 303 if(!empty($object->$associated_id->__activeRecordObject)){ 304 305 if(strtolower($object->hasOne->getOption($associated_id, 'class_name')) == strtolower($object->$associated_id->getType())){ 306 $object->hasOne->replace($associated_id, $object->$associated_id, false); 307 $object->$associated_id->set($object->hasOne->getOption($associated_id, 'foreign_key'), $object->getId()); 308 $success = $object->$associated_id->save() ? $success : false; 309 310 }elseif($object->$associated_id->getType() == 'hasOne'){ 311 $attributes = array(); 312 foreach ((array)$object->$associated_id as $k=>$v){ 313 $k[0] != '_' ? $attributes[$k] = $v : null; 314 } 315 $attributes = array_diff($attributes, array('')); 316 if(!empty($attributes)){ 317 $object->hasOne->build($associated_id, $attributes); 318 } 319 } 320 } 321 } 322 return $success; 323 } 324 325 326 function afterDestroy(&$object) 327 { 328 $success = true; 329 $associated_ids = $object->getAssociatedIds(); 330 foreach ($associated_ids as $associated_id){ 331 if( isset($object->$associated_id->_associatedAs) && 332 $object->$associated_id->_associatedAs == 'hasOne' && 333 $object->$associated_id->getAssociationOption('dependent')){ 334 if ($object->$associated_id->getType() == 'hasOne'){ 335 $object->$associated_id->load(); 336 } 337 if(method_exists($object->$associated_id, 'destroy')){ 338 $success = $object->$associated_id->destroy() ? $success : false; 339 } 340 } 341 } 342 return $success; 343 } 344 } 345 346 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Mon Oct 27 12:43:49 2008 | Cross-referenced by PHPXref 0.6 |