Subversion-Projekte lars-tiefland.prado

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/**
3
 * TDbCommandBuilder 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: TDbCommandBuilder.php 2488 2008-08-06 01:34:06Z knut $
10
 * @package System.Data.Common
11
 */
12
 
13
/**
14
 * TDbCommandBuilder provides basic methods to create query commands for tables
15
 * giving by {@link setTableInfo TableInfo} the property.
16
 *
17
 * @author Wei Zhuo <weizho[at]gmail[dot]com>
18
 * @version $Id: TDbCommandBuilder.php 2488 2008-08-06 01:34:06Z knut $
19
 * @package System.Data.Common
20
 * @since 3.1
21
 */
22
class TDbCommandBuilder extends TComponent
23
{
24
	private $_connection;
25
	private $_tableInfo;
26
 
27
	/**
28
	 * @param TDbConnection database connection.
29
	 * @param TDbTableInfo table information.
30
	 */
31
	public function __construct($connection=null, $tableInfo=null)
32
	{
33
		$this->setDbConnection($connection);
34
		$this->setTableInfo($tableInfo);
35
	}
36
 
37
	/**
38
	 * @return TDbConnection database connection.
39
	 */
40
	public function getDbConnection()
41
	{
42
		return $this->_connection;
43
	}
44
 
45
	/**
46
	 * @param TDbConnection database connection.
47
	 */
48
	public function setDbConnection($value)
49
	{
50
		$this->_connection=$value;
51
	}
52
 
53
	/**
54
	 * @param TDbTableInfo table information.
55
	 */
56
	public function setTableInfo($value)
57
	{
58
		$this->_tableInfo=$value;
59
	}
60
 
61
	/**
62
	 * @param TDbTableInfo table information.
63
	 */
64
	public function getTableInfo()
65
	{
66
		return $this->_tableInfo;
67
	}
68
 
69
	/**
70
	 * Iterate through all the columns and returns the last insert id of the
71
	 * first column that has a sequence or serial.
72
	 * @return mixed last insert id, null if none is found.
73
	 */
74
	public function getLastInsertID()
75
	{
76
		foreach($this->getTableInfo()->getColumns() as $column)
77
		{
78
			if($column->hasSequence())
79
				return $this->getDbConnection()->getLastInsertID($column->getSequenceName());
80
		}
81
	}
82
 
83
	/**
84
	 * Alters the sql to apply $limit and $offset. Default implementation is applicable
85
	 * for PostgreSQL, MySQL and SQLite.
86
	 * @param string SQL query string.
87
	 * @param integer maximum number of rows, -1 to ignore limit.
88
	 * @param integer row offset, -1 to ignore offset.
89
	 * @return string SQL with limit and offset.
90
	 */
91
	public function applyLimitOffset($sql, $limit=-1, $offset=-1)
92
	{
93
		$limit = $limit!==null ? (int)$limit : -1;
94
		$offset = $offset!==null ? (int)$offset : -1;
95
		$limitStr = $limit >= 0 ? ' LIMIT '.$limit : '';
96
		$offsetStr = $offset >= 0 ? ' OFFSET '.$offset : '';
97
		return $sql.$limitStr.$offsetStr;
98
	}
99
 
100
	/**
101
	 * @param string SQL string without existing ordering.
102
	 * @param array pairs of column names as key and direction as value.
103
	 * @return string modified SQL applied with ORDER BY.
104
	 */
105
	public function applyOrdering($sql, $ordering)
106
	{
107
		$orders=array();
108
		foreach($ordering as $name => $direction)
109
		{
110
			$direction = strtolower($direction) == 'desc' ? 'DESC' : 'ASC';
111
			if(strpos($name, '(') && strpos($name, ')')) {
112
				// key is a function (bad practice, but we need to handle it)
113
				$key = $name;
114
			} else {
115
				// key is a column
116
				$key = $this->getTableInfo()->getColumn($name)->getColumnName();
117
			}
118
			$orders[] = $key.' '.$direction;
119
		}
120
		if(count($orders) > 0)
121
			$sql .= ' ORDER BY '.implode(', ', $orders);
122
		return $sql;
123
	}
124
 
125
	/**
126
	 * Computes the SQL condition for search a set of column using regular expression
127
	 * (or LIKE, depending on database implementation) to match a string of
128
	 * keywords (default matches all keywords).
129
	 * @param array list of column id for potential search condition.
130
	 * @param string string of keywords
131
	 * @return string SQL search condition matching on a set of columns.
132
	 */
133
	public function getSearchExpression($fields, $keywords)
134
	{
135
		if(strlen(trim($keywords)) == 0) return '';
136
		$words = preg_split('/\s/u', $keywords);
137
		$conditions = array();
138
		foreach($fields as $field)
139
		{
140
			$column = $this->getTableInfo()->getColumn($field)->getColumnName();
141
			$conditions[] = $this->getSearchCondition($column, $words);
142
		}
143
		return '('.implode(' OR ', $conditions).')';
144
	}
145
 
146
	/**
147
	 * @param string column name.
148
	 * @param array keywords
149
	 * @return string search condition for all words in one column.
150
	 */
151
	protected function getSearchCondition($column, $words)
152
	{
153
		$conditions=array();
154
		foreach($words as $word)
155
			$conditions[] = $column.' LIKE '.$this->getDbConnection()->quoteString('%'.$word.'%');
156
		return '('.implode(' AND ', $conditions).')';
157
	}
158
 
159
	/**
160
	 * Appends the $where condition to the string "SELECT * FROM tableName WHERE ".
161
	 * The tableName is obtained from the {@link setTableInfo TableInfo} property.
162
	 * @param string query condition
163
	 * @param array condition parameters.
164
	 * @return TDbCommand query command.
165
	 */
166
	public function createFindCommand($where='1=1', $parameters=array(), $ordering=array(), $limit=-1, $offset=-1)
167
	{
168
		$table = $this->getTableInfo()->getTableFullName();
169
		$sql = "SELECT * FROM {$table}";
170
		if(!empty($where))
171
			$sql .= " WHERE {$where}";
172
		return $this->applyCriterias($sql, $parameters, $ordering, $limit, $offset);
173
	}
174
 
175
	public function applyCriterias($sql, $parameters=array(),$ordering=array(), $limit=-1, $offset=-1)
176
	{
177
		if(count($ordering) > 0)
178
			$sql = $this->applyOrdering($sql, $ordering);
179
		if($limit>=0 || $offset>=0)
180
			$sql = $this->applyLimitOffset($sql, $limit, $offset);
181
		$command = $this->createCommand($sql);
182
		$this->bindArrayValues($command, $parameters);
183
		return $command;
184
	}
185
 
186
	/**
187
	 * Creates a count(*) command for the table described in {@link setTableInfo TableInfo}.
188
	 * @param string count condition.
189
	 * @param array binding parameters.
190
	 * @return TDbCommand count command.
191
	 */
192
	public function createCountCommand($where='1=1', $parameters=array(),$ordering=array(), $limit=-1, $offset=-1)
193
	{
194
		$table = $this->getTableInfo()->getTableFullName();
195
		$sql = "SELECT COUNT(*) FROM {$table}";
196
		if(!empty($where))
197
			$sql .= " WHERE {$where}";
198
		return $this->applyCriterias($sql, $parameters, $ordering, $limit, $offset);
199
	}
200
 
201
	/**
202
	 * Creates a delete command for the table described in {@link setTableInfo TableInfo}.
203
	 * The conditions for delete is given by the $where argument and the parameters
204
	 * for the condition is given by $parameters.
205
	 * @param string delete condition.
206
	 * @param array delete parameters.
207
	 * @return TDbCommand delete command.
208
	 */
209
	public function createDeleteCommand($where,$parameters=array())
210
	{
211
		$table = $this->getTableInfo()->getTableFullName();
212
		if (!empty($where))
213
			$where = ' WHERE '.$where;
214
		$command = $this->createCommand("DELETE FROM {$table}".$where);
215
		$this->bindArrayValues($command, $parameters);
216
		return $command;
217
	}
218
 
219
	/**
220
	 * Creates an insert command for the table described in {@link setTableInfo TableInfo} for the given data.
221
	 * Each array key in the $data array must correspond to the column name of the table
222
	 * (if a column allows to be null, it may be omitted) to be inserted with
223
	 * the corresponding array value.
224
	 * @param array name-value pairs of new data to be inserted.
225
	 * @return TDbCommand insert command
226
	 */
227
	public function createInsertCommand($data)
228
	{
229
		$table = $this->getTableInfo()->getTableFullName();
230
		list($fields, $bindings) = $this->getInsertFieldBindings($data);
231
		$command = $this->createCommand("INSERT INTO {$table}({$fields}) VALUES ($bindings)");
232
		$this->bindColumnValues($command, $data);
233
		return $command;
234
	}
235
 
236
	/**
237
	 * Creates an update command for the table described in {@link setTableInfo TableInfo} for the given data.
238
	 * Each array key in the $data array must correspond to the column name to be updated with the corresponding array value.
239
	 * @param array name-value pairs of data to be updated.
240
	 * @param string update condition.
241
	 * @param array update parameters.
242
	 * @return TDbCommand update command.
243
	 */
244
	public function createUpdateCommand($data, $where, $parameters=array())
245
	{
246
		$table = $this->getTableInfo()->getTableFullName();
247
		if($this->hasIntegerKey($parameters))
248
			$fields = implode(', ', $this->getColumnBindings($data, true));
249
		else
250
			$fields = implode(', ', $this->getColumnBindings($data));
251
 
252
		if (!empty($where))
253
			$where = ' WHERE '.$where;
254
		$command = $this->createCommand("UPDATE {$table} SET {$fields}".$where);
255
		$this->bindArrayValues($command, array_merge($data, $parameters));
256
		return $command;
257
	}
258
 
259
	/**
260
	 * Returns a list of insert field name and a list of binding names.
261
	 * @param object array or object to be inserted.
262
	 * @return array tuple ($fields, $bindings)
263
	 */
264
	protected function getInsertFieldBindings($values)
265
	{
266
		$fields = array(); $bindings=array();
267
		foreach(array_keys($values) as $name)
268
		{
269
			$fields[] = $this->getTableInfo()->getColumn($name)->getColumnName();
270
			$bindings[] = ':'.$name;
271
		}
272
		return array(implode(', ',$fields), implode(', ', $bindings));
273
	}
274
 
275
	/**
276
	 * Create a name-value or position-value if $position=true binding strings.
277
	 * @param array data for binding.
278
	 * @param boolean true to bind as position values.
279
	 * @return string update column names with corresponding binding substrings.
280
	 */
281
	protected function getColumnBindings($values, $position=false)
282
	{
283
		$bindings=array();
284
		foreach(array_keys($values) as $name)
285
		{
286
			$column = $this->getTableInfo()->getColumn($name)->getColumnName();
287
			$bindings[] = $position ? $column.' = ?' : $column.' = :'.$name;
288
		}
289
		return $bindings;
290
	}
291
 
292
	/**
293
	 * @param string SQL query string.
294
	 * @return TDbCommand corresponding database command.
295
	 */
296
	public function createCommand($sql)
297
	{
298
		$this->getDbConnection()->setActive(true);
299
		return $this->getDbConnection()->createCommand($sql);
300
	}
301
 
302
	/**
303
	 * Bind the name-value pairs of $values where the array keys correspond to column names.
304
	 * @param TDbCommand database command.
305
	 * @param array name-value pairs.
306
	 */
307
	public function bindColumnValues($command, $values)
308
	{
309
		foreach($values as $name=>$value)
310
		{
311
			$column = $this->getTableInfo()->getColumn($name);
312
			if($value === null && $column->getAllowNull())
313
				$command->bindValue(':'.$name, null, PDO::PARAM_NULL);
314
			else
315
				$command->bindValue(':'.$name, $value, $column->getPdoType());
316
		}
317
	}
318
 
319
	/**
320
	 * @param TDbCommand database command
321
	 * @param array values for binding.
322
	 */
323
	public function bindArrayValues($command, $values)
324
	{
325
		if($this->hasIntegerKey($values))
326
		{
327
			$values = array_values($values);
328
			for($i = 0, $max=count($values); $i<$max; $i++)
329
				$command->bindValue($i+1, $values[$i], $this->getPdoType($values[$i]));
330
		}
331
		else
332
		{
333
			foreach($values as $name=>$value)
334
			{
335
				$prop = $name[0]===':' ? $name : ':'.$name;
336
				$command->bindValue($prop, $value, $this->getPdoType($value));
337
			}
338
		}
339
	}
340
 
341
	/**
342
	 * @param mixed PHP value
343
	 * @return integer PDO parameter types.
344
	 */
345
	public static function getPdoType($value)
346
	{
347
		switch(gettype($value))
348
		{
349
			case 'boolean': return PDO::PARAM_BOOL;
350
			case 'integer': return PDO::PARAM_INT;
351
			case 'string' : return PDO::PARAM_STR;
352
			case 'NULL'   : return PDO::PARAM_NULL;
353
		}
354
	}
355
 
356
	/**
357
	 * @param array
358
	 * @return boolean true if any array key is an integer.
359
	 */
360
	protected function hasIntegerKey($array)
361
	{
362
		foreach($array as $k=>$v)
363
		{
364
			if(gettype($k)==='integer')
365
				return true;
366
		}
367
		return false;
368
	}
369
}
370
 
371
?>