# Source for file RawSql.php

Documentation is available at RawSql.php

1. <?php
2. /*
3. * $Id: RawSql.php 1847 2007-06-26 10:05:26Z zYne $
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. Doctrine::autoload('Doctrine_Query_Abstract');
22. /**
23. * Doctrine_RawSql
24. *
25. * @package Doctrine
26. * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
27. * @category Object Relational Mapping
28. * @link www.phpdoctrine.com
29. * @since 1.0
30. * @version $Revision: 1847 $
31. * @author Konsta Vesterinen <kvesteri@cc.hut.fi>
32. */
33. class Doctrine_RawSql extends Doctrine_Query_Abstract
34. {
35. /**
36. * @var array $fields
37. */
38. private $fields = array();
39. /**
40. * parseQueryPart
41. * parses given query part
42. *
43. * @param string $queryPartName the name of the query part
44. * @param string $queryPart query part to be parsed
45. * @param boolean $append whether or not to append the query part to its stack
46. * if false is given, this method will overwrite
47. * the given query part stack with $queryPart
48. * @return Doctrine_Query this object
49. */
50. public function parseQueryPart($queryPartName, $queryPart, $append = false)
51. {
52. if ($queryPartName == 'select') {
53. preg_match_all('/{([^}{]*)}/U', $queryPart, $m);
55. $this->fields = $m[1];
56. $this->parts['select'] = array();
57. } else {
58. if ( ! $append) {
59. $this->parts[$queryPartName] = array($queryPart);
60. } else {
61. $this->parts[$queryPartName][] = $queryPart;
62. }
63. }
64. return $this;
65. }
66. /**
67. * parseQuery
68. * parses an sql query and adds the parts to internal array
69. *
70. * @param string $query query to be parsed
71. * @return Doctrine_RawSql this object
72. */
73. public function parseQuery($query)
74. {
75. preg_match_all('/{([^}{]*)}/U', $query, $m);
77. $this->fields = $m[1];
78. $this->clear();
80. $e = Doctrine_Tokenizer::sqlExplode($query,' ');
82. foreach ($e as $k => $part) {
83. $low = strtolower($part);
84. switch (strtolower($part)) {
85. case 'select':
86. case 'from':
87. case 'where':
88. case 'limit':
89. case 'offset':
90. case 'having':
91. $p = $low;
92. if ( ! isset($parts[$low])) {
93. $parts[$low] = array();
94. }
95. break;
96. case 'order':
97. case 'group':
98. $i = ($k + 1);
99. if (isset($e[$i]) && strtolower($e[$i]) === 'by') {
100. $p = $low;
101. $p .= 'by';
102. $parts[$low . 'by'] = array();
104. } else {
105. $parts[$p][] = $part;
106. }
107. break;
108. case 'by':
109. continue;
110. default:
111. if ( ! isset($parts[$p][0])) {
112. $parts[$p][0] = $part;
113. } else {
114. $parts[$p][0] .= ' '.$part;
115. }
116. }
117. }
119. $this->parts = $parts;
120. $this->parts['select'] = array();
122. return $this;
123. }
124. /**
125. * getQuery
126. * builds the sql query from the given query parts
127. *
128. * @return string the built sql query
129. */
130. public function getQuery()
131. {
132. $select = array();
134. foreach ($this->fields as $field) {
135. $e = explode('.', $field);
136. if ( ! isset($e[1])) {
137. throw new Doctrine_RawSql_Exception('All selected fields in Sql query must be in format tableAlias.fieldName');
138. }
139. // try to auto-add component
140. if ( ! $this->hasTableAlias($e[0])) {
141. try {
142. $this->addComponent($e[0], ucwords($e[0]));
143. } catch(Doctrine_Exception $exception) {
144. throw new Doctrine_RawSql_Exception('The associated component for table alias ' . $e[0] . ' couldn\'t be found.');
145. }
146. }
148. $componentAlias = $this->getComponentAlias($e[0]);
150. if ($e[1] == '*') {
151. foreach ($this->_aliasMap[$componentAlias]['table']->getColumnNames() as $name) {
152. $field = $e[0] . '.' . $name;
154. $select[$componentAlias][$field] = $field . ' AS ' . $e[0] . '__' . $name;
155. }
156. } else {
157. $field = $e[0] . '.' . $e[1];
158. $select[$componentAlias][$field] = $field . ' AS ' . $e[0] . '__' . $e[1];
159. }
160. }
162. // force-add all primary key fields
164. foreach ($this->getTableAliases() as $tableAlias => $componentAlias) {
165. $map = $this->_aliasMap[$componentAlias];
167. foreach ($map['table']->getPrimaryKeys() as $key) {
168. $field = $tableAlias . '.' . $key;
170. if ( ! isset($this->parts['select'][$field])) {
171. $select[$componentAlias][$field] = $field . ' AS ' . $tableAlias . '__' . $key;
172. }
173. }
174. }
176. // first add the fields of the root component
177. reset($this->_aliasMap);
178. $componentAlias = key($this->_aliasMap);
180. $q = 'SELECT ' . implode(', ', $select[$componentAlias]);
181. unset($select[$componentAlias]);
183. foreach ($select as $component => $fields) {
184. if ( ! empty($fields)) {
185. $q .= ', ' . implode(', ', $fields);
186. }
187. }
189. $string = $this->applyInheritance();
190. if ( ! empty($string)) {
191. $this->parts['where'][] = $string;
192. }
193. $copy = $this->parts;
194. unset($copy['select']);
196. $q .= ( ! empty($this->parts['from']))? ' FROM ' . implode(' ', $this->parts['from']) : '';
197. $q .= ( ! empty($this->parts['where']))? ' WHERE ' . implode(' AND ', $this->parts['where']) : '';
198. $q .= ( ! empty($this->parts['groupby']))? ' GROUP BY ' . implode(', ', $this->parts['groupby']) : '';
199. $q .= ( ! empty($this->parts['having']))? ' HAVING ' . implode(' AND ', $this->parts['having']) : '';
200. $q .= ( ! empty($this->parts['orderby']))? ' ORDER BY ' . implode(', ', $this->parts['orderby']) : '';
201. $q .= ( ! empty($this->parts['limit']))? ' LIMIT ' . implode(' ', $this->parts['limit']) : '';
202. $q .= ( ! empty($this->parts['offset']))? ' OFFSET ' . implode(' ', $this->parts['offset']) : '';
204. if ( ! empty($string)) {
205. array_pop($this->parts['where']);
206. }
207. return $q;
208. }
209. /**
210. * getFields
211. * returns the fields associated with this parser
212. *
213. * @return array all the fields associated with this parser
214. */
215. public function getFields()
216. {
217. return $this->fields;
218. }
219. /**
220. * addComponent
221. *
222. * @param string $tableAlias
223. * @param string $componentName
224. * @return Doctrine_RawSql
225. */
226. public function addComponent($tableAlias, $path)
227. {
228. $tmp = explode(' ', $path);
229. $originalAlias = (count($tmp) > 1) ? end($tmp) : null;
231. $e = explode('.', $tmp[0]);
233. $fullPath = $tmp[0];
234. $fullLength = strlen($fullPath);
236. $table = null;
238. $currPath = '';
240. if (isset($this->_aliasMap[$e[0]])) {
241. $table = $this->_aliasMap[$e[0]]['table'];
243. $currPath = $parent = array_shift($e);
244. }
246. foreach ($e as $k => $component) {
247. // get length of the previous path
248. $length = strlen($currPath);
250. // build the current component path
251. $currPath = ($currPath) ? $currPath . '.' . $component : $component;
253. $delimeter = substr($fullPath, $length, 1);
255. // if an alias is not given use the current path as an alias identifier
256. if (strlen($currPath) === $fullLength && isset($originalAlias)) {
257. $componentAlias = $originalAlias;
258. } else {
259. $componentAlias = $currPath;
260. }
261. if ( ! isset($table)) {
262. $conn = Doctrine_Manager::getInstance()
263. ->getConnectionForComponent($component);
265. $table = $conn->getTable($component);
266. $this->_aliasMap[$componentAlias] = array('table' => $table);
267. } else {
268. $relation = $table->getRelation($component);
270. $this->_aliasMap[$componentAlias] = array('table' => $relation->getTable(),
271. 'parent' => $parent,
272. 'relation' => $relation);
273. }
274. $this->addTableAlias($tableAlias, $componentAlias);
276. $parent = $currPath;
277. }
279. return $this;
280. }
282. }