# Source for file Table.php

Documentation is available at Table.php

1. <?php
2. /*
3. * $Id: Table.php 2279 2007-08-27 15:04:32Z Jonathan.Wage $
4. *
5. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
6. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
7. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
8. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
9. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
10. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
11. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
12. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
13. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
14. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
15. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16. *
17. * This software consists of voluntary contributions made by many individuals
18. * and is licensed under the LGPL. For more information, see
19. * <http://www.phpdoctrine.com>.
20. */
21. /**
22. * Doctrine_Table represents a database table
23. * each Doctrine_Table holds the information of foreignKeys and associations
24. *
25. *
26. * @author Konsta Vesterinen <kvesteri@cc.hut.fi>
27. * @package Doctrine
28. * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
29. * @version $Revision: 2279 $
30. * @category Object Relational Mapping
31. * @link www.phpdoctrine.com
32. * @since 1.0
33. */
34. class Doctrine_Table extends Doctrine_Configurable implements Countable
35. {
36. /**
37. * @var array $data temporary data which is then loaded into Doctrine_Record::$data
38. */
39. private $data = array();
40. /**
41. * @var array $primaryKeys an array containing all primary key column names
42. */
43. private $primaryKeys = array();
44. /**
45. * @var mixed $identifier
46. */
47. private $identifier;
48. /**
49. * @see Doctrine_Identifier constants
50. * @var integer $identifierType the type of identifier this table uses
51. */
52. private $identifierType;
53. /**
54. * @var Doctrine_Connection $conn Doctrine_Connection object that created this table
55. */
56. private $conn;
57. /**
58. * @var array $identityMap first level cache
59. */
60. private $identityMap = array();
61. /**
62. * @var Doctrine_Table_Repository $repository record repository
63. */
64. private $repository;
65. /**
66. * @var array $columns an array of column definitions,
67. * keys as column names and values as column definitions
68. *
69. * the definition array has atleast the following values:
70. *
71. * -- type the column type, eg. 'integer'
72. * -- length the column length, eg. 11
73. *
74. * additional keys:
75. * -- notnull whether or not the column is marked as notnull
76. * -- values enum values
77. * -- notblank notblank validator + notnull constraint
78. * ... many more
79. */
80. protected $columns = array();
81. /**
82. * @var array $columnAliases an array of column aliases
83. * keys as column aliases and values as column names
84. */
85. protected $columnAliases = array();
86. /**
87. * @var integer $columnCount cached column count, Doctrine_Record uses this column count in when
88. * determining its state
89. */
90. private $columnCount;
91. /**
92. * @var boolean $hasDefaultValues whether or not this table has default values
93. */
94. private $hasDefaultValues;
95. /**
96. * @var array $options an array containing all options
97. *
98. * -- name name of the component, for example component name of the GroupTable is 'Group'
99. *
100. * -- parents the parent classes of this component
101. *
102. * -- declaringClass name of the table definition declaring class (when using inheritance the class
103. * that defines the table structure can be any class in the inheritance hierarchy,
104. * hence we need reflection to check out which class actually calls setTableDefinition)
105. *
106. * -- tableName database table name, in most cases this is the same as component name but in some cases
107. * where one-table-multi-class inheritance is used this will be the name of the inherited table
108. *
109. * -- sequenceName Some databases need sequences instead of auto incrementation primary keys,
110. * you can set specific sequence for your table by calling setOption('sequenceName', $seqName)
111. * where $seqName is the name of the desired sequence
112. *
113. * -- enumMap enum value arrays
114. *
115. * -- inheritanceMap inheritanceMap is used for inheritance mapping, keys representing columns and values
116. * the column values that should correspond to child classes
117. *
118. * -- type table type (mysql example: INNODB)
119. *
120. * -- charset character set
121. *
122. * -- foreignKeys the foreign keys of this table
123. *
124. * -- checks the check constraints of this table, eg. 'price > dicounted_price'
125. *
126. * -- collation collation attribute
127. *
128. * -- indexes the index definitions of this table
129. *
130. * -- treeImpl the tree implementation of this table (if any)
131. *
132. * -- treeOptions the tree options
133. *
134. * -- versioning
135. */
136. protected $options = array('name' => null,
137. 'tableName' => null,
138. 'sequenceName' => null,
139. 'inheritanceMap' => array(),
140. 'enumMap' => array(),
141. 'engine' => null,
142. 'charset' => null,
143. 'collation' => null,
144. 'treeImpl' => null,
145. 'treeOptions' => null,
146. 'indexes' => array(),
147. 'parents' => array(),
148. 'versioning' => null,
149. );
150. /**
151. * @var Doctrine_Tree $tree tree object associated with this table
152. */
153. protected $tree;
154. /**
155. * @var Doctrine_Relation_Parser $_parser relation parser object
156. */
157. protected $_parser;
158. /**
159. * @var array $_templates an array containing all templates attached to this table
160. */
161. protected $_templates;
164. /**
165. * the constructor
166. * @throws Doctrine_Connection_Exception if there are no opened connections
167. * @throws Doctrine_Table_Exception if there is already an instance of this table
168. * @return void
169. */
170. public function __construct($name, Doctrine_Connection $conn)
171. {
172. $this->conn = $conn;
174. $this->setParent($this->conn);
176. $this->options['name'] = $name;
177. $this->_parser = new Doctrine_Relation_Parser($this);
179. if ( ! class_exists($name) || empty($name)) {
180. throw new Doctrine_Exception("Couldn't find class " . $name);
181. }
182. $record = new $name($this);
184. $names = array();
186. $class = $name;
188. // get parent classes
190. do {
191. if ($class == "Doctrine_Record") {
192. break;
193. }
195. $name = $class;
196. $names[] = $name;
197. } while ($class = get_parent_class($class));
199. // reverse names
200. $names = array_reverse($names);
201. // save parents
202. array_pop($names);
203. $this->options['parents'] = $names;
205. // create database table
206. if (method_exists($record, 'setTableDefinition')) {
207. $record->setTableDefinition();
208. // get the declaring class of setTableDefinition method
209. $method = new ReflectionMethod($this->options['name'], 'setTableDefinition');
210. $class = $method->getDeclaringClass();
211. } else {
212. $class = new ReflectionClass($class);
213. }
214. $this->options['declaringClass'] = $class;
216. // set the table definition for the given tree implementation
217. if ($this->isTree()) {
218. $this->getTree()->setTableDefinition();
219. }
221. $this->columnCount = count($this->columns);
223. if ( ! isset($this->options['tableName'])) {
224. $this->options['tableName'] = Doctrine::tableize($class->getName());
225. }
227. switch (count($this->primaryKeys)) {
228. case 0:
229. $this->columns = array_merge(array('id' =>
230. array('type' => 'integer',
231. 'length' => 20,
232. 'autoincrement' => true,
233. 'primary' => true)), $this->columns);
235. $this->primaryKeys[] = 'id';
236. $this->identifier = 'id';
237. $this->identifierType = Doctrine::IDENTIFIER_AUTOINC;
238. $this->columnCount++;
239. break;
240. default:
241. if (count($this->primaryKeys) > 1) {
242. $this->identifier = $this->primaryKeys;
243. $this->identifierType = Doctrine::IDENTIFIER_COMPOSITE;
245. } else {
246. foreach ($this->primaryKeys as $pk) {
247. $e = $this->columns[$pk];
249. $found = false;
251. foreach ($e as $option => $value) {
252. if ($found)
253. break;
255. $e2 = explode(':', $option);
257. switch (strtolower($e2[0])) {
258. case 'autoincrement':
259. case 'autoinc':
260. $this->identifierType = Doctrine::IDENTIFIER_AUTOINC;
261. $found = true;
262. break;
263. case 'seq':
264. case 'sequence':
265. $this->identifierType = Doctrine::IDENTIFIER_SEQUENCE;
266. $found = true;
268. if ($value) {
269. $this->options['sequenceName'] = $value;
270. } else {
271. if (($sequence = $this->getAttribute(Doctrine::ATTR_DEFAULT_SEQUENCE)) !== null) {
272. $this->options['sequenceName'] = $sequence;
273. } else {
274. $this->options['sequenceName'] = $this->conn->getSequenceName($this->options['tableName']);
275. }
276. }
277. break;
278. }
279. }
280. if ( ! isset($this->identifierType)) {
281. $this->identifierType = Doctrine::IDENTIFIER_NATURAL;
282. }
283. $this->identifier = $pk;
284. }
285. }
286. }
289. $record->setUp();
291. // if tree, set up tree
292. if ($this->isTree()) {
293. $this->getTree()->setUp();
294. }
295. $this->repository = new Doctrine_Table_Repository($this);
296. }
297. /**
298. * getTemplates
299. * returns all templates attached to this table
300. *
301. * @return array an array containing all templates
302. */
303. public function getTemplates()
304. {
305. return $this->_templates;
306. }
307. /**
308. * export
309. * exports this table to database based on column and option definitions
310. *
311. * @throws Doctrine_Connection_Exception if some error other than Doctrine::ERR_ALREADY_EXISTS
312. * occurred during the create table operation
313. * @return boolean whether or not the export operation was successful
314. * false if table already existed in the database
315. */
316. public function export()
317. {
318. $this->conn->export->exportTable($this);
319. }
320. /**
321. * getExportableFormat
322. * returns exportable presentation of this object
323. *
324. * @return array
325. */
326. public function getExportableFormat($parseForeignKeys = true)
327. {
328. $columns = array();
329. $primary = array();
331. foreach ($this->getColumns() as $name => $column) {
332. $definition = $column;
334. switch ($definition['type']) {
335. case 'enum':
336. if (isset($definition['default'])) {
337. $definition['default'] = $this->enumIndex($name, $definition['default']);
338. }
339. break;
340. case 'boolean':
341. if (isset($definition['default'])) {
342. $definition['default'] = $this->getConnection()->convertBooleans($definition['default']);
343. }
344. break;
345. }
346. $columns[$name] = $definition;
348. if(isset($definition['primary']) && $definition['primary']) {
349. $primary[] = $name;
350. }
351. }
352. $options['foreignKeys'] = array();
354. if ($parseForeignKeys) {
355. if ($this->getAttribute(Doctrine::ATTR_EXPORT) & Doctrine::EXPORT_CONSTRAINTS) {
357. $constraints = array();
359. $emptyIntegrity = array('onUpdate' => null,
360. 'onDelete' => null);
362. foreach ($this->getRelations() as $name => $relation) {
363. $fk = $relation->toArray();
364. $fk['foreignTable'] = $relation->getTable()->getTableName();
366. if ($relation->getTable() === $this && in_array($relation->getLocal(), $primary)) {
367. if ($relation->hasConstraint()) {
368. throw new Doctrine_Table_Exception("Badly constructed integrity constraints.");
369. }
371. continue;
372. }
374. $integrity = array('onUpdate' => $fk['onUpdate'],
375. 'onDelete' => $fk['onDelete']);
377. if ($relation instanceof Doctrine_Relation_LocalKey) {
378. $def = array('local' => $relation->getLocal(),
379. 'foreign' => $relation->getForeign(),
380. 'foreignTable' => $relation->getTable()->getTableName());
382. if (($key = array_search($def, $options['foreignKeys'])) === false) {
383. $options['foreignKeys'][] = $def;
385. $constraints[] = $integrity;
386. } else {
387. if ($integrity !== $emptyIntegrity) {
388. $constraints[$key] = $integrity;
389. }
390. }
391. }
392. }
394. foreach ($constraints as $k => $def) {
395. $options['foreignKeys'][$k] = array_merge($options['foreignKeys'][$k], $def);
396. }
398. }
399. }
400. $options['primary'] = $primary;
402. return array('tableName' => $this->getOption('tableName'),
403. 'columns' => $columns,
404. 'options' => array_merge($this->getOptions(), $options));
405. }
406. /**
407. * exportConstraints
408. * exports the constraints of this table into database based on option definitions
409. *
410. * @throws Doctrine_Connection_Exception if something went wrong on db level
411. * @return void
412. */
413. public function exportConstraints()
414. {
415. try {
416. $this->conn->beginTransaction();
418. foreach ($this->options['index'] as $index => $definition) {
419. $this->conn->export->createIndex($this->options['tableName'], $index, $definition);
420. }
421. $this->conn->commit();
422. } catch(Doctrine_Connection_Exception $e) {
423. $this->conn->rollback();
425. throw $e;
426. }
427. }
428. /**
429. * getRelationParser
430. * return the relation parser associated with this table
431. *
432. * @return Doctrine_Relation_Parser relation parser object
433. */
434. public function getRelationParser()
435. {
436. return $this->_parser;
437. }
438. /**
439. * __get
440. * an alias for getOption
441. *
442. * @param string $option
443. */
444. public function __get($option)
445. {
446. if (isset($this->options[$option])) {
447. return $this->options[$option];
448. }
449. return null;
450. }
451. /**
452. * __isset
453. *
454. * @param string $option
455. */
456. public function __isset($option)
457. {
458. return isset($this->options[$option]);
459. }
460. /**
461. * getOptions
462. * returns all options of this table and the associated values
463. *
464. * @return array all options and their values
465. */
466. public function getOptions()
467. {
468. return $this->options;
469. }
470. /**
471. * addForeignKey
472. *
473. * adds a foreignKey to this table
474. *
475. * @return void
476. */
477. public function addForeignKey(array $definition)
478. {
479. $this->options['foreignKeys'][] = $definition;
480. }
481. /**
482. /**
483. * addCheckConstraint
484. *
485. * adds a check constraint to this table
486. *
487. * @return void
488. */
489. public function addCheckConstraint($definition, $name)
490. {
491. if (is_string($name)) {
492. $this->options['checks'][$name] = $definition;
493. } else {
494. $this->options['checks'][] = $definition;
495. }
497. return $this;
498. }
499. /**
500. * addIndex
501. *
502. * adds an index to this table
503. *
504. * @return void
505. */
506. public function addIndex($index, array $definition)
507. {
508. $this->options['indexes'][$index] = $definition;
509. }
510. /**
511. /**
512. * getIndex
513. *
514. * @return array|boolean array on success, FALSE on failure
515. */
516. public function getIndex($index)
517. {
518. if (isset($this->options['indexes'][$index])) {
519. return $this->options['indexes'][$index];
520. }
522. return false;
523. }
524. public function bind($args, $type)
525. {
526. $options = array();
527. $options['type'] = $type;
529. if ( ! isset($args[1])) {
530. $args[1] = array();
531. }
533. // the following is needed for backwards compatibility
534. if (is_string($args[1])) {
535. if ( ! isset($args[2])) {
536. $args[2] = array();
537. } elseif (is_string($args[2])) {
538. $args[2] = (array) $args[2];
539. }
541. $classes = array_merge($this->options['parents'], array($this->getComponentName()));
544. $e = explode('.', $args[1]);
545. if (in_array($e[0], $classes)) {
546. if ($options['type'] >= Doctrine_Relation::MANY) {
547. $options['foreign'] = $e[1];
548. } else {
549. $options['local'] = $e[1];
550. }
551. } else {
552. $e2 = explode(' as ', $args[0]);
553. if ($e[0] !== $e2[0] && ( ! isset($e2[1]) || $e[0] !== $e2[1])) {
554. $options['refClass'] = $e[0];
555. }
557. $options['foreign'] = $e[1];
558. }
560. $options = array_merge($args[2], $options);
562. $this->_parser->bind($args[0], $options);
563. } else {
564. $options = array_merge($args[1], $options);
565. $this->_parser->bind($args[0], $options);
566. }
567. }
569. /**
570. * hasRelation
571. *
572. * @param string $alias the relation to check if exists
573. * @return boolean true if the relation exists otherwise false
574. */
575. public function hasRelation($alias)
576. {
577. return $this->_parser->hasRelation($alias);
578. }
580. /**
581. * getRelation
582. *
583. * @param string $alias relation alias
584. */
585. public function getRelation($alias, $recursive = true)
586. {
587. return $this->_parser->getRelation($alias, $recursive);
588. }
589. /**
590. * getRelations
591. * returns an array containing all relation objects
592. *
593. * @return array an array of Doctrine_Relation objects
594. */
595. public function getRelations()
596. {
597. return $this->_parser->getRelations();
598. }
599. /**
600. * createQuery
601. * creates a new Doctrine_Query object and adds the component name
602. * of this table as the query 'from' part
603. *
604. * @return Doctrine_Query
605. */
606. public function createQuery()
607. {
608. return Doctrine_Query::create()->from($this->getComponentName());
609. }
610. /**
611. * getRepository
612. *
613. * @return Doctrine_Table_Repository
614. */
615. public function getRepository()
616. {
617. return $this->repository;
618. }
619. /**
620. * setOption
621. * sets an option and returns this object in order to
622. * allow flexible method chaining
623. *
624. * @see Doctrine_Table::$_options for available options
625. * @param string $name the name of the option to set
626. * @param mixed $value the value of the option
627. * @return Doctrine_Table this object
628. */
629. public function setOption($name, $value)
630. {
631. switch ($name) {
632. case 'name':
633. case 'tableName':
634. break;
635. case 'enumMap':
636. case 'inheritanceMap':
637. case 'index':
638. case 'treeOptions':
639. if ( ! is_array($value)) {
640. throw new Doctrine_Table_Exception($name . ' should be an array.');
641. }
642. break;
643. }
644. $this->options[$name] = $value;
645. }
646. /**
647. * getOption
648. * returns the value of given option
649. *
650. * @param string $name the name of the option
651. * @return mixed the value of given option
652. */
653. public function getOption($name)
654. {
655. if (isset($this->options[$name])) {
656. return $this->options[$name];
657. }
658. return null;
659. }
660. /**
661. * getColumnName
662. *
663. * returns a column name for column alias
664. * if the actual name for the alias cannot be found
665. * this method returns the given alias
666. *
667. * @param string $alias column alias
668. * @return string column name
669. */
670. public function getColumnName($alias)
671. {
672. $alias = strtolower($alias);
673. if(isset($this->columnAliases[$alias])) {
674. return $this->columnAliases[$alias];
675. }
677. return $alias;
678. }
679. /**
680. * setColumn
681. *
682. * @param string $name
683. * @param string $type
684. * @param integer $length
685. * @param mixed $options
686. * @throws Doctrine_Table_Exception if trying use wrongly typed parameter
687. * @return void
688. */
689. public function setColumn($name, $type, $length = null, $options = array())
690. {
691. if (is_string($options)) {
692. $options = explode('|', $options);
693. }
695. foreach ($options as $k => $option) {
696. if (is_numeric($k)) {
697. if ( ! empty($option)) {
698. $options[$option] = true;
699. }
700. unset($options[$k]);
701. }
702. }
704. $name = strtolower($name);
705. $parts = explode(' as ', $name);
707. if (count($parts) > 1) {
708. $this->columnAliases[$parts[1]] = $parts[0];
709. $name = $parts[0];
710. }
714. if ($length == null) {
715. switch ($type) {
716. case 'string':
717. case 'clob':
718. case 'float':
719. case 'integer':
720. case 'array':
721. case 'object':
722. case 'blob':
723. case 'gzip':
724. // use php int max
725. $length = 2147483647;
726. break;
727. case 'boolean':
728. $length = 1;
729. case 'date':
730. // YYYY-MM-DD ISO 8601
731. $length = 10;
732. case 'time':
733. // HH:NN:SS+00:00 ISO 8601
734. $length = 14;
735. case 'timestamp':
736. // YYYY-MM-DDTHH:MM:SS+00:00 ISO 8601
737. $length = 25;
738. break;
739. }
740. }
742. $this->columns[$name] = $options;
743. $this->columns[$name]['type'] = $type;
744. $this->columns[$name]['length'] = $length;
746. if (isset($options['primary'])) {
747. $this->primaryKeys[] = $name;
748. }
749. if (isset($options['default'])) {
750. $this->hasDefaultValues = true;
751. }
752. }
753. /**
754. * hasDefaultValues
755. * returns true if this table has default values, otherwise false
756. *
757. * @return boolean
758. */
759. public function hasDefaultValues()
760. {
761. return $this->hasDefaultValues;
762. }
763. /**
764. * getDefaultValueOf
765. * returns the default value(if any) for given column
766. *
767. * @param string $column
768. * @return mixed
769. */
770. public function getDefaultValueOf($column)
771. {
772. $column = strtolower($column);
773. if ( ! isset($this->columns[$column])) {
774. throw new Doctrine_Table_Exception("Couldn't get default value. Column ".$column." doesn't exist.");
775. }
776. if (isset($this->columns[$column]['default'])) {
777. return $this->columns[$column]['default'];
778. } else {
779. return null;
780. }
781. }
782. /**
783. * @return mixed
784. */
785. public function getIdentifier()
786. {
787. return $this->identifier;
788. }
789. /**
790. * @return integer
791. */
792. public function getIdentifierType()
793. {
794. return $this->identifierType;
795. }
796. /**
797. * hasColumn
798. * @return boolean
799. */
800. public function hasColumn($name)
801. {
802. return isset($this->columns[$name]);
803. }
804. /**
805. * @param mixed $key
806. * @return void
807. */
808. public function setPrimaryKey($key)
809. {
810. switch (gettype($key)) {
811. case "array":
812. $this->primaryKeys = array_values($key);
813. break;
814. case "string":
815. $this->primaryKeys[] = $key;
816. break;
817. };
818. }
819. /**
820. * returns all primary keys
821. * @return array
822. */
823. public function getPrimaryKeys()
824. {
825. return $this->primaryKeys;
826. }
827. /**
828. * @return boolean
829. */
830. public function hasPrimaryKey($key)
831. {
832. return in_array($key,$this->primaryKeys);
833. }
834. /**
835. * @return Doctrine_Connection
836. */
837. public function getConnection()
838. {
839. return $this->conn;
840. }
841. /**
842. * create
843. * creates a new record
844. *
845. * @param $array an array where keys are field names and values representing field values
846. * @return Doctrine_Record
847. */
848. public function create(array $array = array()) {
849. $this->data = $array;
850. $record = new $this->options['name']($this, true);
851. $this->data = array();
852. return $record;
853. }
854. /**
855. * finds a record by its identifier
856. *
857. * @param $id database row id
858. * @return Doctrine_Record|false a record for given database identifier
859. */
860. public function find($id)
861. {
862. if ($id !== null) {
863. if ( ! is_array($id)) {
864. $id = array($id);
865. } else {
866. $id = array_values($id);
867. }
869. $records = Doctrine_Query::create()
870. ->from($this->getComponentName())
871. ->where(implode(' = ? AND ', $this->primaryKeys) . ' = ?')
872. ->execute($id);
874. if (count($records) === 0) {
875. return false;
876. }
878. return $records->getFirst();
879. }
880. return false;
881. }
882. /**
883. * applyInheritance
884. * @param $where query where part to be modified
885. * @return string query where part with column aggregation inheritance added
886. */
887. final public function applyInheritance($where)
888. {
889. if ( ! empty($this->options['inheritanceMap'])) {
890. $a = array();
891. foreach ($this->options['inheritanceMap'] as $field => $value) {
892. $a[] = $field . ' = ?';
893. }
894. $i = implode(' AND ', $a);
895. $where .= ' AND ' . $i;
896. }
897. return $where;
898. }
899. /**
900. * findAll
901. * returns a collection of records
902. *
903. * @return Doctrine_Collection
904. */
905. public function findAll()
906. {
907. $graph = new Doctrine_Query($this->conn);
908. $users = $graph->query('FROM ' . $this->options['name']);
909. return $users;
910. }
911. /**
912. * findByDql
913. * finds records with given DQL where clause
914. * returns a collection of records
915. *
916. * @param string $dql DQL after WHERE clause
917. * @param array $params query parameters
918. * @return Doctrine_Collection
919. */
920. public function findBySql($dql, array $params = array()) {
921. $q = new Doctrine_Query($this->conn);
922. $users = $q->query('FROM ' . $this->options['name'] . ' WHERE ' . $dql, $params);
923. return $users;
924. }
926. public function findByDql($dql, array $params = array()) {
927. return $this->findBySql($dql, $params);
928. }
929. /**
930. * clear
931. * clears the first level cache (identityMap)
932. *
933. * @return void
934. */
935. public function clear()
936. {
937. $this->identityMap = array();
938. }
939. /**
940. * addRecord
941. * adds a record to identity map
942. *
943. * @param Doctrine_Record $record record to be added
944. * @return boolean
945. */
946. public function addRecord(Doctrine_Record $record)
947. {
948. $id = implode(' ', $record->identifier());
950. if (isset($this->identityMap[$id])) {
951. return false;
952. }
954. $this->identityMap[$id] = $record;
956. return true;
957. }
958. /**
959. * getRecord
960. * first checks if record exists in identityMap, if not
961. * returns a new record
962. *
963. * @return Doctrine_Record
964. */
965. public function getRecord()
966. {
967. if ( ! empty($this->data)) {
968. $this->data = array_change_key_case($this->data, CASE_LOWER);
970. $key = $this->getIdentifier();
972. if ( ! is_array($key)) {
973. $key = array($key);
974. }
976. $found = false;
977. foreach ($key as $k) {
978. if ( ! isset($this->data[$k])) {
979. // primary key column not found return new record
980. $found = true;
981. break;
982. }
983. $id[] = $this->data[$k];
984. }
986. if ($found) {
987. $recordName = $this->getClassnameToReturn();
988. $record = new $recordName($this, true);
989. $this->data = array();
991. return $record;
992. }
995. $id = implode(' ', $id);
997. if (isset($this->identityMap[$id])) {
998. $record = $this->identityMap[$id];
999. $record->hydrate($this->data);
1000. } else {
1001. $recordName = $this->getClassnameToReturn();
1002. $record = new $recordName($this);
1003. $this->identityMap[$id] = $record;
1004. }
1005. $this->data = array();
1006. } else {
1007. $recordName = $this->getClassnameToReturn();
1008. $record = new $recordName($this, true);
1009. }
1012. return $record;
1013. }
1015. /**
1016. * Get the classname to return. Most often this is just the options['name']
1017. *
1018. * Check the subclasses option and the inheritanceMap for each subclass to see
1019. * if all the maps in a subclass is met. If this is the case return that
1020. * subclass name. If no subclasses match or if there are no subclasses defined
1021. * return the name of the class for this tables record.
1022. *
1023. * @todo this function could use reflection to check the first time it runs
1024. * if the subclassing option is not set.
1025. *
1026. * @return string The name of the class to create
1027. *
1028. */
1029. public function getClassnameToReturn()
1030. {
1031. if (!isset($this->options['subclasses'])) {
1032. return $this->options['name'];
1033. }
1034. foreach ($this->options['subclasses'] as $subclass) {
1035. $table = $this->conn->getTable($subclass);
1036. $inheritanceMap = $table->getOption('inheritanceMap');
1037. $nomatch = false;
1038. foreach ($inheritanceMap as $key => $value) {
1039. if ( ! isset($this->data[$key]) || $this->data[$key] != $value) {
1040. $nomatch = true;
1041. break;
1042. }
1043. }
1044. if ( ! $nomatch) {
1045. return $table->getComponentName();
1046. }
1047. }
1048. return $this->options['name'];
1049. }
1051. /**
1052. * @param $id database row id
1053. * @throws Doctrine_Find_Exception
1054. */
1055. final public function getProxy($id = null)
1056. {
1057. if ($id !== null) {
1058. $query = 'SELECT ' . implode(', ',$this->primaryKeys)
1059. . ' FROM ' . $this->getTableName()
1060. . ' WHERE ' . implode(' = ? && ',$this->primaryKeys) . ' = ?';
1061. $query = $this->applyInheritance($query);
1063. $params = array_merge(array($id), array_values($this->options['inheritanceMap']));
1065. $this->data = $this->conn->execute($query, $params)->fetch(PDO::FETCH_ASSOC);
1067. if ($this->data === false)
1068. return false;
1069. }
1070. return $this->getRecord();
1071. }
1072. /**
1073. * count
1074. *
1075. * @return integer
1076. */
1077. public function count()
1078. {
1079. $a = $this->conn->execute('SELECT COUNT(1) FROM ' . $this->options['tableName'])->fetch(Doctrine::FETCH_NUM);
1080. return current($a);
1081. }
1082. /**
1083. * @return Doctrine_Query a Doctrine_Query object
1084. */
1085. public function getQueryObject()
1086. {
1087. $graph = new Doctrine_Query($this->getConnection());
1088. $graph->load($this->getComponentName());
1089. return $graph;
1090. }
1091. /**
1092. * @param string $field
1093. * @return array
1094. */
1095. public function getEnumValues($field)
1096. {
1097. if (isset($this->columns[$field]['values'])) {
1098. return $this->columns[$field]['values'];
1099. } else {
1100. return array();
1101. }
1102. }
1103. /**
1104. * enumValue
1105. *
1106. * @param string $field
1107. * @param integer $index
1108. * @return mixed
1109. */
1110. public function enumValue($field, $index)
1111. {
1112. if ($index instanceof Doctrine_Null)
1113. return $index;
1115. return isset($this->columns[$field]['values'][$index]) ? $this->columns[$field]['values'][$index] : $index;
1116. }
1117. /**
1118. * enumIndex
1119. *
1120. * @param string $field
1121. * @param mixed $value
1122. * @return mixed
1123. */
1124. public function enumIndex($field, $value)
1125. {
1126. $values = $this->getEnumValues($field);
1128. return array_search($value, $values);
1129. }
1130. /**
1131. * getColumnCount
1132. *
1133. * @return integer the number of columns in this table
1134. */
1135. public function getColumnCount()
1136. {
1137. return $this->columnCount;
1138. }
1140. /**
1141. * returns all columns and their definitions
1142. *
1143. * @return array
1144. */
1145. public function getColumns()
1146. {
1147. return $this->columns;
1148. }
1149. /**
1150. * returns an array containing all the column names
1151. *
1152. * @return array
1153. */
1154. public function getColumnNames()
1155. {
1156. return array_keys($this->columns);
1157. }
1158. /**
1159. * getDefinitionOf
1160. *
1161. * @return mixed array on success, false on failure
1162. */
1163. public function getDefinitionOf($column)
1164. {
1165. if (isset($this->columns[$column])) {
1166. return $this->columns[$column];
1167. }
1168. return false;
1169. }
1170. /**
1171. * getTypeOf
1172. *
1173. * @return mixed string on success, false on failure
1174. */
1175. public function getTypeOf($column)
1176. {
1177. if (isset($this->columns[$column])) {
1178. return $this->columns[$column]['type'];
1179. }
1180. return false;
1181. }
1182. /**
1183. * setData
1184. * doctrine uses this function internally
1185. * users are strongly discouraged to use this function
1186. *
1187. * @param array $data internal data
1188. * @return void
1189. */
1190. public function setData(array $data)
1191. {
1192. $this->data = $data;
1193. }
1194. /**
1195. /**
1196. * returns internal data, used by Doctrine_Record instances
1197. * when retrieving data from database
1198. *
1199. * @return array
1200. */
1201. public function getData()
1202. {
1203. return $this->data;
1204. }
1205. /**
1206. * prepareValue
1207. * this method performs special data preparation depending on
1208. * the type of the given column
1209. *
1210. * 1. It unserializes array and object typed columns
1211. * 2. Uncompresses gzip typed columns
1212. * 3. Gets the appropriate enum values for enum typed columns
1213. * 4. Initializes special null object pointer for null values (for fast column existence checking purposes)
1214. *
1215. * example:
1216. * <code type='php'>
1217. * $field = 'name';
1218. * $value = null;
1219. * $table->prepareValue($field, $value); // Doctrine_Null
1220. * </code>
1221. *
1222. * @throws Doctrine_Table_Exception if unserialization of array/object typed column fails or
1223. * @throws Doctrine_Table_Exception if uncompression of gzip typed column fails *
1224. * @param string $field the name of the field
1225. * @param string $value field value
1226. * @return mixed prepared value
1227. */
1228. public function prepareValue($field, $value)
1229. {
1230. if ($value === self::$_null) {
1231. return self::$_null;
1232. } else if ($value === null) {
1233. return null;
1234. } else {
1235. $type = $this->getTypeOf($field);
1237. switch ($type) {
1238. case 'array':
1239. case 'object':
1240. if (is_string($value)) {
1241. $value = unserialize($value);
1243. if ($value === false) {
1244. throw new Doctrine_Table_Exception('Unserialization of ' . $field . ' failed.');
1245. }
1246. return $value;
1247. }
1248. break;
1249. case 'gzip':
1250. $value = gzuncompress($value);
1252. if ($value === false) {
1253. throw new Doctrine_Table_Exception('Uncompressing of ' . $field . ' failed.');
1254. }
1255. return $value;
1256. break;
1257. case 'enum':
1258. return $this->enumValue($field, $value);
1259. break;
1260. case 'boolean':
1261. return (boolean) $value;
1262. break;
1263. case 'integer':
1264. // don't do any casting here PHP INT_MAX is smaller than what the databases support
1265. break;
1266. }
1267. }
1268. return $value;
1269. }
1270. /**
1271. * getter for associated tree
1272. *
1273. * @return mixed if tree return instance of Doctrine_Tree, otherwise returns false
1274. */
1275. public function getTree() {
1276. if (isset($this->options['treeImpl'])) {
1277. if ( ! $this->tree) {
1278. $options = isset($this->options['treeOptions']) ? $this->options['treeOptions'] : array();
1279. $this->tree = Doctrine_Tree::factory($this,
1280. $this->options['treeImpl'],
1281. $options
1282. );
1283. }
1284. return $this->tree;
1285. }
1286. return false;
1287. }
1288. public function getComponentName()
1289. {
1290. return $this->options['name'];
1291. }
1292. public function getTableName()
1293. {
1294. return $this->options['tableName'];
1295. }
1296. public function setTableName($tableName)
1297. {
1298. $this->options['tableName'] = $tableName;
1299. }
1300. /**
1301. * determine if table acts as tree
1302. *
1303. * @return mixed if tree return true, otherwise returns false
1304. */
1305. public function isTree() {
1306. return ( ! is_null($this->options['treeImpl'])) ? true : false;
1307. }
1309. public function getTemplate($template)
1310. {
1311. if ( ! isset($this->_templates[$template])) {
1312. throw new Doctrine_Table_Exception('Template ' . $template . ' not loaded');
1313. }
1315. return $this->_templates[$template];
1316. }
1318. public function addTemplate($template, Doctrine_Template $impl)
1319. {
1320. $this->_templates[$template] = $impl;
1321. }
1322. /**
1323. * returns a string representation of this object
1324. *
1325. * @return string
1326. */
1327. public function __toString()
1328. {
1329. return Doctrine_Lib::getTableAsString($this);
1330. }
1331. }