Subversion-Projekte lars-tiefland.prado

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/**
3
 * TActiveRecordGateway, TActiveRecordStatementType, TActiveRecordEventParameter classes file.
4
 *
5
 * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
6
 * @link http://www.pradosoft.com/
7
 * @copyright Copyright &copy; 2005-2008 PradoSoft
8
 * @license http://www.pradosoft.com/license/
9
 * @version $Id: TActiveRecordGateway.php 2541 2008-10-21 15:05:13Z qiang.xue $
10
 * @package System.Data.ActiveRecord
11
 */
12
 
13
/**
14
 * TActiveRecordGateway excutes the SQL command queries and returns the data
15
 * record as arrays (for most finder methods).
16
 *
17
 * @author Wei Zhuo <weizho[at]gmail[dot]com>
18
 * @version $Id: TActiveRecordGateway.php 2541 2008-10-21 15:05:13Z qiang.xue $
19
 * @package System.Data.ActiveRecord
20
 * @since 3.1
21
 */
22
class TActiveRecordGateway extends TComponent
23
{
24
	private $_manager;
25
	private $_tables=array(); //table cache
26
	private $_meta=array(); //meta data cache.
27
	private $_commandBuilders=array();
28
	private $_currentRecord;
29
 
30
	/**
31
	 * Constant name for specifying optional table name in TActiveRecord.
32
	 */
33
	const TABLE_CONST='TABLE';
34
	/**
35
	 * Method name for returning optional table name in in TActiveRecord
36
	 */
37
	const TABLE_METHOD='table';
38
 
39
	/**
40
	 * Record gateway constructor.
41
	 * @param TActiveRecordManager $manager
42
	 */
43
	public function __construct(TActiveRecordManager $manager)
44
	{
45
		$this->_manager=$manager;
46
	}
47
 
48
	/**
49
	 * @return TActiveRecordManager record manager.
50
	 */
51
	protected function getManager()
52
	{
53
		return $this->_manager;
54
	}
55
 
56
	/**
57
	 * Gets the table name from the 'TABLE' constant of the active record
58
	 * class if defined, otherwise use the class name as table name.
59
	 * @param TActiveRecord active record instance
60
	 * @return string table name for the given record class.
61
	 */
62
	protected function getRecordTableName(TActiveRecord $record)
63
	{
64
		$class = new ReflectionClass($record);
65
		if($class->hasConstant(self::TABLE_CONST))
66
		{
67
			$value = $class->getConstant(self::TABLE_CONST);
68
			if(empty($value))
69
				throw new TActiveRecordException('ar_invalid_tablename_property',
70
					get_class($record),self::TABLE_CONST);
71
			return $value;
72
		}
73
		elseif ($class->hasMethod(self::TABLE_METHOD))
74
		{
75
			$value = $record->{self::TABLE_METHOD}();
76
			if(empty($value))
77
				throw new TActiveRecordException('ar_invalid_tablename_method',
78
					get_class($record),self::TABLE_METHOD);
79
			return $value;
80
		}
81
		else
82
			return strtolower(get_class($record));
83
	}
84
 
85
	/**
86
	 * Returns table information, trys the application cache first.
87
	 * @param TActiveRecord $record
88
	 * @return TDbTableInfo table information.
89
	 */
90
	public function getRecordTableInfo(TActiveRecord $record)
91
	{
92
		$tableName = $this->getRecordTableName($record);
93
		return $this->getTableInfo($record->getDbConnection(), $tableName);
94
	}
95
 
96
	/**
97
	 * Returns table information for table in the database connection.
98
	 * @param TDbConnection database connection
99
	 * @param string table name
100
	 * @return TDbTableInfo table details.
101
	 */
102
	public function getTableInfo(TDbConnection $connection, $tableName)
103
	{
104
		$connStr = $connection->getConnectionString();
105
		$key = $connStr.$tableName;
106
		if(!isset($this->_tables[$key]))
107
		{
108
			//call this first to ensure that unserializing the cache
109
			//will find the correct driver dependent classes.
110
			if(!isset($this->_meta[$connStr]))
111
			{
112
				Prado::using('System.Data.Common.TDbMetaData');
113
				$this->_meta[$connStr] = TDbMetaData::getInstance($connection);
114
			}
115
 
116
			$tableInfo = null;
117
			if(($cache=$this->getManager()->getCache())!==null)
118
				$tableInfo = $cache->get($key);
119
			if(empty($tableInfo))
120
			{
121
				$tableInfo = $this->_meta[$connStr]->getTableInfo($tableName);
122
				if($cache!==null)
123
					$cache->set($key, $tableInfo);
124
			}
125
			$this->_tables[$key] = $tableInfo;
126
		}
127
		return $this->_tables[$key];
128
	}
129
 
130
	/**
131
	 * @param TActiveRecord $record
132
	 * @return TDataGatewayCommand
133
	 */
134
	public function getCommand(TActiveRecord $record)
135
	{
136
		$conn = $record->getDbConnection();
137
		$connStr = $conn->getConnectionString();
138
		$tableInfo = $this->getRecordTableInfo($record);
139
		if(!isset($this->_commandBuilders[$connStr]))
140
		{
141
			$builder = $tableInfo->createCommandBuilder($record->getDbConnection());
142
			Prado::using('System.Data.DataGateway.TDataGatewayCommand');
143
			$command = new TDataGatewayCommand($builder);
144
			$command->OnCreateCommand[] = array($this, 'onCreateCommand');
145
			$command->OnExecuteCommand[] = array($this, 'onExecuteCommand');
146
			$this->_commandBuilders[$connStr] = $command;
147
 
148
		}
149
		$this->_commandBuilders[$connStr]->getBuilder()->setTableInfo($tableInfo);
150
		$this->_currentRecord=$record;
151
		return $this->_commandBuilders[$connStr];
152
	}
153
 
154
	/**
155
	 * Raised when a command is prepared and parameter binding is completed.
156
	 * The parameter object is TDataGatewayEventParameter of which the
157
	 * {@link TDataGatewayEventParameter::getCommand Command} property can be
158
	 * inspected to obtain the sql query to be executed.
159
	 * This method also raises the OnCreateCommand event on the ActiveRecord
160
	 * object calling this gateway.
161
	 * @param TDataGatewayCommand originator $sender
162
	 * @param TDataGatewayEventParameter
163
	 */
164
	public function onCreateCommand($sender, $param)
165
	{
166
		$this->raiseEvent('OnCreateCommand', $this, $param);
167
		if($this->_currentRecord!==null)
168
			$this->_currentRecord->onCreateCommand($param);
169
	}
170
 
171
	/**
172
	 * Raised when a command is executed and the result from the database was returned.
173
	 * The parameter object is TDataGatewayResultEventParameter of which the
174
	 * {@link TDataGatewayEventParameter::getResult Result} property contains
175
	 * the data return from the database. The data returned can be changed
176
	 * by setting the {@link TDataGatewayEventParameter::setResult Result} property.
177
	 * This method also raises the OnCreateCommand event on the ActiveRecord
178
	 * object calling this gateway.
179
	 * @param TDataGatewayCommand originator $sender
180
	 * @param TDataGatewayResultEventParameter
181
	 */
182
	public function onExecuteCommand($sender, $param)
183
	{
184
		$this->raiseEvent('OnExecuteCommand', $this, $param);
185
		if($this->_currentRecord!==null)
186
			$this->_currentRecord->onExecuteCommand($param);
187
	}
188
 
189
	/**
190
	 * Returns record data matching the given primary key(s). If the table uses
191
	 * composite key, specify the name value pairs as an array.
192
	 * @param TActiveRecord active record instance.
193
	 * @param array primary name value pairs
194
	 * @return array record data
195
	 */
196
	public function findRecordByPK(TActiveRecord $record,$keys)
197
	{
198
		$command = $this->getCommand($record);
199
		return $command->findByPk($keys);
200
	}
201
 
202
	/**
203
	 * Returns records matching the list of given primary keys.
204
	 * @param TActiveRecord active record instance.
205
	 * @param array list of primary name value pairs
206
	 * @return array matching data.
207
	 */
208
	public function findRecordsByPks(TActiveRecord $record, $keys)
209
	{
210
		return $this->getCommand($record)->findAllByPk($keys);
211
	}
212
 
213
 
214
	/**
215
	 * Returns record data matching the given critera. If $iterator is true, it will
216
	 * return multiple rows as TDbDataReader otherwise it returns the <b>first</b> row data.
217
	 * @param TActiveRecord active record finder instance.
218
	 * @param TActiveRecordCriteria search criteria.
219
	 * @param boolean true to return multiple rows as iterator, false returns first row.
220
	 * @return mixed matching data.
221
	 */
222
	public function findRecordsByCriteria(TActiveRecord $record, $criteria, $iterator=false)
223
	{
224
		$command = $this->getCommand($record);
225
		return $iterator ? $command->findAll($criteria) : $command->find($criteria);
226
	}
227
 
228
	/**
229
	 * Return record data from sql query.
230
	 * @param TActiveRecord active record finder instance.
231
	 * @param TActiveRecordCriteria sql query
232
	 * @return array result.
233
	 */
234
	public function findRecordBySql(TActiveRecord $record, $criteria)
235
	{
236
		return $this->getCommand($record)->findBySql($criteria);
237
	}
238
 
239
	/**
240
	 * Return record data from sql query.
241
	 * @param TActiveRecord active record finder instance.
242
	 * @param TActiveRecordCriteria sql query
243
	 * @return TDbDataReader result iterator.
244
	 */
245
	public function findRecordsBySql(TActiveRecord $record, $criteria)
246
	{
247
		return $this->getCommand($record)->findAllBySql($criteria);
248
	}
249
 
250
	public function findRecordsByIndex(TActiveRecord $record, $criteria, $fields, $values)
251
	{
252
		return $this->getCommand($record)->findAllByIndex($criteria,$fields,$values);
253
	}
254
 
255
	/**
256
	 * Returns the number of records that match the given criteria.
257
	 * @param TActiveRecord active record finder instance.
258
	 * @param TActiveRecordCriteria search criteria
259
	 * @return int number of records.
260
	 */
261
	public function countRecords(TActiveRecord $record, $criteria)
262
	{
263
		return $this->getCommand($record)->count($criteria);
264
	}
265
 
266
	/**
267
	 * Insert a new record.
268
	 * @param TActiveRecord new record.
269
	 * @return int number of rows affected.
270
	 */
271
	public function insert(TActiveRecord $record)
272
	{
273
		//$this->updateAssociatedRecords($record,true);
274
		$result = $this->getCommand($record)->insert($this->getInsertValues($record));
275
		if($result)
276
			$this->updatePostInsert($record);
277
		//$this->updateAssociatedRecords($record);
278
		return $result;
279
	}
280
 
281
	/**
282
	 * Sets the last insert ID to the corresponding property of the record if available.
283
	 * @param TActiveRecord record for insertion
284
	 */
285
	protected function updatePostInsert($record)
286
	{
287
		$command = $this->getCommand($record);
288
		$tableInfo = $command->getTableInfo();
289
		foreach($tableInfo->getColumns() as $name => $column)
290
		{
291
			if($column->hasSequence())
292
				$record->setColumnValue($name,$command->getLastInsertID($column->getSequenceName()));
293
		}
294
	}
295
 
296
	/**
297
	 * @param TActiveRecord record
298
	 * @return array insert values.
299
	 */
300
	protected function getInsertValues(TActiveRecord $record)
301
	{
302
		$values=array();
303
		$tableInfo = $this->getCommand($record)->getTableInfo();
304
		foreach($tableInfo->getColumns() as $name=>$column)
305
		{
306
			if($column->getIsExcluded())
307
				continue;
308
			$value = $record->getColumnValue($name);
309
			if(!$column->getAllowNull() && $value===null && !$column->hasSequence() && !$column->getDefaultValue())
310
			{
311
				throw new TActiveRecordException(
312
					'ar_value_must_not_be_null', get_class($record),
313
					$tableInfo->getTableFullName(), $name);
314
			}
315
			if($value!==null)
316
				$values[$name] = $value;
317
		}
318
		return $values;
319
	}
320
 
321
	/**
322
	 * Update the record.
323
	 * @param TActiveRecord dirty record.
324
	 * @return int number of rows affected.
325
	 */
326
	public function update(TActiveRecord $record)
327
	{
328
		//$this->updateAssociatedRecords($record,true);
329
		list($data, $keys) = $this->getUpdateValues($record);
330
		$result = $this->getCommand($record)->updateByPk($data, $keys);
331
		//$this->updateAssociatedRecords($record);
332
		return $result;
333
	}
334
 
335
	protected function getUpdateValues(TActiveRecord $record)
336
	{
337
		$values=array();
338
		$tableInfo = $this->getCommand($record)->getTableInfo();
339
		$primary=array();
340
		foreach($tableInfo->getColumns() as $name=>$column)
341
		{
342
			if($column->getIsExcluded())
343
				continue;
344
			$value = $record->getColumnValue($name);
345
			if(!$column->getAllowNull() && $value===null)
346
			{
347
				throw new TActiveRecordException(
348
					'ar_value_must_not_be_null', get_class($record),
349
					$tableInfo->getTableFullName(), $name);
350
			}
351
			if($column->getIsPrimaryKey())
352
				$primary[] = $value;
353
			else
354
				$values[$name] = $value;
355
		}
356
		return array($values,$primary);
357
	}
358
 
359
	protected function updateAssociatedRecords(TActiveRecord $record,$updateBelongsTo=false)
360
	{
361
		$context = new TActiveRecordRelationContext($record);
362
		return $context->updateAssociatedRecords($updateBelongsTo);
363
	}
364
 
365
	/**
366
	 * Delete the record.
367
	 * @param TActiveRecord record to be deleted.
368
	 * @return int number of rows affected.
369
	 */
370
	public function delete(TActiveRecord $record)
371
	{
372
		return $this->getCommand($record)->deleteByPk($this->getPrimaryKeyValues($record));
373
	}
374
 
375
	protected function getPrimaryKeyValues(TActiveRecord $record)
376
	{
377
		$tableInfo = $this->getCommand($record)->getTableInfo();
378
		$primary=array();
379
		foreach($tableInfo->getColumns() as $name=>$column)
380
		{
381
			if($column->getIsPrimaryKey())
382
				$primary[$name] = $record->getColumnValue($name);
383
		}
384
		return $primary;
385
	}
386
 
387
	/**
388
	 * Delete multiple records using primary keys.
389
	 * @param TActiveRecord finder instance.
390
	 * @return int number of rows deleted.
391
	 */
392
	public function deleteRecordsByPk(TActiveRecord $record, $keys)
393
	{
394
		return $this->getCommand($record)->deleteByPk($keys);
395
	}
396
 
397
	/**
398
	 * Delete multiple records by criteria.
399
	 * @param TActiveRecord active record finder instance.
400
	 * @param TActiveRecordCriteria search criteria
401
	 * @return int number of records.
402
	 */
403
	public function deleteRecordsByCriteria(TActiveRecord $record, $criteria)
404
	{
405
		return $this->getCommand($record)->delete($criteria);
406
	}
407
 
408
	/**
409
	 * Raise the corresponding command event, insert, update, delete or select.
410
	 * @param string command type
411
	 * @param TDbCommand sql command to be executed.
412
	 * @param TActiveRecord active record
413
	 * @param TActiveRecordCriteria data for the command.
414
	 */
415
	protected function raiseCommandEvent($event,$command,$record,$criteria)
416
	{
417
		if(!($criteria instanceof TSqlCriteria))
418
			$criteria = new TActiveRecordCriteria(null,$criteria);
419
		$param = new TActiveRecordEventParameter($command,$record,$criteria);
420
		$manager = $record->getRecordManager();
421
		$manager->{$event}($param);
422
		$record->{$event}($param);
423
	}
424
}
425