| [ 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 for a single associated object that this object holds an id to. 23 * * <tt>belongsTo->assign($association_id, $Associate);</tt> - assigns the associate object, extracts the primary key, and sets it as the foreign key. 24 * * <tt>belongsTo->build($association_id, $attributes = array())</tt> - returns a new object of the associated type that has been instantiated 25 * with +attributes+ and linked to this object through a foreign key but has not yet been saved. 26 * * <tt>belongsTo->create($association_id, $attributes = array())</tt> - returns a new object of the associated type that has been instantiated 27 * with +attributes+ and linked to this object through a foreign key and that has already been saved (if it passed the validation). 28 * 29 * Example: A Post class declares <tt>belongsTo('author')</tt>, which will add: 30 * * <tt>$Post->author->load()</tt> (similar to <tt>$Author->find($author_id)</tt>) 31 * * <tt>$Post->author->assign($Author)</tt> (similar to <tt>$Post->author_id = $Author->getId();</tt>) 32 * * <tt>$Post->author->build($Author);</tt> (similar to <tt>$Post->author = new Author();</tt>) 33 * * <tt>$Post->author->create($Author);</tt> (similar to <tt>$Post->author = new Author(); $Post->author->save();</tt>) 34 * The declaration can also include an options hash to specialize the behavior of the association. 35 * 36 * Options are: 37 * * <tt>class_name</tt> - specify the class name of the association. Use it only if that name can't be inferred 38 * from the association name. So <tt>belongsTo('author')</tt> will by default be linked to the 'Author' class, but 39 * if the real class name is 'Person', you'll have to specify it with this option. 40 * * <tt>conditions</tt> - specify the conditions that the associated object must meet in order to be included as a "WHERE" 41 * sql fragment, such as "authorized = 1". 42 * * <tt>order</tt> - specify the order from which the associated object will be picked at the top. Specified as 43 * an "ORDER BY" sql fragment, such as "last_name, first_name DESC" 44 * * <tt>primary_key_name</tt> - specify the foreign key used for the association. By default this is guessed to be the name 45 * of the associated class in lower-case and "_id" suffixed. So a 'Person' class that makes a belongsTo association to a 46 * 'Boss' class will use "boss_id" as the default primary_key_name. 47 * * <tt>counter_cache</tt> - caches the number of belonging objects on the associate class through use of increment_counter 48 * and decrement_counter. The counter cache is incremented when an object of this class is created and decremented when it's 49 * destroyed. This requires that a column named "#{table_name}_count" (such as comments_count for a belonging Comment class) 50 * is used on the associate class (such as a Post class). 51 * 52 * Option examples: 53 * belongsTo('firm', array('primary_key_name' => 'client_of')); 54 * belongsTo('author', array('class_name' => 'Person', 'primary_key_name' => 'author_id')); 55 * belongsTo('valid_coupon', array('class_name' => 'Coupon', 'primary_key_name' => 'coupon_id', 'conditions' => "'discounts' > 'payments_count'")); 56 */ 57 class AkBelongsTo extends AkAssociation 58 { 59 var $associated_ids = array(); 60 61 function &addAssociated($association_id, $options = array()) 62 { 63 64 $default_options = array( 65 'class_name' => empty($options['class_name']) ? AkInflector::camelize($association_id) : $options['class_name'], 66 'primary_key_name', 67 'remote', 68 'conditions', 69 'order', 70 //'dependent', 71 'instantiate'=>false, 72 'counter_cache' => false 73 ); 74 75 $options = array_merge($default_options, $options); 76 77 //$options['table_name'] = empty($options['table_name']) ? AkInflector::tableize($options['class_name']) : $options['table_name']; 78 $options['primary_key_name'] = empty($options['primary_key_name']) ? AkInflector::underscore($options['class_name']).'_id' : $options['primary_key_name']; 79 if($options['counter_cache']){ 80 $options['counter_cache_column'] = !isset($options['counter_cache_column']) ? AkInflector::underscore($options['class_name']).'_counter' : $options['counter_cache_column']; 81 } 82 83 $this->setOptions($association_id, $options); 84 85 $associated = $this->addModel($association_id, new AkAssociatedActiveRecord()); 86 87 $this->setAssociatedId($association_id, $associated->getId()); 88 89 $this->_build($association_id, $associated); 90 91 $this->_saveLoadedHandler($association_id, $associated); 92 93 if($options['instantiate']){ 94 $associated =& $this->assign($association_id, new $options['class_name']($this->Owner->get($options['primary_key_name']))); 95 } 96 97 return $associated; 98 } 99 100 101 function getType() 102 { 103 return 'belongsTo'; 104 } 105 106 107 function &findAssociated($association_id) 108 { 109 $result = false; 110 $primary_key_name = $this->Owner->$association_id->getAssociationOption('primary_key_name'); 111 $primary_key_name_value = $this->Owner->get($primary_key_name); 112 if(!$primary_key_name_value){ 113 return $result; 114 } 115 if(empty($this->Owner->$association_id->__activeRecordObject)){ 116 $this->build($association_id, array(), false); 117 } 118 119 $result =& $this->Owner->$association_id->find($primary_key_name_value); 120 121 return $result; 122 } 123 124 function &assign($association_id, &$Associated) 125 { 126 $primary_key_name = $this->Owner->$association_id->getAssociationOption('primary_key_name'); 127 if($Associated->save()){ 128 $this->Owner->set($primary_key_name, $Associated->getId()); 129 } 130 $Associated =& $this->_build($association_id, &$Associated); 131 return $Associated; 132 } 133 134 function &build($association_id, $attributes = array(), $replace = true) 135 { 136 $class_name = $this->Owner->$association_id->getAssociationOption('class_name'); 137 Ak::import($class_name); 138 $record =& new $class_name($attributes); 139 $record =& $this->Owner->$association_id->replace($record); 140 return $record; 141 } 142 143 /** 144 * Returns a new object of the associated type that has been instantiated with attributes 145 * and linked to this object through a foreign key and that has already been saved (if it passed the validation) 146 */ 147 function &create($association_id, $attributes = array()) 148 { 149 $class_name = $this->Owner->$association_id->getAssociationOption('class_name'); 150 $record =& new $class_name($attributes); 151 $record->save(); 152 $this->replace($association_id, $record, true); 153 return $this->Owner->$association_id; 154 } 155 156 157 function &load($association_id) 158 { 159 if (!$this->Owner->isNewRecord()){ 160 if(empty($this->Owner->$association_id->_loaded)){ 161 if($Associated =& $this->findAssociated($association_id)){ 162 $Associated->_loaded = true; 163 $this->_build($association_id, $Associated, false); 164 } 165 } 166 } 167 return $this->Owner->$association_id; 168 } 169 170 function &replace($association_id, &$NewAssociated) 171 { 172 $counter_cache_name = $this->Owner->belongsTo->getOption($association_id, 'counter_cache_column'); 173 if(empty($NewAssociated)){ 174 $primary_key = $this->Owner->belongsTo->getOption($association_id, 'primary_key_name'); 175 if($counter_cache_name && isset($this->Owner->$association_id->$counter_cache_name) && !$this->Owner->isNewRecord()){ 176 $this->Owner->$association_id->decrementCounter($counter_cache_name, $this->Owner->get($primary_key)); 177 } 178 $this->Owner->$association_id =& $this->_getLoadedHandler($association_id); 179 $this->Owner->set($primary_key, null); 180 }else{ 181 $primary_key = $this->Owner->belongsTo->getOption($association_id, 'primary_key_name'); 182 if($counter_cache_name && !$this->Owner->isNewRecord()){ 183 $this->Owner->$association_id->incrementCounter($counter_cache_name, $NewAssociated->getId()); 184 $previous_id = $this->Owner->get($primary_key); 185 if($previous_id){ 186 $this->Owner->$association_id->decrementCounter($counter_cache_name, $previous_id); 187 } 188 } 189 if(!$NewAssociated->isNewRecord()){ 190 $this->Owner->set($primary_key, $NewAssociated->getId()); 191 } 192 $this->updated[$association_id] = true; 193 194 $this->updated[$association_id] = true; 195 $this->loaded[$association_id] = true; 196 } 197 $this->_build($association_id, $NewAssociated); 198 return $NewAssociated; 199 } 200 201 202 function getAssociatedFinderSqlOptions($association_id, $options = array()) 203 { 204 $default_options = array( 205 'conditions' => $this->Owner->$association_id->getAssociationOption('include_conditions_when_included'), 206 'order' => $this->Owner->$association_id->getAssociationOption('include_order_when_included') 207 ); 208 209 if(empty($this->Owner->$association_id->__activeRecordObject)){ 210 $this->build($association_id, array(), false); 211 } 212 213 $table_name = $this->Owner->$association_id->getTableName(); 214 $options = array_merge($default_options, $options); 215 216 $finder_options = array(); 217 218 foreach ($options as $option=>$available) { 219 if($available){ 220 $value = $this->Owner->$association_id->getAssociationOption($option); 221 empty($value) ? null : ($finder_options[$option] = trim($this->Owner->$association_id->_addTableAliasesToAssociatedSql('_'.$association_id, $value))); 222 } 223 } 224 225 $finder_options['joins'] = $this->Owner->$association_id->constructSqlForInclusion(); 226 227 $finder_options['selection'] = ''; 228 foreach (array_keys($this->Owner->$association_id->getColumns()) as $column_name){ 229 $finder_options['selection'] .= '_'.$association_id.'.'.$column_name.' AS _'.$association_id.'_'.$column_name.', '; 230 } 231 $finder_options['selection'] = trim($finder_options['selection'], ', '); 232 233 return $finder_options; 234 } 235 236 function constructSqlForInclusion($association_id) 237 { 238 return ' LEFT OUTER JOIN '. 239 $this->Owner->$association_id->getTableName().' AS _'.$association_id. 240 ' ON '. 241 '__owner.'.$this->Owner->$association_id->getAssociationOption('primary_key_name'). 242 ' = '. 243 '_'.$association_id.'.'.$this->Owner->$association_id->getPrimaryKey().' '; 244 } 245 246 247 /** 248 * Triggers 249 */ 250 251 function beforeSave(&$object) 252 { 253 $association_ids = $object->getAssociatedIds(); 254 foreach ($association_ids as $association_id){ 255 if( !empty($object->$association_id->__activeRecordObject) && 256 strtolower($object->belongsTo->getOption($association_id, 'class_name')) == strtolower($object->$association_id->getType())){ 257 $primary_key_name = $this->Owner->belongsTo->getOption($association_id, 'primary_key_name'); 258 if($object->$association_id->isNewRecord() && !$object->$association_id->hasAttributesDefined()){ 259 $object->$association_id->save(true); 260 } 261 $primary_key_name_value = $object->$association_id->getId(); 262 if(!empty($primary_key_name_value)){ 263 $object->set($primary_key_name, $primary_key_name_value); 264 } 265 } 266 } 267 return true; 268 } 269 270 function beforeDestroy(&$object) 271 { 272 $association_ids = $object->getAssociatedIds(); 273 foreach ($association_ids as $association_id){ 274 if(!empty($object->$association_id) && is_object($object->$association_id) && method_exists($object->$association_id,'getType') && 275 strtolower($object->belongsTo->getOption($association_id, 'class_name')) == strtolower($object->$association_id->getType())){ 276 $primary_key_name = $this->Owner->$association_id->getAssociationOption('primary_key_name'); 277 if($this->Owner->$association_id->getAssociationOption('counter_cache')){ 278 $object->$association_id->decrementCounter(AkInflector::pluralize($association_id).'_count', $object->get($primary_key_name)); 279 } 280 } 281 } 282 return true; 283 } 284 } 285 286 287 ?>
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 |