1: <?php
2: /**
3: * Copyright 2007 Maintainable Software, LLC
4: * Copyright 2008-2012 Horde LLC (http://www.horde.org/)
5: *
6: * @author Mike Naberezny <mike@maintainable.com>
7: * @author Derek DeVries <derek@maintainable.com>
8: * @author Chuck Hagenbuch <chuck@horde.org>
9: * @license http://www.horde.org/licenses/bsd
10: * @category Horde
11: * @package Db
12: * @subpackage Adapter
13: */
14:
15: /**
16: * @author Mike Naberezny <mike@maintainable.com>
17: * @author Derek DeVries <derek@maintainable.com>
18: * @author Chuck Hagenbuch <chuck@horde.org>
19: * @license http://www.horde.org/licenses/bsd
20: * @category Horde
21: * @package Db
22: * @subpackage Adapter
23: */
24: class Horde_Db_Adapter_Base_TableDefinition implements ArrayAccess, IteratorAggregate
25: {
26: protected $_name = null;
27: protected $_base = null;
28: protected $_options = null;
29: protected $_columns = null;
30: protected $_primaryKey = null;
31:
32: protected $_columntypes = array('string', 'text', 'integer', 'float',
33: 'datetime', 'timestamp', 'time', 'date', 'binary', 'boolean');
34:
35: /**
36: * Constructor.
37: *
38: * @param string $name
39: * @param Horde_Db_Adapter_Base_Schema $base
40: * @param array $options
41: */
42: public function __construct($name, $base, $options = array())
43: {
44: $this->_name = $name;
45: $this->_base = $base;
46: $this->_options = $options;
47: $this->_columns = array();
48: }
49:
50: /**
51: * @return string
52: */
53: public function getName()
54: {
55: return $this->_name;
56: }
57:
58: /**
59: * @return array
60: */
61: public function getOptions()
62: {
63: return $this->_options;
64: }
65:
66: /**
67: * @param string $name
68: */
69: public function primaryKey($name)
70: {
71: if (is_scalar($name) && $name !== false) {
72: $natives = $this->_native();
73: $this->column($name, $natives['autoincrementKey']);
74: }
75:
76: $this->_primaryKey = $name;
77: }
78:
79: /**
80: * Adds a new column to the table definition.
81: *
82: * Examples:
83: * <code>
84: * // Assuming $def is an instance of Horde_Db_Adapter_Base_TableDefinition
85: *
86: * $def->column('granted', 'boolean');
87: * // => granted BOOLEAN
88: *
89: * $def->column('picture', 'binary', 'limit' => 4096);
90: * // => picture BLOB(4096)
91: *
92: * $def->column('sales_stage', 'string', array('limit' => 20, 'default' => 'new', 'null' => false));
93: * // => sales_stage VARCHAR(20) DEFAULT 'new' NOT NULL
94: * </code>
95: *
96: * @param string $type Column type, one of:
97: * autoincrementKey, string, text, integer, float,
98: * datetime, timestamp, time, date, binary, boolean.
99: * @param array $options Column options:
100: * - limit: (integer) Maximum column length (string,
101: * text, binary or integer columns only)
102: * - default: (mixed) The column's default value.
103: * You cannot explicitly set the default value to
104: * NULL. Simply leave off this option if you want
105: * a NULL default value.
106: * - null: (boolean) Whether NULL values are allowed
107: * in the column.
108: * - precision: (integer) The number precision
109: * (float columns only).
110: * - scale: (integer) The number scaling (float
111: * columns only).
112: * - unsigned: (boolean) Whether the column is an
113: * unsigned number (integer columns only).
114: * - autoincrement: (boolean) Whether the column is
115: * an autoincrement column. Restrictions are
116: * RDMS specific.
117: *
118: * @return Horde_Db_Adapter_Base_TableDefinition This object.
119: */
120: public function column($name, $type, $options = array())
121: {
122: $options = array_merge(
123: array('limit' => null,
124: 'precision' => null,
125: 'scale' => null,
126: 'unsigned' => null,
127: 'default' => null,
128: 'null' => null,
129: 'autoincrement' => null),
130: $options);
131:
132: $column = $this->_base->makeColumnDefinition($this->_base, $name, $type);
133: $column->setLimit($options['limit']);
134: $column->setPrecision($options['precision']);
135: $column->setScale($options['scale']);
136: $column->setUnsigned($options['unsigned']);
137: $column->setDefault($options['default']);
138: $column->setNull($options['null']);
139: $column->setAutoIncrement($options['autoincrement']);
140:
141: $this[$name] ? $this[$name] = $column : $this->_columns[] = $column;
142:
143: return $this;
144: }
145:
146: /**
147: * Adds created_at and updated_at columns to the table.
148: */
149: public function timestamps()
150: {
151: return $this->column('created_at', 'datetime')
152: ->column('updated_at', 'datetime');
153: }
154:
155: /**
156: * Add one or several references to foreign keys
157: *
158: * This method returns self.
159: */
160: public function belongsTo($columns)
161: {
162: if (!is_array($columns)) { $columns = array($columns); }
163: foreach ($columns as $col) {
164: $this->column($col . '_id', 'integer');
165: }
166:
167: return $this;
168: }
169:
170: /**
171: * Alias for the belongsTo() method
172: *
173: * This method returns self.
174: */
175: public function references($columns)
176: {
177: return $this->belongsTo($columns);
178: }
179:
180: /**
181: * Use __call to provide shorthand column creation ($this->integer(), etc.)
182: */
183: public function __call($method, $arguments)
184: {
185: if (!in_array($method, $this->_columntypes)) {
186: throw new BadMethodCallException('Call to undeclared method "' . $method . '"');
187: }
188: if (count($arguments) > 0 && count($arguments) < 3) {
189: return $this->column($arguments[0], $method,
190: isset($arguments[1]) ? $arguments[1] : array());
191: }
192: throw new BadMethodCallException('Method "'.$method.'" takes two arguments');
193: }
194:
195: /**
196: * Wrap up table creation block & create the table
197: */
198: public function end()
199: {
200: return $this->_base->endTable($this);
201: }
202:
203: /**
204: * Returns a String whose contents are the column definitions
205: * concatenated together. This string can then be pre and appended to
206: * to generate the final SQL to create the table.
207: *
208: * @return string
209: */
210: public function toSql()
211: {
212: $cols = array();
213: foreach ($this->_columns as $col) {
214: $cols[] = $col->toSql();
215: }
216: $sql = ' ' . implode(", \n ", $cols);
217:
218: // Specify composite primary keys as well
219: if (is_array($this->_primaryKey)) {
220: $pk = array();
221: foreach ($this->_primaryKey as $pkColumn) {
222: $pk[] = $this->_base->quoteColumnName($pkColumn);
223: }
224: $sql .= ", \n PRIMARY KEY(" . implode(', ', $pk) . ')';
225: }
226:
227: return $sql;
228: }
229:
230: public function __toString()
231: {
232: return $this->toSql();
233: }
234:
235:
236: /*##########################################################################
237: # ArrayAccess
238: ##########################################################################*/
239:
240: /**
241: * ArrayAccess: Check if the given offset exists
242: *
243: * @param int $offset
244: * @return boolean
245: */
246: public function offsetExists($offset)
247: {
248: foreach ($this->_columns as $column) {
249: if ($column->getName() == $offset) return true;
250: }
251: return false;
252: }
253:
254: /**
255: * ArrayAccess: Return the value for the given offset.
256: *
257: * @param int $offset
258: * @return object {@link {@Horde_Db_Adapter_Base_ColumnDefinition}
259: */
260: public function offsetGet($offset)
261: {
262: if (!$this->offsetExists($offset)) {
263: return null;
264: }
265:
266: foreach ($this->_columns as $column) {
267: if ($column->getName() == $offset) {
268: return $column;
269: }
270: }
271: }
272:
273: /**
274: * ArrayAccess: Set value for given offset
275: *
276: * @param int $offset
277: * @param mixed $value
278: */
279: public function offsetSet($offset, $value)
280: {
281: foreach ($this->_columns as $key=>$column) {
282: if ($column->getName() == $offset) {
283: $this->_columns[$key] = $value;
284: }
285: }
286: }
287:
288: /**
289: * ArrayAccess: remove element
290: *
291: * @param int $offset
292: */
293: public function offsetUnset($offset)
294: {
295: foreach ($this->_columns as $key=>$column) {
296: if ($column->getName() == $offset) {
297: unset($this->_columns[$key]);
298: }
299: }
300: }
301:
302:
303: /*##########################################################################
304: # IteratorAggregate
305: ##########################################################################*/
306:
307: public function getIterator()
308: {
309: return new ArrayIterator($this->_columns);
310: }
311:
312:
313: /*##########################################################################
314: # Protected
315: ##########################################################################*/
316:
317: /**
318: * Get the types
319: */
320: protected function _native()
321: {
322: return $this->_base->nativeDatabaseTypes();
323: }
324:
325: }
326: