Subversion-Projekte lars-tiefland.prado

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/**
3
 * TTableGateway class 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$
10
 * @package System.Data.DataGateway
11
 */
12
 
13
/**
14
 * Loads the data gateway command builder and sql criteria.
15
 */
16
Prado::using('System.Data.DataGateway.TSqlCriteria');
17
Prado::using('System.Data.DataGateway.TDataGatewayCommand');
18
 
19
/**
20
 * TTableGateway class provides several find methods to get data from the database
21
 * and update, insert, and delete methods.
22
 *
23
 * Each method maps the input parameters into a SQL call and executes the SQL
24
 * against a database connection. The TTableGateway is stateless
25
 * (with respect to the data and data objects), as its role is to push data back and forth.
26
 *
27
 * Example usage:
28
 * <code>
29
 * //create a connection
30
 * $dsn = 'pgsql:host=localhost;dbname=test';
31
 * $conn = new TDbConnection($dsn, 'dbuser','dbpass');
32
 *
33
 * //create a table gateway for table/view named 'address'
34
 * $table = new TTableGateway('address', $conn);
35
 *
36
 * //insert a new row, returns last insert id (if applicable)
37
 * $id = $table->insert(array('name'=>'wei', 'phone'=>'111111'));
38
 *
39
 * $record1 = $table->findByPk($id); //find inserted record
40
 *
41
 * //finds all records, returns an iterator
42
 * $records = $table->findAll();
43
 * print_r($records->readAll());
44
 *
45
 * //update the row
46
 * $table->updateByPk($record1, $id);
47
 * </code>
48
 *
49
 * All methods that may return more than one row of data will return an
50
 * TDbDataReader iterator.
51
 *
52
 * The OnCreateCommand event is raised when a command is prepared and parameter
53
 * binding is completed. The parameter object is a TDataGatewayEventParameter of which the
54
 * {@link TDataGatewayEventParameter::getCommand Command} property can be
55
 * inspected to obtain the sql query to be executed.
56
 *
57
 * The OnExecuteCommand	event is raised when a command is executed and the result
58
 * from the database was returned. The parameter object is a
59
 * TDataGatewayResultEventParameter of which the
60
 * {@link TDataGatewayEventParameter::getResult Result} property contains
61
 * the data return from the database. The data returned can be changed
62
 * by setting the {@link TDataGatewayEventParameter::setResult Result} property.
63
 *
64
 * <code>
65
 * $table->OnCreateCommand[] = 'log_it'; //any valid PHP callback statement
66
 * $table->OnExecuteCommand[] = array($obj, 'method_name'); // calls 'method_name' on $obj
67
 *
68
 * function log_it($sender, $param)
69
 * {
70
 *     var_dump($param); //TDataGatewayEventParameter object.
71
 * }
72
 * </code>
73
 *
74
 * @author Wei Zhuo <weizho[at]gmail[dot]com>
75
 * @version $Id$
76
 * @package System.Data.DataGateway
77
 * @since 3.1
78
 */
79
class TTableGateway extends TComponent
80
{
81
	private $_command;
82
	private $_connection;
83
 
84
	/**
85
	 * Creates a new generic table gateway for a given table or view name
86
	 * and a database connection.
87
	 * @param string|TDbTableInfo table or view name or table information.
88
	 * @param TDbConnection database connection.
89
	 */
90
	public function __construct($table,$connection)
91
	{
92
		$this->_connection=$connection;
93
		if(is_string($table))
94
			$this->setTableName($table);
95
		else if($table instanceof TDbTableInfo)
96
			$this->setTableInfo($table);
97
		else
98
			throw new TDbException('dbtablegateway_invalid_table_info');
99
	}
100
 
101
	/**
102
	 * @param TDbTableInfo table or view information.
103
	 */
104
	protected function setTableInfo($tableInfo)
105
	{
106
		$builder = $tableInfo->createCommandBuilder($this->getDbConnection());
107
		$this->initCommandBuilder($builder);
108
	}
109
 
110
	/**
111
	 * Sets up the command builder for the given table.
112
	 * @param string table or view name.
113
	 */
114
	protected function setTableName($tableName)
115
	{
116
		Prado::using('System.Data.Common.TDbMetaData');
117
		$meta = TDbMetaData::getInstance($this->getDbConnection());
118
		$this->initCommandBuilder($meta->createCommandBuilder($tableName));
119
	}
120
 
121
	public function getTableInfo()
122
	{
123
		return $this->getCommand()->getTableInfo();
124
	}
125
 
126
	public function getTableName()
127
	{
128
		return $this->getTableInfo()->getTableName();
129
	}
130
 
131
	/**
132
	 * @param TDbCommandBuilder database specific command builder.
133
	 */
134
	protected function initCommandBuilder($builder)
135
	{
136
		$this->_command = new TDataGatewayCommand($builder);
137
		$this->_command->OnCreateCommand[] = array($this, 'onCreateCommand');
138
		$this->_command->OnExecuteCommand[] = array($this, 'onExecuteCommand');
139
	}
140
 
141
	/**
142
	 * Raised when a command is prepared and parameter binding is completed.
143
	 * The parameter object is TDataGatewayEventParameter of which the
144
	 * {@link TDataGatewayEventParameter::getCommand Command} property can be
145
	 * inspected to obtain the sql query to be executed.
146
	 * @param TDataGatewayCommand originator $sender
147
	 * @param TDataGatewayEventParameter
148
	 */
149
	public function onCreateCommand($sender, $param)
150
	{
151
		$this->raiseEvent('OnCreateCommand', $this, $param);
152
	}
153
 
154
	/**
155
	 * Raised when a command is executed and the result from the database was returned.
156
	 * The parameter object is TDataGatewayResultEventParameter of which the
157
	 * {@link TDataGatewayEventParameter::getResult Result} property contains
158
	 * the data return from the database. The data returned can be changed
159
	 * by setting the {@link TDataGatewayEventParameter::setResult Result} property.
160
	 * @param TDataGatewayCommand originator $sender
161
	 * @param TDataGatewayResultEventParameter
162
	 */
163
	public function onExecuteCommand($sender, $param)
164
	{
165
		$this->raiseEvent('OnExecuteCommand', $this, $param);
166
	}
167
 
168
	/**
169
	 * @return TDataGatewayCommand command builder and executor.
170
	 */
171
	protected function getCommand()
172
	{
173
		return $this->_command;
174
	}
175
 
176
	/**
177
	 * @return TDbConnection database connection.
178
	 */
179
	public function getDbConnection()
180
	{
181
		return $this->_connection;
182
	}
183
 
184
	/**
185
	 * Execute arbituary sql command with binding parameters.
186
	 * @param string SQL query string.
187
	 * @param array binding parameters, positional or named.
188
	 * @return array query results.
189
	 */
190
	public function findBySql($sql, $parameters=array())
191
	{
192
		$args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null;
193
		$criteria = $this->getCriteria($sql,$parameters, $args);
194
		return $this->getCommand()->findBySql($criteria);
195
	}
196
 
197
	/**
198
	 * Execute arbituary sql command with binding parameters.
199
	 * @param string SQL query string.
200
	 * @param array binding parameters, positional or named.
201
	 * @return TDbDataReader query results.
202
	 */
203
	public function findAllBySql($sql, $parameters=array())
204
	{
205
		$args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null;
206
		$criteria = $this->getCriteria($sql,$parameters, $args);
207
		return $this->getCommand()->findAllBySql($criteria);
208
	}
209
 
210
	/**
211
	 * Find one single record that matches the criteria.
212
	 *
213
	 * Usage:
214
	 * <code>
215
	 * $table->find('username = :name AND password = :pass',
216
	 * 					array(':name'=>$name, ':pass'=>$pass));
217
	 * $table->find('username = ? AND password = ?', array($name, $pass));
218
	 * $table->find('username = ? AND password = ?', $name, $pass);
219
	 * //$criteria is of TSqlCriteria
220
	 * $table->find($criteria); //the 2nd parameter for find() is ignored.
221
	 * </code>
222
	 *
223
	 * @param string|TSqlCriteria SQL condition or criteria object.
224
	 * @param mixed parameter values.
225
	 * @return array matching record object.
226
	 */
227
	public function find($criteria, $parameters=array())
228
	{
229
		$args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null;
230
		$criteria = $this->getCriteria($criteria,$parameters, $args);
231
		return $this->getCommand()->find($criteria);
232
	}
233
 
234
	/**
235
	 * Accepts same parameters as find(), but returns TDbDataReader instead.
236
	 * @param string|TSqlCriteria SQL condition or criteria object.
237
	 * @param mixed parameter values.
238
	 * @return TDbDataReader matching records.
239
	 */
240
	public function findAll($criteria=null, $parameters=array())
241
	{
242
		$args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null;
243
		if($criteria!==null)
244
			$criteria = $this->getCriteria($criteria,$parameters, $args);
245
		return $this->getCommand()->findAll($criteria);
246
	}
247
 
248
	/**
249
	 * Find one record using only the primary key or composite primary keys. Usage:
250
	 *
251
	 * <code>
252
	 * $table->findByPk($primaryKey);
253
	 * $table->findByPk($key1, $key2, ...);
254
	 * $table->findByPk(array($key1,$key2,...));
255
	 * </code>
256
	 *
257
	 * @param mixed primary keys
258
	 * @return array matching record.
259
	 */
260
	public function findByPk($keys)
261
	{
262
		if(func_num_args() > 1)
263
			$keys = func_get_args();
264
		return $this->getCommand()->findByPk($keys);
265
	}
266
 
267
	/**
268
	 * Similar to findByPk(), but returns TDbDataReader instead.
269
	 *
270
	 * For scalar primary keys:
271
	 * <code>
272
	 * $table->findAllByPk($key1, $key2, ...);
273
	 * $table->findAllByPk(array($key1, $key2, ...));
274
	 * </code>
275
	 *
276
	 * For composite keys:
277
	 * <code>
278
	 * $table->findAllByPk(array($key1, $key2), array($key3, $key4), ...);
279
	 * $table->findAllByPk(array(array($key1, $key2), array($key3, $key4), ...));
280
	 * </code>
281
	 * @param mixed primary keys
282
	 * @return TDbDataReader data reader.
283
	 */
284
	public function findAllByPks($keys)
285
	{
286
		if(func_num_args() > 1)
287
			$keys = func_get_args();
288
		return $this->getCommand()->findAllByPk($keys);
289
	}
290
 
291
	/**
292
	 * Delete records from the table with condition given by $where and
293
	 * binding values specified by $parameter argument.
294
	 * This method uses additional arguments as $parameters. E.g.
295
	 * <code>
296
	 * $table->delete('age > ? AND location = ?', $age, $location);
297
	 * </code>
298
	 * @param string delete condition.
299
	 * @param array condition parameters.
300
	 * @return integer number of records deleted.
301
	 */
302
	public function deleteAll($criteria, $parameters=array())
303
	{
304
		$args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null;
305
		$criteria = $this->getCriteria($criteria,$parameters, $args);
306
		return $this->getCommand()->delete($criteria);
307
	}
308
 
309
	/**
310
	 * Delete records by primary key. Usage:
311
	 *
312
	 * <code>
313
	 * $table->deleteByPk($primaryKey); //delete 1 record
314
	 * $table->deleteByPk($key1,$key2,...); //delete multiple records
315
	 * $table->deleteByPk(array($key1,$key2,...)); //delete multiple records
316
	 * </code>
317
	 *
318
	 * For composite primary keys (determined from the table definitions):
319
	 * <code>
320
	 * $table->deleteByPk(array($key1,$key2)); //delete 1 record
321
	 *
322
	 * //delete multiple records
323
	 * $table->deleteByPk(array($key1,$key2), array($key3,$key4),...);
324
	 *
325
	 * //delete multiple records
326
	 * $table->deleteByPk(array( array($key1,$key2), array($key3,$key4), .. ));
327
	 * </code>
328
	 *
329
	 * @param mixed primary key values.
330
	 * @return int number of records deleted.
331
	 */
332
	public function deleteByPk($keys)
333
	{
334
		if(func_num_args() > 1)
335
			$keys = func_get_args();
336
		return $this->getCommand()->deleteByPk($keys);
337
	}
338
 
339
	/**
340
	 * Alias for deleteByPk()
341
	 */
342
	public function deleteAllByPks($keys)
343
	{
344
		if(func_num_args() > 1)
345
			$keys = func_get_args();
346
		return $this->deleteByPk($keys);
347
	}
348
 
349
	/**
350
	 * Find the number of records.
351
	 * @param string|TSqlCriteria SQL condition or criteria object.
352
	 * @param mixed parameter values.
353
	 * @return int number of records.
354
	 */
355
	public function count($criteria=null,$parameters=array())
356
	{
357
		$args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null;
358
		if($criteria!==null)
359
			$criteria = $this->getCriteria($criteria,$parameters, $args);
360
		return $this->getCommand()->count($criteria);
361
	}
362
 
363
	/**
364
	 * Updates the table with new name-value pair $data. Each array key must
365
	 * correspond to a column name in the table. The update condition is
366
	 * specified by the $where argument and additional binding values can be
367
	 * specified using the $parameter argument.
368
	 * This method uses additional arguments as $parameters. E.g.
369
	 * <code>
370
	 * $gateway->update($data, 'age > ? AND location = ?', $age, $location);
371
	 * </code>
372
	 * @param array new record data.
373
	 * @param string update condition
374
	 * @param array additional binding name-value pairs.
375
	 * @return integer number of records updated.
376
	 */
377
	public function update($data, $criteria, $parameters=array())
378
	{
379
		$args = func_num_args() > 2 ? array_slice(func_get_args(),2) : null;
380
		$criteria = $this->getCriteria($criteria,$parameters, $args);
381
		return $this->getCommand()->update($data, $criteria);
382
	}
383
 
384
	/**
385
	 * Inserts a new record into the table. Each array key must
386
	 * correspond to a column name in the table unless a null value is permitted.
387
	 * @param array new record data.
388
	 * @return mixed last insert id if one column contains a serial or sequence,
389
	 * otherwise true if command executes successfully and affected 1 or more rows.
390
	 */
391
	public function insert($data)
392
	{
393
		return $this->getCommand()->insert($data);
394
	}
395
 
396
	/**
397
	 * @return mixed last insert id, null if none is found.
398
	 */
399
	public function getLastInsertId()
400
	{
401
		return $this->getCommand()->getLastInsertId();
402
	}
403
 
404
	/**
405
	 * Create a new TSqlCriteria object from a string $criteria. The $args
406
	 * are additional parameters and are used in place of the $parameters
407
	 * if $parameters is not an array and $args is an arrary.
408
	 * @param string|TSqlCriteria sql criteria
409
	 * @param mixed parameters passed by the user.
410
	 * @param array additional parameters obtained from function_get_args().
411
	 * @return TSqlCriteria criteria object.
412
	 */
413
	protected function getCriteria($criteria, $parameters, $args)
414
	{
415
		if(is_string($criteria))
416
		{
417
			$useArgs = !is_array($parameters) && is_array($args);
418
			return new TSqlCriteria($criteria,$useArgs ? $args : $parameters);
419
		}
420
		else if($criteria instanceof TSqlCriteria)
421
			return $criteria;
422
		else
423
			throw new TDbException('dbtablegateway_invalid_criteria');
424
	}
425
 
426
	/**
427
	 * Dynamic find method using parts of method name as search criteria.
428
	 * Method name starting with "findBy" only returns 1 record.
429
	 * Method name starting with "findAllBy" returns 0 or more records.
430
	 * Method name starting with "deleteBy" deletes records by the trail criteria.
431
	 * The condition is taken as part of the method name after "findBy", "findAllBy"
432
	 * or "deleteBy".
433
	 *
434
	 * The following are equivalent:
435
	 * <code>
436
	 * $table->findByName($name)
437
	 * $table->find('Name = ?', $name);
438
	 * </code>
439
	 * <code>
440
	 * $table->findByUsernameAndPassword($name,$pass); // OR may be used
441
	 * $table->findBy_Username_And_Password($name,$pass); // _OR_ may be used
442
	 * $table->find('Username = ? AND Password = ?', $name, $pass);
443
	 * </code>
444
	 * <code>
445
	 * $table->findAllByAge($age);
446
	 * $table->findAll('Age = ?', $age);
447
	 * </code>
448
	 * <code>
449
	 * $table->deleteAll('Name = ?', $name);
450
	 * $table->deleteByName($name);
451
	 * </code>
452
	 * @return mixed single record if method name starts with "findBy", 0 or more records
453
	 * if method name starts with "findAllBy"
454
	 */
455
	public function __call($method,$args)
456
	{
457
		$delete =false;
458
		if($findOne = substr(strtolower($method),0,6)==='findby')
459
			$condition = $method[6]==='_' ? substr($method,7) : substr($method,6);
460
		else if(substr(strtolower($method),0,9)==='findallby')
461
			$condition = $method[9]==='_' ? substr($method,10) : substr($method,9);
462
		else if($delete = substr(strtolower($method),0,8)==='deleteby')
463
			$condition = $method[8]==='_' ? substr($method,9) : substr($method,8);
464
		else if($delete = substr(strtolower($method),0,11)==='deleteallby')
465
			$condition = $method[11]==='_' ? substr($method,12) : substr($method,11);
466
		else
467
			return null;
468
 
469
		$criteria = $this->getCommand()->createCriteriaFromString($method, $condition, $args);
470
		if($delete)
471
			return $this->deleteAll($criteria);
472
		else
473
			return $findOne ? $this->find($criteria) : $this->findAll($criteria);
474
	}
475
}
476