QPDB.php 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711
  1. <?php
  2. /** @file
  3. * This package contains classes for handling database transactions from
  4. * within QueryPath.
  5. *
  6. * The tools here use the PDO (PHP Data Objects) library to execute database
  7. * functions.
  8. *
  9. * Using tools in this package, you can write QueryPath database queries
  10. * that query an RDBMS and then insert the results into the document.
  11. *
  12. * Example:
  13. *
  14. * @code
  15. * <?php
  16. * $template = '<?xml version="1.0"?><tr><td class="colOne"/><td class="colTwo"/><td class="colThree"/></tr>';
  17. * $qp = qp(QueryPath::HTML_STUB, 'body') // Open a stub HTML doc and select <body/>
  18. * ->append('<table><tbody/></table>')
  19. * ->dbInit($this->dsn)
  20. * ->queryInto('SELECT * FROM qpdb_test WHERE 1', array(), $template)
  21. * ->doneWithQuery()
  22. * ->writeHTML();
  23. * ?>
  24. * @endcode
  25. *
  26. * The code above will take the results of a SQL query and insert them into a n
  27. * HTML table.
  28. *
  29. * If you are doing many database operations across multiple QueryPath objects,
  30. * it is better to avoid using {@link QPDB::dbInit()}. Instead, you should
  31. * call the static {@link QPDB::baseDB()} method to configure a single database
  32. * connection that can be shared by all {@link QueryPath} instances.
  33. *
  34. * Thus, we could rewrite the above to look like this:
  35. * @code
  36. * <?php
  37. * QPDB::baseDB($someDN);
  38. *
  39. * $template = '<?xml version="1.0"?><tr><td class="colOne"/><td class="colTwo"/><td class="colThree"/></tr>';
  40. * $qp = qp(QueryPath::HTML_STUB, 'body') // Open a stub HTML doc and select <body/>
  41. * ->append('<table><tbody/></table>')
  42. * ->queryInto('SELECT * FROM qpdb_test WHERE 1', array(), $template)
  43. * ->doneWithQuery()
  44. * ->writeHTML();
  45. * ?>
  46. * @endcode
  47. *
  48. * Note that in this case, the QueryPath object doesn't need to call a method to
  49. * activate the database. There is no call to {@link dbInit()}. Instead, it checks
  50. * the base class to find the shared database.
  51. *
  52. * (Note that if you were to add a dbInit() call to the above, it would create
  53. * a new database connection.)
  54. *
  55. * The result of both of these examples will be identical.
  56. * The output looks something like this:
  57. *
  58. * @code
  59. * <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  60. * <html xmlns="http://www.w3.org/1999/xhtml">
  61. * <head>
  62. * <meta http-equiv="Content-Type" content="text/html; charset=utf-8"></meta>
  63. * <title>Untitled</title>
  64. * </head>
  65. *<body>
  66. *<table>
  67. * <tbody>
  68. * <tr>
  69. * <td class="colOne">Title 0</td>
  70. * <td class="colTwo">Body 0</td>
  71. * <td class="colThree">Footer 0</td>
  72. * </tr>
  73. * <tr>
  74. * <td class="colOne">Title 1</td>
  75. * <td class="colTwo">Body 1</td>
  76. * <td class="colThree">Footer 1</td>
  77. * </tr>
  78. * <tr>
  79. * <td class="colOne">Title 2</td>
  80. * <td class="colTwo">Body 2</td>
  81. * <td class="colThree">Footer 2</td>
  82. * </tr>
  83. * <tr>
  84. * <td class="colOne">Title 3</td>
  85. * <td class="colTwo">Body 3</td>
  86. * <td class="colThree">Footer 3</td>
  87. * </tr>
  88. * <tr>
  89. * <td class="colOne">Title 4</td>
  90. * <td class="colTwo">Body 4</td>
  91. * <td class="colThree">Footer 4</td>
  92. * </tr>
  93. * </tbody>
  94. *</table>
  95. *</body>
  96. *</html>
  97. * @endcode
  98. *
  99. * Note how the CSS classes are used to correlate DB table names to template
  100. * locations.
  101. *
  102. *
  103. * @author M Butcher <matt@aleph-null.tv>
  104. * @license http://opensource.org/licenses/lgpl-2.1.php LGPL or MIT-like license.
  105. * @see QueryPathExtension
  106. * @see QueryPathExtensionRegistry::extend()
  107. * @see QPDB
  108. */
  109. /**
  110. * Provide DB access to a QueryPath object.
  111. *
  112. * This extension provides tools for communicating with a database using the
  113. * QueryPath library. It relies upon PDO for underlying database communiction. This
  114. * means that it supports all databases that PDO supports, including MySQL,
  115. * PostgreSQL, and SQLite.
  116. *
  117. * Here is an extended example taken from the unit tests for this library.
  118. *
  119. * Let's say we create a database with code like this:
  120. * @code
  121. *<?php
  122. * public function setUp() {
  123. * $this->db = new PDO($this->dsn);
  124. * $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  125. * $this->db->exec('CREATE TABLE IF NOT EXISTS qpdb_test (colOne, colTwo, colThree)');
  126. *
  127. * $stmt = $this->db->prepare(
  128. * 'INSERT INTO qpdb_test (colOne, colTwo, colThree) VALUES (:one, :two, :three)'
  129. * );
  130. *
  131. * for ($i = 0; $i < 5; ++$i) {
  132. * $vals = array(':one' => 'Title ' . $i, ':two' => 'Body ' . $i, ':three' => 'Footer ' . $i);
  133. * $stmt->execute($vals);
  134. * $stmt->closeCursor();
  135. * }
  136. * }
  137. * ?>
  138. * @endcode
  139. *
  140. * From QueryPath with QPDB, we can now do very elaborate DB chains like this:
  141. *
  142. * @code
  143. * <?php
  144. * $sql = 'SELECT * FROM qpdb_test';
  145. * $args = array();
  146. * $qp = qp(QueryPath::HTML_STUB, 'body') // Open a stub HTML doc and select <body/>
  147. * ->append('<h1></h1>') // Add <h1/>
  148. * ->children() // Select the <h1/>
  149. * ->dbInit($this->dsn) // Connect to the database
  150. * ->query($sql, $args) // Execute the SQL query
  151. * ->nextRow() // Select a row. By default, no row is selected.
  152. * ->appendColumn('colOne') // Append Row 1, Col 1 (Title 0)
  153. * ->parent() // Go back to the <body/>
  154. * ->append('<p/>') // Append a <p/> to the body
  155. * ->find('p') // Find the <p/> we just created.
  156. * ->nextRow() // Advance to row 2
  157. * ->prependColumn('colTwo') // Get row 2, col 2. (Body 1)
  158. * ->columnAfter('colThree') // Get row 2 col 3. (Footer 1)
  159. * ->doneWithQuery() // Let QueryPath clean up. YOU SHOULD ALWAYS DO THIS.
  160. * ->writeHTML(); // Write the output as HTML.
  161. * ?>
  162. * @endcode
  163. *
  164. * With the code above, we step through the document, selectively building elements
  165. * as we go, and then populating this elements with data from our initial query.
  166. *
  167. * When the last command, {@link QueryPath:::writeHTML()}, is run, we will get output
  168. * like this:
  169. *
  170. * @code
  171. * <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  172. * <html xmlns="http://www.w3.org/1999/xhtml">
  173. * <head>
  174. * <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  175. * <title>Untitled</title>
  176. * </head>
  177. * <body>
  178. * <h1>Title 0</h1>
  179. * <p>Body 1</p>
  180. * Footer 1</body>
  181. * </html>
  182. * @endcode
  183. *
  184. * Notice the body section in particular. This is where the data has been
  185. * inserted.
  186. *
  187. * Sometimes you want to do something a lot simpler, like give QueryPath a
  188. * template and have it navigate a query, inserting the data into a template, and
  189. * then inserting the template into the document. This can be done simply with
  190. * the {@link queryInto()} function.
  191. *
  192. * Here's an example from another unit test:
  193. *
  194. * @code
  195. * <?php
  196. * $template = '<?xml version="1.0"?><li class="colOne"/>';
  197. * $sql = 'SELECT * FROM qpdb_test';
  198. * $args = array();
  199. * $qp = qp(QueryPath::HTML_STUB, 'body')
  200. * ->append('<ul/>') // Add a new <ul/>
  201. * ->children() // Select the <ul/>
  202. * ->dbInit($this->dsn) // Initialize the DB
  203. * // BIG LINE: Query the results, run them through the template, and insert them.
  204. * ->queryInto($sql, $args, $template)
  205. * ->doneWithQuery()
  206. * ->writeHTML(); // Write the results as HTML.
  207. * ?>
  208. * @endcode
  209. *
  210. * The simple code above puts the first column of the select statement
  211. * into an unordered list. The example output looks like this:
  212. *
  213. * @code
  214. * <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  215. * <html xmlns="http://www.w3.org/1999/xhtml">
  216. * <head>
  217. * <meta http-equiv="Content-Type" content="text/html; charset=utf-8"></meta>
  218. * <title>Untitled</title>
  219. * </head>
  220. * <body>
  221. * <ul>
  222. * <li class="colOne">Title 0</li>
  223. * <li class="colOne">Title 1</li>
  224. * <li class="colOne">Title 2</li>
  225. * <li class="colOne">Title 3</li>
  226. * <li class="colOne">Title 4</li>
  227. * </ul>
  228. * </body>
  229. * </html>
  230. * @endcode
  231. *
  232. * Typical starting methods for this class are {@link QPDB::baseDB()},
  233. * {@link QPDB::query()}, and {@link QPDB::queryInto()}.
  234. *
  235. * @ingroup querypath_extensions
  236. */
  237. class QPDB implements QueryPathExtension {
  238. protected $qp;
  239. protected $dsn;
  240. protected $db;
  241. protected $opts;
  242. protected $row = NULL;
  243. protected $stmt = NULL;
  244. protected static $con = NULL;
  245. /**
  246. * Create a new database instance for all QueryPath objects to share.
  247. *
  248. * This method need be called only once. From there, other QPDB instances
  249. * will (by default) share the same database instance.
  250. *
  251. * Normally, a DSN should be passed in. Username, password, and db params
  252. * are all passed in using the options array.
  253. *
  254. * On rare occasions, it may be more fitting to pass in an existing database
  255. * connection (which must be a {@link PDO} object). In such cases, the $dsn
  256. * parameter can take a PDO object instead of a DSN string. The standard options
  257. * will be ignored, though.
  258. *
  259. * <b>Warning:</b> If you pass in a PDO object that is configured to NOT throw
  260. * exceptions, you will need to handle error checking differently.
  261. *
  262. * <b>Remember to always use {@link QPDB::doneWithQuery()} when you are done
  263. * with a query. It gives PDO a chance to clean up open connections that may
  264. * prevent other instances from accessing or modifying data.</b>
  265. *
  266. * @param string $dsn
  267. * The DSN of the database to connect to. You can also pass in a PDO object, which
  268. * will set the QPDB object's database to the one passed in.
  269. * @param array $options
  270. * An array of configuration options. The following options are currently supported:
  271. * - username => (string)
  272. * - password => (string)
  273. * - db params => (array) These will be passed into the new PDO object.
  274. * See the PDO documentation for a list of options. By default, the
  275. * only flag set is {@link PDO::ATTR_ERRMODE}, which is set to
  276. * {@link PDO::ERRMODE_EXCEPTION}.
  277. * @throws PDOException
  278. * An exception may be thrown if the connection cannot be made.
  279. */
  280. static function baseDB($dsn, $options = array()) {
  281. $opts = $options + array(
  282. 'username' => NULL,
  283. 'password' => NULL,
  284. 'db params' => array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION),
  285. );
  286. // Allow this to handle the case where an outside
  287. // connection does the initialization.
  288. if ($dsn instanceof PDO) {
  289. self::$con = $dsn;
  290. return;
  291. }
  292. self::$con = new PDO($dsn, $opts['username'], $opts['password'], $opts['db params']);
  293. }
  294. /**
  295. *
  296. * This method may be used to share the connection with other,
  297. * non-QueryPath objects.
  298. */
  299. static function getBaseDB() {return self::$con;}
  300. /**
  301. * Used to control whether or not all rows in a result should be cycled through.
  302. */
  303. protected $cycleRows = FALSE;
  304. /**
  305. * Construct a new QPDB object. This is usually done by QueryPath itself.
  306. */
  307. public function __construct(QueryPath $qp) {
  308. $this->qp = $qp;
  309. // By default, we set it up to use the base DB.
  310. $this->db = self::$con;
  311. }
  312. /**
  313. * Create a new connection to the database. Use the PDO DSN syntax for a
  314. * connection string.
  315. *
  316. * This creates a database connection that will last for the duration of
  317. * the QueryPath object. This method ought to be used only in two cases:
  318. * - When you will only run a couple of queries during the life of the
  319. * process.
  320. * - When you need to connect to a database that will only be used for
  321. * a few things.
  322. * Otherwise, you should use {@link QPDB::baseDB} to configure a single
  323. * database connection that all of {@link QueryPath} can share.
  324. *
  325. * <b>Remember to always use {@link QPDB::doneWithQuery()} when you are done
  326. * with a query. It gives PDO a chance to clean up open connections that may
  327. * prevent other instances from accessing or modifying data.</b>
  328. *
  329. * @param string $dsn
  330. * The PDO DSN connection string.
  331. * @param array $options
  332. * Connection options. The following options are supported:
  333. * - username => (string)
  334. * - password => (string)
  335. * - db params => (array) These will be passed into the new PDO object.
  336. * See the PDO documentation for a list of options. By default, the
  337. * only flag set is {@link PDO::ATTR_ERRMODE}, which is set to
  338. * {@link PDO::ERRMODE_EXCEPTION}.
  339. * @return QueryPath
  340. * The QueryPath object.
  341. * @throws PDOException
  342. * The PDO library is configured to throw exceptions, so any of the
  343. * database functions may throw a PDOException.
  344. */
  345. public function dbInit($dsn, $options = array()) {
  346. $this->opts = $options + array(
  347. 'username' => NULL,
  348. 'password' => NULL,
  349. 'db params' => array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION),
  350. );
  351. $this->dsn = $dsn;
  352. $this->db = new PDO($dsn, $this->opts['username'], $this->opts['password'], $this->opts['db params']);
  353. /*
  354. foreach ($this->opts['db params'] as $key => $val)
  355. $this->db->setAttribute($key, $val);
  356. */
  357. return $this->qp;
  358. }
  359. /**
  360. * Execute a SQL query, and store the results.
  361. *
  362. * This will execute a SQL query (as a prepared statement), and then store
  363. * the results internally for later use. The data can be iterated using
  364. * {@link nextRow()}. QueryPath can also be instructed to do internal iteration
  365. * using the {@link withEachRow()} method. Finally, on the occasion that the
  366. * statement itself is needed, {@link getStatement()} can be used.
  367. *
  368. * Use this when you need to access the results of a query, or when the
  369. * parameter to a query should be escaped. If the query takes no external
  370. * parameters and does not return results, you may wish to use the
  371. * (ever so slightly faster) {@link exec()} function instead.
  372. *
  373. * Make sure you use {@link doneWithQuery()} after finishing with the database
  374. * results returned by this method.
  375. *
  376. * <b>Usage</b>
  377. *
  378. * Here is a simple example:
  379. * <code>
  380. * <?php
  381. * QPQDB::baseDB($someDSN);
  382. *
  383. * $args = array(':something' => 'myColumn');
  384. * qp()->query('SELECT :something FROM foo', $args)->doneWithQuery();
  385. * ?>
  386. * </code>
  387. *
  388. * The above would execute the given query, substituting myColumn in place of
  389. * :something before executing the query The {@link doneWithQuery()} method
  390. * indicates that we are not going to use the results for anything. This method
  391. * discards the results.
  392. *
  393. * A more typical use of the query() function would involve inserting data
  394. * using {@link appendColumn()}, {@link prependColumn()}, {@link columnBefore()},
  395. * or {@link columnAfter()}. See the main documentation for {@link QPDB} to view
  396. * a more realistic example.
  397. *
  398. * @param string $sql
  399. * The query to be executed.
  400. * @param array $args
  401. * An associative array of substitutions to make.
  402. * @throws PDOException
  403. * Throws an exception if the query cannot be executed.
  404. */
  405. public function query($sql, $args = array()) {
  406. $this->stmt = $this->db->prepare($sql);
  407. $this->stmt->execute($args);
  408. return $this->qp;
  409. }
  410. /**
  411. * Query and append the results.
  412. *
  413. * Run a query and inject the results directly into the
  414. * elements in the QueryPath object.
  415. *
  416. * If the third argument is empty, the data will be inserted directly into
  417. * the QueryPath elements unaltered. However, if a template is provided in
  418. * the third parameter, the query data will be merged into that template
  419. * and then be added to each QueryPath element.
  420. *
  421. * The template will be merged once for each row, even if no row data is
  422. * appended into the template.
  423. *
  424. * A template is simply a piece of markup labeled for insertion of
  425. * data. See {@link QPTPL} and {@link QPTPL.php} for more information.
  426. *
  427. * Since this does not use a stanard {@link query()}, there is no need
  428. * to call {@link doneWithQuery()} after this method.
  429. *
  430. * @param string $sql
  431. * The SQL query to execute. In this context, the query is typically a
  432. * SELECT statement.
  433. * @param array $args
  434. * An array of arguments to be substituted into the query. See {@link query()}
  435. * for details.
  436. * @param mixed $template
  437. * A template into which query results will be merged prior to being appended
  438. * into the QueryPath. For details on the template, see {@link QPTPL::tpl()}.
  439. * @see QPTPL.php
  440. * @see QPTPL::tpl()
  441. * @see query()
  442. */
  443. public function queryInto($sql, $args = array(), $template = NULL) {
  444. $stmt = $this->db->prepare($sql);
  445. $stmt->setFetchMode(PDO::FETCH_ASSOC);
  446. $stmt->execute($args);
  447. // If no template, put all values in together.
  448. if (empty($template)) {
  449. foreach ($stmt as $row) foreach ($row as $datum) $this->qp->append($datum);
  450. }
  451. // Otherwise, we run the results through a template, and then append.
  452. else {
  453. foreach ($stmt as $row) $this->qp->tpl($template, $row);
  454. }
  455. $stmt->closeCursor();
  456. return $this->qp;
  457. }
  458. /**
  459. * Free up resources when a query is no longer used.
  460. *
  461. * This function should <i>always</i> be called when the database
  462. * results for a query are no longer needed. This frees up the
  463. * database cursor, discards the data, and resets resources for future
  464. * use.
  465. *
  466. * If this method is not called, some PDO database drivers will not allow
  467. * subsequent queries, while others will keep tables in a locked state where
  468. * writes will not be allowed.
  469. *
  470. * @return QueryPath
  471. * The QueryPath object.
  472. */
  473. public function doneWithQuery() {
  474. if (isset($this->stmt) && $this->stmt instanceof PDOStatement) {
  475. // Some drivers choke if results haven't been iterated.
  476. //while($this->stmt->fetch()) {}
  477. $this->stmt->closeCursor();
  478. }
  479. unset($this->stmt);
  480. $this->row = NULL;
  481. $this->cycleRows = FALSE;
  482. return $this->qp;
  483. }
  484. /**
  485. * Execute a SQL query, but expect no value.
  486. *
  487. * If your SQL query will have parameters, you are encouraged to
  488. * use {@link query()}, which includes built-in SQL Injection
  489. * protection.
  490. *
  491. * @param string $sql
  492. * A SQL statement.
  493. * @throws PDOException
  494. * An exception will be thrown if a query cannot be executed.
  495. */
  496. public function exec($sql) {
  497. $this->db->exec($sql);
  498. return $this->qp;
  499. }
  500. /**
  501. * Advance the query results row cursor.
  502. *
  503. * In a result set where more than one row was returned, this will
  504. * move the pointer to the next row in the set.
  505. *
  506. * The PDO library does not have a consistent way of determining how many
  507. * rows a result set has. The suggested technique is to first execute a
  508. * COUNT() SQL query and get the data from that.
  509. *
  510. * The {@link withEachRow()} method will begin at the next row after the
  511. * currently selected one.
  512. *
  513. * @return QueryPath
  514. * The QueryPath object.
  515. */
  516. public function nextRow() {
  517. $this->row = $this->stmt->fetch(PDO::FETCH_ASSOC);
  518. return $this->qp;
  519. }
  520. /**
  521. * Set the object to use each row, instead of only one row.
  522. *
  523. * This is used primarily to instruct QPDB to iterate through all of the
  524. * rows when appending, prepending, inserting before, or inserting after.
  525. *
  526. * @return QueryPath
  527. * The QueryPath object.
  528. * @see appendColumn()
  529. * @see prependColumn()
  530. * @see columnBefore()
  531. * @see columnAfter()
  532. */
  533. public function withEachRow() {
  534. $this->cycleRows = TRUE;
  535. return $this->qp;
  536. }
  537. /**
  538. * This is the implementation behind the append/prepend and before/after methods.
  539. *
  540. * @param mixed $columnName
  541. * The name of the column whose data should be added to the currently selected
  542. * elements. This can be either a string or an array of strings.
  543. * @param string $qpFunc
  544. * The name of the QueryPath function that should be executed to insert data
  545. * into the object.
  546. * @param string $wrap
  547. * The HTML/XML markup that will be used to wrap around the column data before
  548. * the data is inserted into the QueryPath object.
  549. */
  550. protected function addData($columnName, $qpFunc = 'append', $wrap = NULL) {
  551. $columns = is_array($columnName) ? $columnName : array($columnName);
  552. $hasWrap = !empty($wrap);
  553. if ($this->cycleRows) {
  554. while (($row = $this->stmt->fetch(PDO::FETCH_ASSOC)) !== FALSE) {
  555. foreach ($columns as $col) {
  556. if (isset($row[$col])) {
  557. $data = $row[$col];
  558. if ($hasWrap)
  559. $data = qp()->append($wrap)->deepest()->append($data)->top();
  560. $this->qp->$qpFunc($data);
  561. }
  562. }
  563. }
  564. $this->cycleRows = FALSE;
  565. $this->doneWithQuery();
  566. }
  567. else {
  568. if ($this->row !== FALSE) {
  569. foreach ($columns as $col) {
  570. if (isset($this->row[$col])) {
  571. $data = $this->row[$col];
  572. if ($hasWrap)
  573. $data = qp()->append($wrap)->deepest()->append($data)->top();
  574. $this->qp->$qpFunc($data);
  575. }
  576. }
  577. }
  578. }
  579. return $this->qp;
  580. }
  581. /**
  582. * Get back the raw PDOStatement object after a {@link query()}.
  583. *
  584. * @return PDOStatement
  585. * Return the PDO statement object. If this is called and no statement
  586. * has been executed (or the statement has already been cleaned up),
  587. * this will return NULL.
  588. */
  589. public function getStatement() {
  590. return $this->stmt;
  591. }
  592. /**
  593. * Get the last insert ID.
  594. *
  595. * This will only return a meaningful result when used after an INSERT.
  596. *
  597. * @return mixed
  598. * Return the ID from the last insert. The value and behavior of this
  599. * is database-dependent. See the official PDO driver documentation for
  600. * the database you are using.
  601. * @since 1.3
  602. */
  603. public function getLastInsertID() {
  604. $con = self::$con;
  605. return $con->lastInsertId();
  606. }
  607. /**
  608. * Append the data in the given column(s) to the QueryPath.
  609. *
  610. * This appends data to every item in the current QueryPath. The data will
  611. * be retrieved from the database result, using $columnName as the key.
  612. *
  613. * @param mixed $columnName
  614. * Either a string or an array of strings. The value(s) here should match
  615. * one or more column headers from the current SQL {@link query}'s results.
  616. * @param string $wrap
  617. * IF this is supplied, then the value or values retrieved from the database
  618. * will be wrapped in this HTML/XML before being inserted into the QueryPath.
  619. * @see QueryPath::wrap()
  620. * @see QueryPath::append()
  621. */
  622. public function appendColumn($columnName, $wrap = NULL) {
  623. return $this->addData($columnName, 'append', $wrap);
  624. }
  625. /**
  626. * Prepend the data from the given column into the QueryPath.
  627. *
  628. * This takes the data from the given column(s) and inserts it into each
  629. * element currently found in the QueryPath.
  630. * @param mixed $columnName
  631. * Either a string or an array of strings. The value(s) here should match
  632. * one or more column headers from the current SQL {@link query}'s results.
  633. * @param string $wrap
  634. * IF this is supplied, then the value or values retrieved from the database
  635. * will be wrapped in this HTML/XML before being inserted into the QueryPath.
  636. * @see QueryPath::wrap()
  637. * @see QueryPath::prepend()
  638. */
  639. public function prependColumn($columnName, $wrap = NULL) {
  640. return $this->addData($columnName, 'prepend', $wrap);
  641. }
  642. /**
  643. * Insert the data from the given column before each element in the QueryPath.
  644. *
  645. * This inserts the data before each element in the currently matched QueryPath.
  646. *
  647. * @param mixed $columnName
  648. * Either a string or an array of strings. The value(s) here should match
  649. * one or more column headers from the current SQL {@link query}'s results.
  650. * @param string $wrap
  651. * IF this is supplied, then the value or values retrieved from the database
  652. * will be wrapped in this HTML/XML before being inserted into the QueryPath.
  653. * @see QueryPath::wrap()
  654. * @see QueryPath::before()
  655. * @see prependColumn()
  656. */
  657. public function columnBefore($columnName, $wrap = NULL) {
  658. return $this->addData($columnName, 'before', $wrap);
  659. }
  660. /**
  661. * Insert data from the given column(s) after each element in the QueryPath.
  662. *
  663. * This inserts data from the given columns after each element in the QueryPath
  664. * object. IF HTML/XML is given in the $wrap parameter, then the column data
  665. * will be wrapped in that markup before being inserted into the QueryPath.
  666. *
  667. * @param mixed $columnName
  668. * Either a string or an array of strings. The value(s) here should match
  669. * one or more column headers from the current SQL {@link query}'s results.
  670. * @param string $wrap
  671. * IF this is supplied, then the value or values retrieved from the database
  672. * will be wrapped in this HTML/XML before being inserted into the QueryPath.
  673. * @see QueryPath::wrap()
  674. * @see QueryPath::after()
  675. * @see appendColumn()
  676. */
  677. public function columnAfter($columnName, $wrap = NULL) {
  678. return $this->addData($columnName, 'after', $wrap);
  679. }
  680. }
  681. // The define allows another class to extend this.
  682. if (!defined('QPDB_OVERRIDE'))
  683. QueryPathExtensionRegistry::extend('QPDB');