PageRenderTime 131ms CodeModel.GetById 8ms RepoModel.GetById 0ms app.codeStats 0ms

/classes/PodsData.php

https://github.com/ElmsPark/pods
PHP | 2472 lines | 1477 code | 464 blank | 531 comment | 481 complexity | 677db5e1953003597f49115d0e8ffefd MD5 | raw file
Possible License(s): AGPL-1.0
  1. <?php
  2. /**
  3. * @package Pods
  4. */
  5. class PodsData {
  6. // base
  7. /**
  8. * @var string
  9. */
  10. static protected $prefix = 'pods_';
  11. /**
  12. * @var array
  13. */
  14. static protected $field_types = array();
  15. /**
  16. * @var bool
  17. */
  18. public static $display_errors = true;
  19. /**
  20. * @var PodsAPI
  21. */
  22. public $api = null;
  23. /**
  24. * @var null
  25. */
  26. public $select = null;
  27. /**
  28. * @var null
  29. */
  30. public $table = null;
  31. /**
  32. * @var null
  33. */
  34. public $pod = null;
  35. /**
  36. * @var array|bool|mixed|null|void
  37. */
  38. public $pod_data = null;
  39. /**
  40. * @var int
  41. */
  42. public $id = 0;
  43. /**
  44. * @var string
  45. */
  46. public $field_id = 'id';
  47. /**
  48. * @var string
  49. */
  50. public $field_index = 'name';
  51. /**
  52. * @var string
  53. */
  54. public $field_slug = '';
  55. /**
  56. * @var string
  57. */
  58. public $join = '';
  59. /**
  60. * @var array
  61. */
  62. public $where = array();
  63. /**
  64. * @var array
  65. */
  66. public $where_default = array();
  67. /**
  68. * @var string
  69. */
  70. public $orderby = '';
  71. /**
  72. * @var array
  73. */
  74. public $fields = array();
  75. /**
  76. * @var array
  77. */
  78. public $aliases = array();
  79. /**
  80. * @var
  81. */
  82. public $detail_page;
  83. // data
  84. /**
  85. * @var int
  86. */
  87. public $row_number = -1;
  88. /**
  89. * @var
  90. */
  91. public $data;
  92. /**
  93. * @var
  94. */
  95. public $row;
  96. /**
  97. * @var
  98. */
  99. public $insert_id;
  100. /**
  101. * @var
  102. */
  103. public $total;
  104. /**
  105. * @var
  106. */
  107. public $total_found;
  108. /**
  109. * @var bool
  110. */
  111. public $total_found_calculated;
  112. // pagination
  113. /**
  114. * @var string
  115. */
  116. public $page_var = 'pg';
  117. /**
  118. * @var int
  119. */
  120. public $page = 1;
  121. /**
  122. * @var int
  123. */
  124. public $limit = 15;
  125. /**
  126. * @var bool
  127. */
  128. public $pagination = true;
  129. // search
  130. /**
  131. * @var bool
  132. */
  133. public $search = true;
  134. /**
  135. * @var string
  136. */
  137. public $search_var = 'search';
  138. /**
  139. * @var string
  140. */
  141. public $search_mode = 'int'; // int | text | text_like
  142. /**
  143. * @var string
  144. */
  145. public $search_query = '';
  146. /**
  147. * @var array
  148. */
  149. public $search_fields = array();
  150. /**
  151. * @var string
  152. */
  153. public $search_where = array();
  154. /**
  155. * @var array
  156. */
  157. public $filters = array();
  158. /**
  159. * Holds Traversal information about Pods
  160. *
  161. * @var array
  162. */
  163. public $traversal = array();
  164. /**
  165. * Holds custom Traversals to be included
  166. *
  167. * @var array
  168. */
  169. public $traverse = array();
  170. /**
  171. * Last select() query SQL
  172. *
  173. * @var string
  174. */
  175. public $sql = false;
  176. /**
  177. * Last total sql
  178. *
  179. * @var string
  180. */
  181. public $total_sql = false;
  182. /**
  183. * Data Abstraction Class for Pods
  184. *
  185. * @param string $pod Pod name
  186. * @param integer $id Pod Item ID
  187. * @param bool $strict If true throws an error if a pod is not found.
  188. *
  189. * @license http://www.gnu.org/licenses/gpl-2.0.html
  190. * @since 2.0.0
  191. */
  192. public function __construct ( $pod = null, $id = 0, $strict = true ) {
  193. global $wpdb;
  194. if ( is_object( $pod ) && 'PodsAPI' == get_class( $pod ) ) {
  195. $this->api = $pod;
  196. $pod = $this->api->pod;
  197. }
  198. else
  199. $this->api = pods_api( $pod );
  200. $this->api->display_errors =& self::$display_errors;
  201. if ( !empty( $pod ) ) {
  202. $this->pod_data =& $this->api->pod_data;
  203. if ( false === $this->pod_data ) {
  204. if ( true === $strict )
  205. return pods_error( 'Pod not found', $this );
  206. else
  207. return $this;
  208. }
  209. $this->pod_id = $this->pod_data[ 'id' ];
  210. $this->pod = $this->pod_data[ 'name' ];
  211. $this->fields = $this->pod_data[ 'fields' ];
  212. if ( isset( $this->pod_data[ 'options' ][ 'detail_url' ] ) )
  213. $this->detail_page = $this->pod_data[ 'options' ][ 'detail_url' ];
  214. if ( isset( $this->pod_data[ 'select' ] ) )
  215. $this->select = $this->pod_data[ 'select' ];
  216. if ( isset( $this->pod_data[ 'table' ] ) )
  217. $this->table = $this->pod_data[ 'table' ];
  218. if ( isset( $this->pod_data[ 'join' ] ) )
  219. $this->join = $this->pod_data[ 'join' ];
  220. if ( isset( $this->pod_data[ 'field_id' ] ) )
  221. $this->field_id = $this->pod_data[ 'field_id' ];
  222. if ( isset( $this->pod_data[ 'field_index' ] ) )
  223. $this->field_index = $this->pod_data[ 'field_index' ];
  224. if ( isset( $this->pod_data[ 'field_slug' ] ) )
  225. $this->field_slug = $this->pod_data[ 'field_slug' ];
  226. if ( isset( $this->pod_data[ 'where' ] ) )
  227. $this->where = $this->pod_data[ 'where' ];
  228. if ( isset( $this->pod_data[ 'where_default' ] ) )
  229. $this->where_default = $this->pod_data[ 'where_default' ];
  230. if ( isset( $this->pod_data[ 'orderby' ] ) )
  231. $this->orderby = $this->pod_data[ 'orderby' ];
  232. if ( null !== $id && !is_array( $id ) && !is_object( $id ) ) {
  233. $this->id = $id;
  234. $this->fetch( $this->id );
  235. }
  236. }
  237. }
  238. /**
  239. * Insert an item, eventually mapping to WPDB::insert
  240. *
  241. * @param string $table Table name
  242. * @param array $data Data to insert (in column => value pairs). Both $data columns and $data values should be "raw" (neither should be SQL escaped).
  243. * @param array $format (optional) An array of formats to be mapped to each of the value in $data.
  244. *
  245. * @return int|bool The ID of the item
  246. *
  247. * @uses wpdb::insert
  248. *
  249. * @since 2.0.0
  250. */
  251. public function insert ( $table, $data, $format = null ) {
  252. /**
  253. * @var $wpdb wpdb
  254. */
  255. global $wpdb;
  256. if ( strlen( $table ) < 1 || empty( $data ) || !is_array( $data ) )
  257. return false;
  258. if ( empty( $format ) ) {
  259. $format = array();
  260. foreach ( $data as $field ) {
  261. if ( isset( self::$field_types[ $field ] ) )
  262. $format[] = self::$field_types[ $field ];
  263. elseif ( isset( $wpdb->field_types[ $field ] ) )
  264. $format[] = $wpdb->field_types[ $field ];
  265. else
  266. break;
  267. }
  268. }
  269. list( $table, $data, $format ) = $this->do_hook( 'insert', array( $table, $data, $format ) );
  270. $result = $wpdb->insert( $table, $data, $format );
  271. $this->insert_id = $wpdb->insert_id;
  272. if ( false !== $result )
  273. return $this->insert_id;
  274. return false;
  275. }
  276. /**
  277. * @static
  278. *
  279. * Insert into a table, if unique key exists just update values.
  280. *
  281. * Data must be a key value pair array, keys act as table rows.
  282. *
  283. * Returns the prepared query from wpdb or false for errors
  284. *
  285. * @param string $table Name of the table to update
  286. * @param array $data column => value pairs
  287. * @param array $formats For $wpdb->prepare, uses sprintf formatting
  288. *
  289. * @return mixed Sanitized query string
  290. *
  291. * @uses wpdb::prepare
  292. *
  293. * @since 2.0.0
  294. */
  295. public static function insert_on_duplicate ( $table, $data, $formats = array() ) {
  296. /**
  297. * @var $wpdb wpdb
  298. */
  299. global $wpdb;
  300. $columns = array_keys( $data );
  301. $update = array();
  302. $values = array();
  303. foreach ( $columns as $column ) {
  304. $update[] = "`{$column}` = VALUES( `{$column}` )";
  305. $values[] = "%s";
  306. }
  307. if ( empty( $formats ) )
  308. $formats = $values;
  309. $columns_data = implode( '`, `', $columns );
  310. $formats = implode( ", ", $formats );
  311. $update = implode( ', ', $update );
  312. $sql = "INSERT INTO `{$table}` ( `{$columns_data}` ) VALUES ( {$formats} ) ON DUPLICATE KEY UPDATE {$update}";
  313. return $wpdb->prepare( $sql, $data );
  314. }
  315. /**
  316. * Update an item, eventually mapping to WPDB::update
  317. *
  318. * @param string $table Table name
  319. * @param array $data Data to update (in column => value pairs). Both $data columns and $data values should be "raw" (neither should be SQL escaped).
  320. * @param array $where A named array of WHERE clauses (in column => value pairs). Multiple clauses will be joined with ANDs. Both $where columns and $where values should be "raw".
  321. * @param array $format (optional) An array of formats to be mapped to each of the values in $data.
  322. * @param array $where_format (optional) An array of formats to be mapped to each of the values in $where.
  323. *
  324. * @return bool
  325. * @since 2.0.0
  326. */
  327. public function update ( $table, $data, $where, $format = null, $where_format = null ) {
  328. /**
  329. * @var $wpdb wpdb
  330. */
  331. global $wpdb;
  332. if ( strlen( $table ) < 1 || empty( $data ) || !is_array( $data ) )
  333. return false;
  334. if ( empty( $format ) ) {
  335. $format = array();
  336. foreach ( $data as $field ) {
  337. if ( isset( self::$field_types[ $field ] ) )
  338. $form = self::$field_types[ $field ];
  339. elseif ( isset( $wpdb->field_types[ $field ] ) )
  340. $form = $wpdb->field_types[ $field ];
  341. else
  342. $form = '%s';
  343. $format[] = $form;
  344. }
  345. }
  346. if ( empty( $where_format ) ) {
  347. $where_format = array();
  348. foreach ( (array) array_keys( $where ) as $field ) {
  349. if ( isset( self::$field_types[ $field ] ) )
  350. $form = self::$field_types[ $field ];
  351. elseif ( isset( $wpdb->field_types[ $field ] ) )
  352. $form = $wpdb->field_types[ $field ];
  353. else
  354. $form = '%s';
  355. $where_format[] = $form;
  356. }
  357. }
  358. list( $table, $data, $where, $format, $where_format ) = $this->do_hook( 'update', array(
  359. $table,
  360. $data,
  361. $where,
  362. $format,
  363. $where_format
  364. ) );
  365. $result = $wpdb->update( $table, $data, $where, $format, $where_format );
  366. if ( false !== $result )
  367. return true;
  368. return false;
  369. }
  370. /**
  371. * Delete an item
  372. *
  373. * @param string $table Table name
  374. * @param array $where A named array of WHERE clauses (in column => value pairs). Multiple clauses will be joined with ANDs. Both $where columns and $where values should be "raw".
  375. * @param array $where_format (optional) An array of formats to be mapped to each of the values in $where.
  376. *
  377. * @return array|bool|mixed|null|void
  378. *
  379. * @uses PodsData::query
  380. * @uses PodsData::prepare
  381. *
  382. * @since 2.0.0
  383. */
  384. public function delete ( $table, $where, $where_format = null ) {
  385. /**
  386. * @var $wpdb wpdb
  387. */
  388. global $wpdb;
  389. if ( strlen( $table ) < 1 || empty( $where ) || !is_array( $where ) )
  390. return false;
  391. $wheres = array();
  392. $where_formats = $where_format = (array) $where_format;
  393. foreach ( (array) array_keys( $where ) as $field ) {
  394. if ( !empty( $where_format ) )
  395. $form = ( $form = array_shift( $where_formats ) ) ? $form : $where_format[ 0 ];
  396. elseif ( isset( self::$field_types[ $field ] ) )
  397. $form = self::$field_types[ $field ];
  398. elseif ( isset( $wpdb->field_types[ $field ] ) )
  399. $form = $wpdb->field_types[ $field ];
  400. else
  401. $form = '%s';
  402. $wheres[] = "`{$field}` = {$form}";
  403. }
  404. $sql = "DELETE FROM `$table` WHERE " . implode( ' AND ', $wheres );
  405. list( $sql, $where ) = $this->do_hook( 'delete', array(
  406. $sql,
  407. array_values( $where )
  408. ), $table, $where, $where_format, $wheres );
  409. return $this->query( self::prepare( $sql, $where ) );
  410. }
  411. /**
  412. * Select items, eventually building dynamic query
  413. *
  414. * @param array $params
  415. *
  416. * @return array|bool|mixed
  417. * @since 2.0.0
  418. */
  419. public function select ( $params ) {
  420. $cache_key = $results = false;
  421. // Debug purposes
  422. if ( 1 == pods_var( 'pods_debug_params', 'get', 0 ) && is_user_logged_in() && ( is_super_admin() || current_user_can( 'delete_users' ) || current_user_can( 'pods' ) ) )
  423. pods_debug( $params );
  424. // Get from cache if enabled
  425. if ( null !== pods_var( 'expires', $params, null, null, true ) ) {
  426. $cache_key = md5( serialize( get_object_vars( $params ) ) );
  427. $results = pods_view_get( $cache_key, pods_var( 'cache_mode', $params, 'cache', null, true ), 'pods_data_select' );
  428. if ( empty( $results ) )
  429. $results = false;
  430. }
  431. if ( empty( $results ) ) {
  432. // Build
  433. $this->sql = $this->build( $params );
  434. // Debug purposes
  435. if ( ( 1 == pods_var( 'pods_debug_sql', 'get', 0 ) || 1 == pods_var( 'pods_debug_sql_all', 'get', 0 ) ) && is_user_logged_in() && ( is_super_admin() || current_user_can( 'delete_users' ) || current_user_can( 'pods' ) ) )
  436. echo "<textarea cols='100' rows='24'>{$this->sql}</textarea>";
  437. if ( empty( $this->sql ) )
  438. return array();
  439. // Get Data
  440. $results = pods_query( $this->sql, $this );
  441. // Cache if enabled
  442. if ( false !== $cache_key )
  443. pods_view_set( $cache_key, $results, pods_var( 'expires', $params, 0, null, true ), pods_var( 'cache_mode', $params, 'cache', null, true ), 'pods_data_select' );
  444. }
  445. $results = $this->do_hook( 'select', $results );
  446. $this->data = $results;
  447. $this->row_number = -1;
  448. // Fill in empty field data (if none provided)
  449. if ( ( !isset( $this->fields ) || empty( $this->fields ) ) && !empty( $this->data ) ) {
  450. $this->fields = array();
  451. $data = (array) @current( $this->data );
  452. foreach ( $data as $data_key => $data_value ) {
  453. $this->fields[ $data_key ] = array( 'label' => ucwords( str_replace( '-', ' ', str_replace( '_', ' ', $data_key ) ) ) );
  454. }
  455. $this->fields = PodsForm::fields_setup( $this->fields );
  456. }
  457. $this->total_found_calculated = false;
  458. $this->total = count( (array) $this->data );
  459. return $this->data;
  460. }
  461. public function calculate_totals () {
  462. /**
  463. * @var $wpdb wpdb
  464. */
  465. global $wpdb;
  466. // Set totals
  467. if ( false !== $this->total_sql )
  468. $total = @current( $wpdb->get_col( $this->get_sql( $this->total_sql ) ) );
  469. else
  470. $total = @current( $wpdb->get_col( "SELECT FOUND_ROWS()" ) );
  471. $total = $this->do_hook( 'select_total', $total );
  472. $this->total_found = 0;
  473. $this->total_found_calculated = true;
  474. if ( is_numeric( $total ) )
  475. $this->total_found = $total;
  476. }
  477. /**
  478. * Build/Rewrite dynamic SQL and handle search/filter/sort
  479. *
  480. * @param array $params
  481. *
  482. * @return bool|mixed|string
  483. * @since 2.0.0
  484. */
  485. public function build ( $params ) {
  486. $simple_tableless_objects = PodsForm::field_method( 'pick', 'simple_objects' );
  487. $defaults = array(
  488. 'select' => '*',
  489. 'distinct' => true,
  490. 'table' => null,
  491. 'join' => null,
  492. 'where' => null,
  493. 'groupby' => null,
  494. 'having' => null,
  495. 'orderby' => null,
  496. 'limit' => -1,
  497. 'offset' => null,
  498. 'id' => null,
  499. 'index' => null,
  500. 'page' => 1,
  501. 'pagination' => $this->pagination,
  502. 'search' => $this->search,
  503. 'search_query' => null,
  504. 'search_mode' => null,
  505. 'search_across' => false,
  506. 'search_across_picks' => false,
  507. 'search_across_files' => false,
  508. 'filters' => array(),
  509. 'fields' => array(),
  510. 'traverse' => array(),
  511. 'sql' => null,
  512. 'strict' => false
  513. );
  514. $params = (object) array_merge( $defaults, (array) $params );
  515. if ( 0 < strlen( $params->sql ) )
  516. return $params->sql;
  517. // Validate
  518. $params->page = pods_absint( $params->page );
  519. $params->pagination = (boolean) $params->pagination;
  520. if ( 0 == $params->page || !$params->pagination )
  521. $params->page = 1;
  522. $params->limit = (int) $params->limit;
  523. if ( 0 == $params->limit )
  524. $params->limit = -1;
  525. $this->limit = $params->limit;
  526. $offset = ( $params->limit * ( $params->page - 1 ) );
  527. if ( 0 < (int) $params->offset )
  528. $params->offset += $offset;
  529. else
  530. $params->offset = $offset;
  531. if ( !$params->pagination || -1 == $params->limit )
  532. $params->offset = 0;
  533. if ( ( empty( $params->fields ) || !is_array( $params->fields ) ) && is_array( $this->pod_data ) && isset( $this->fields ) && !empty( $this->fields ) )
  534. $params->fields = $this->fields;
  535. if ( empty( $params->index ) )
  536. $params->index = $this->field_index;
  537. if ( empty( $params->id ) )
  538. $params->id = $this->field_id;
  539. if ( empty( $params->table ) && is_array( $this->pod_data ) && isset( $this->table ) && !empty( $this->table ) )
  540. $params->table = $this->table;
  541. if ( empty( $params->table ) )
  542. return false;
  543. if ( false === strpos( $params->table, '(' ) && false === strpos( $params->table, '`' ) )
  544. $params->table = '`' . $params->table . '`';
  545. if ( !empty( $params->join ) )
  546. $params->join = array_merge( (array) $this->join, (array) $params->join );
  547. elseif ( false === $params->strict )
  548. $params->join = $this->join;
  549. if ( false === $params->strict && !empty( $this->where ) ) {
  550. if ( empty( $params->where ) && !empty( $this->where_default ) )
  551. $params->where = array_values( (array) $this->where_default );
  552. $params->where = array_merge( (array) $params->where, array_values( (array) $this->where ) );
  553. }
  554. // Allow where array ( 'field' => 'value' ) and WP_Query meta_query syntax
  555. $params->where = $this->query_fields( (array) $params->where, $this->pod_data );
  556. if ( empty( $params->where ) )
  557. $params->where = array();
  558. // Allow having array ( 'field' => 'value' ) and WP_Query meta_query syntax
  559. $params->having = $this->query_fields( (array) $params->having, $this->pod_data );
  560. if ( empty( $params->having ) )
  561. $params->having = array();
  562. if ( !empty( $params->orderby ) )
  563. $params->orderby = (array) $params->orderby;
  564. else
  565. $params->orderby = array();
  566. if ( false === $params->strict && !empty( $this->orderby ) )
  567. $params->orderby = array_merge( $params->orderby, (array) $this->orderby );
  568. if ( !empty( $params->traverse ) )
  569. $this->traverse = $params->traverse;
  570. $allowed_search_modes = array( 'int', 'text', 'text_like' );
  571. if ( !empty( $params->search_mode ) && in_array( $params->search_mode, $allowed_search_modes ) )
  572. $this->search_mode = $params->search_mode;
  573. $params->search = (boolean) $params->search;
  574. // Get Aliases for future reference
  575. $selectsfound = '';
  576. if ( !empty( $params->select ) ) {
  577. if ( is_array( $params->select ) )
  578. $selectsfound = implode( ', ', $params->select );
  579. else
  580. $selectsfound = $params->select;
  581. }
  582. // Pull Aliases from SQL query too
  583. if ( null !== $params->sql ) {
  584. $temp_sql = ' ' . trim( str_replace( array( "\n", "\r" ), ' ', $params->sql ) );
  585. $temp_sql = preg_replace( array(
  586. '/\sSELECT\sSQL_CALC_FOUND_ROWS\s/i',
  587. '/\sSELECT\s/i'
  588. ),
  589. array(
  590. ' SELECT ',
  591. ' SELECT SQL_CALC_FOUND_ROWS '
  592. ),
  593. $temp_sql );
  594. preg_match( '/\sSELECT SQL_CALC_FOUND_ROWS\s(.*)\sFROM/i', $temp_sql, $selectmatches );
  595. if ( isset( $selectmatches[ 1 ] ) && !empty( $selectmatches[ 1 ] ) && false !== stripos( $selectmatches[ 1 ], ' AS ' ) )
  596. $selectsfound .= ( !empty( $selectsfound ) ? ', ' : '' ) . $selectmatches[ 1 ];
  597. }
  598. // Build Alias list
  599. $this->aliases = array();
  600. if ( !empty( $selectsfound ) && false !== stripos( $selectsfound, ' AS ' ) ) {
  601. $theselects = array_filter( explode( ', ', $selectsfound ) );
  602. if ( empty( $theselects ) )
  603. $theselects = array_filter( explode( ',', $selectsfound ) );
  604. foreach ( $theselects as $selected ) {
  605. $selected = trim( $selected );
  606. if ( strlen( $selected ) < 1 )
  607. continue;
  608. $selectfield = explode( ' AS ', str_replace( ' as ', ' AS ', $selected ) );
  609. if ( 2 == count( $selectfield ) ) {
  610. $field = trim( trim( $selectfield[ 1 ] ), '`' );
  611. $real_field = trim( trim( $selectfield[ 0 ] ), '`' );
  612. $this->aliases[ $field ] = $real_field;
  613. }
  614. }
  615. }
  616. // Search
  617. if ( !empty( $params->search ) && !empty( $params->fields ) ) {
  618. if ( false !== $params->search_query && 0 < strlen( $params->search_query ) ) {
  619. $where = $having = array();
  620. if ( false !== $params->search_across ) {
  621. foreach ( $params->fields as $key => $field ) {
  622. if ( is_array( $field ) ) {
  623. $attributes = $field;
  624. $field = pods_var( 'name', $field, $key, null, true );
  625. }
  626. else {
  627. $attributes = array(
  628. 'type' => '',
  629. 'options' => array()
  630. );
  631. }
  632. if ( isset( $attributes[ 'options' ][ 'search' ] ) && !$attributes[ 'options' ][ 'search' ] )
  633. continue;
  634. if ( in_array( $attributes[ 'type' ], array( 'date', 'time', 'datetime', 'number', 'decimal', 'currency', 'phone', 'password', 'boolean' ) ) )
  635. continue;
  636. if ( in_array( $field, $params->filters ) )
  637. continue;
  638. $fieldfield = '`' . $field . '`';
  639. if ( 'pick' == $attributes[ 'type' ] && !in_array( pods_var( 'pick_object', $attributes ), $simple_tableless_objects ) ) {
  640. if ( false === $params->search_across_picks )
  641. continue;
  642. else {
  643. if ( !isset( $attributes[ 'table_info' ] ) || empty( $attributes[ 'table_info' ] ) )
  644. $attributes[ 'table_info' ] = $this->api->get_table_info( pods_var( 'pick_object', $attributes ), pods_var( 'pick_val', $attributes ) );
  645. if ( empty( $attributes[ 'table_info' ][ 'field_index' ] ) )
  646. continue;
  647. $fieldfield = $fieldfield . '.`' . $attributes[ 'table_info' ][ 'field_index' ] . '`';
  648. }
  649. }
  650. elseif ( in_array( $attributes[ 'type' ], apply_filters( 'pods_file_field_types', array( 'file', 'avatar' ) ) ) ) {
  651. if ( false === $params->search_across_files )
  652. continue;
  653. else
  654. $fieldfield = $fieldfield . '.`post_title`';
  655. }
  656. else
  657. $fieldfield = '`t`.' . $fieldfield;
  658. if ( isset( $this->aliases[ $field ] ) )
  659. $fieldfield = '`' . $this->aliases[ $field ] . '`';
  660. if ( isset( $attributes[ 'real_name' ] ) && !empty( $attributes[ 'real_name' ] ) )
  661. $fieldfield = $attributes[ 'real_name' ];
  662. if ( isset( $attributes[ 'group_related' ] ) && false !== $attributes[ 'group_related' ] )
  663. $having[] = "{$fieldfield} LIKE '%" . pods_sanitize( $params->search_query ) . "%'";
  664. else
  665. $where[] = "{$fieldfield} LIKE '%" . pods_sanitize( $params->search_query ) . "%'";
  666. }
  667. }
  668. elseif ( !empty( $params->index ) ) {
  669. $attributes = array();
  670. if ( isset( $params->fields[ $params->index ] ) )
  671. $attributes = $params->fields[ $params->index ];
  672. $fieldfield = '`t`.`' . $params->index . '`';
  673. if ( isset( $attributes[ 'real_name' ] ) && false !== $attributes[ 'real_name' ] && !empty( $attributes[ 'real_name' ] ) )
  674. $fieldfield = $attributes[ 'real_name' ];
  675. if ( isset( $attributes[ 'group_related' ] ) && false !== $attributes[ 'group_related' ] )
  676. $having[] = "{$fieldfield} LIKE '%" . pods_sanitize( $params->search_query ) . "%'";
  677. else
  678. $where[] = "{$fieldfield} LIKE '%" . pods_sanitize( $params->search_query ) . "%'";
  679. }
  680. if ( !empty( $where ) )
  681. $params->where[] = '(' . implode( ' OR ', $where ) . ')';
  682. if ( !empty( $having ) )
  683. $params->having[] = '(' . implode( ' OR ', $having ) . ')';
  684. }
  685. // Filter
  686. foreach ( $params->filters as $filter ) {
  687. $where = $having = array();
  688. if ( !isset( $params->fields[ $filter ] ) )
  689. continue;
  690. $attributes = $params->fields[ $filter ];
  691. $field = pods_var( 'name', $attributes, $filter, null, true );
  692. $filterfield = '`' . $field . '`';
  693. if ( 'pick' == $attributes[ 'type' ] && !in_array( pods_var( 'pick_object', $attributes ), $simple_tableless_objects ) ) {
  694. if ( !isset( $attributes[ 'table_info' ] ) || empty( $attributes[ 'table_info' ] ) )
  695. $attributes[ 'table_info' ] = $this->api->get_table_info( pods_var( 'pick_object', $attributes ), pods_var( 'pick_val', $attributes ) );
  696. if ( empty( $attributes[ 'table_info' ][ 'field_index' ] ) )
  697. continue;
  698. $filterfield = $filterfield . '.`' . $attributes[ 'table_info' ][ 'field_index' ] . '`';
  699. }
  700. elseif ( in_array( $attributes[ 'type' ], apply_filters( 'pods_file_field_types', array( 'file', 'avatar' ) ) ) )
  701. $filterfield = $filterfield . '.`post_title`';
  702. else
  703. $filterfield = '`t`.' . $filterfield;
  704. if ( isset( $this->aliases[ $field ] ) )
  705. $filterfield = '`' . $this->aliases[ $field ] . '`';
  706. if ( isset( $attributes[ 'real_name' ] ) && false !== $attributes[ 'real_name' ] && !empty( $attributes[ 'real_name' ] ) )
  707. $filterfield = $attributes[ 'real_name' ];
  708. if ( 'pick' == $attributes[ 'type' ] ) {
  709. $filter_value = pods_var( 'filter_' . $field, 'get', false, null, true );
  710. if ( in_array( pods_var( 'pick_object', $attributes ), $simple_tableless_objects ) ) {
  711. if ( strlen( $filter_value ) < 1 )
  712. continue;
  713. if ( isset( $attributes[ 'group_related' ] ) && false !== $attributes[ 'group_related' ] ) {
  714. $having[] = "( {$filterfield} = '" . $filter_value . "'"
  715. . " OR {$filterfield} LIKE '%\"" . $filter_value . "\"%' )";
  716. }
  717. else {
  718. $where[] = "( {$filterfield} = '" . $filter_value . "'"
  719. . " OR {$filterfield} LIKE '%\"" . $filter_value . "\"%' )";
  720. }
  721. }
  722. else {
  723. $filter_value = (int) $filter_value;
  724. if ( empty( $filter_value ) || empty( $attributes[ 'table_info' ][ 'field_id' ] ) )
  725. continue;
  726. $filterfield = '`' . $field . '`.`' . $attributes[ 'table_info' ][ 'field_id' ] . '`';
  727. if ( isset( $attributes[ 'group_related' ] ) && false !== $attributes[ 'group_related' ] )
  728. $having[] = "{$filterfield} = " . $filter_value;
  729. else
  730. $where[] = "{$filterfield} = " . $filter_value;
  731. }
  732. }
  733. elseif ( in_array( $attributes[ 'type' ], array( 'date', 'datetime' ) ) ) {
  734. $start = date_i18n( 'Y-m-d' ) . ( 'datetime' == $attributes[ 'type' ] ) ? ' 00:00:00' : '';
  735. $end = date_i18n( 'Y-m-d' ) . ( 'datetime' == $attributes[ 'type' ] ) ? ' 23:59:59' : '';
  736. $start_value = pods_var( 'filter_' . $field . '_start', 'get', false );
  737. $end_value = pods_var( 'filter_' . $field . '_end', 'get', false );
  738. if ( empty( $start_value ) && empty( $end_value ) )
  739. continue;
  740. if ( !empty( $start_value ) )
  741. $start = date_i18n( 'Y-m-d', strtotime( $start_value ) ) . ( 'datetime' == $attributes[ 'type' ] ? ' 00:00:00' : '' );
  742. if ( !empty( $end_value ) )
  743. $end = date_i18n( 'Y-m-d', strtotime( $end_value ) ) . ( 'datetime' == $attributes[ 'type' ] ? ' 23:59:59' : '' );
  744. if ( true === $attributes[ 'date_ongoing' ] ) {
  745. $date_ongoing = '`' . $attributes[ 'date_ongoing' ] . '`';
  746. if ( isset( $this->aliases[ $date_ongoing ] ) )
  747. $date_ongoing = '`' . $this->aliases[ $date_ongoing ] . '`';
  748. if ( isset( $attributes[ 'group_related' ] ) && false !== $attributes[ 'group_related' ] )
  749. $having[] = "(({$filterfield} <= '$start' OR ({$filterfield} >= '$start' AND {$filterfield} <= '$end')) AND ({$date_ongoing} >= '$start' OR ({$date_ongoing} >= '$start' AND {$date_ongoing} <= '$end')))";
  750. else
  751. $where[] = "(({$filterfield} <= '$start' OR ({$filterfield} >= '$start' AND {$filterfield} <= '$end')) AND ({$date_ongoing} >= '$start' OR ({$date_ongoing} >= '$start' AND {$date_ongoing} <= '$end')))";
  752. }
  753. else {
  754. if ( isset( $attributes[ 'group_related' ] ) && false !== $attributes[ 'group_related' ] )
  755. $having[] = "({$filterfield} BETWEEN '$start' AND '$end')";
  756. else
  757. $where[] = "({$filterfield} BETWEEN '$start' AND '$end')";
  758. }
  759. }
  760. else {
  761. $filter_value = pods_var( 'filter_' . $field, 'get', '' );
  762. if ( strlen( $filter_value ) < 1 )
  763. continue;
  764. if ( isset( $attributes[ 'group_related' ] ) && false !== $attributes[ 'group_related' ] )
  765. $having[] = "{$filterfield} LIKE '%" . $filter_value . "%'";
  766. else
  767. $where[] = "{$filterfield} LIKE '%" . $filter_value . "%'";
  768. }
  769. if ( !empty( $where ) )
  770. $params->where[] = '(' . implode( ' AND ', $where ) . ')';
  771. if ( !empty( $having ) )
  772. $params->having[] = '(' . implode( ' AND ', $having ) . ')';
  773. }
  774. }
  775. // Traverse the Rabbit Hole
  776. if ( !empty( $this->pod ) ) {
  777. $haystack = implode( ' ', (array) $params->select )
  778. . ' ' . implode( ' ', (array) $params->where )
  779. . ' ' . implode( ' ', (array) $params->groupby )
  780. . ' ' . implode( ' ', (array) $params->having )
  781. . ' ' . implode( ' ', (array) $params->orderby );
  782. $haystack = preg_replace( '/\s/', ' ', $haystack );
  783. $haystack = preg_replace( '/\w\(/', ' ', $haystack );
  784. $haystack = str_replace( array( '(', ')', ' ', '\\\'', "\\\"" ), ' ', $haystack );
  785. preg_match_all( '/`?[\w]+`?(?:\\.`?[\w]+`?)+(?=[^"\']*(?:"[^"]*"[^"]*|\'[^\']*\'[^\']*)*$)/', $haystack, $found, PREG_PATTERN_ORDER );
  786. $found = (array) @current( $found );
  787. $find = $replace = array();
  788. foreach ( $found as $key => $value ) {
  789. $value = str_replace( '`', '', $value );
  790. $value = explode( '.', $value );
  791. $dot = array_pop( $value );
  792. if ( in_array( '/\b' . trim( $found[ $key ], '`' ) . '\b(?=[^"\']*(?:"[^"]*"[^"]*|\'[^\']*\'[^\']*)*$)/', $find ) ) {
  793. unset( $found[ $key ] );
  794. continue;
  795. }
  796. $find[ $key ] = '/\b' . trim( $found[ $key ], '`' ) . '\b(?=[^"\']*(?:"[^"]*"[^"]*|\'[^\']*\'[^\']*)*$)/';
  797. $esc_start = $esc_end = '`';
  798. if ( strlen( ltrim( $found[ $key ], '`' ) ) < strlen( $found[ $key ] ) )
  799. $esc_start = '';
  800. if ( strlen( rtrim( $found[ $key ], '`' ) ) < strlen( $found[ $key ] ) )
  801. $esc_end = '';
  802. if ( '*' != $dot )
  803. $dot = '`' . $dot . $esc_end;
  804. $replace[ $key ] = $esc_start . implode( '_', $value ) . '`.' . $dot;
  805. if ( 't' == $value[ 0 ] ) {
  806. unset( $found[ $key ] );
  807. continue;
  808. }
  809. unset( $found[ $key ] );
  810. if ( !in_array( $value, $found ) )
  811. $found[ $key ] = $value;
  812. }
  813. if ( !empty( $this->traverse ) ) {
  814. foreach ( (array) $this->traverse as $key => $traverse ) {
  815. $traverse = str_replace( '`', '', $traverse );
  816. $already_found = false;
  817. foreach ( $found as $traversal ) {
  818. if ( is_array( $traversal ) )
  819. $traversal = implode( '.', $traversal );
  820. if ( $traversal == $traverse ) {
  821. $already_found = true;
  822. break;
  823. }
  824. }
  825. if ( !$already_found )
  826. $found[ 'traverse_' . $key ] = explode( '.', $traverse );
  827. }
  828. }
  829. $joins = array();
  830. if ( !empty( $find ) ) {
  831. $params->select = preg_replace( $find, $replace, $params->select );
  832. $params->where = preg_replace( $find, $replace, $params->where );
  833. $params->groupby = preg_replace( $find, $replace, $params->groupby );
  834. $params->having = preg_replace( $find, $replace, $params->having );
  835. $params->orderby = preg_replace( $find, $replace, $params->orderby );
  836. if ( !empty( $found ) )
  837. $joins = $this->traverse( $found, $params->fields, $params );
  838. elseif ( false !== $params->search )
  839. $joins = $this->traverse( null, $params->fields, $params );
  840. }
  841. }
  842. // Traversal Search
  843. if ( !empty( $params->search ) && !empty( $this->search_where ) )
  844. $params->where = array_merge( (array) $this->search_where, $params->where );
  845. if ( !empty( $params->join ) && !empty( $joins ) )
  846. $params->join = array_merge( $joins, (array) $params->join );
  847. elseif ( !empty( $joins ) )
  848. $params->join = $joins;
  849. // Build
  850. if ( null === $params->sql ) {
  851. $sql = "
  852. SELECT
  853. " . ( $params->distinct ? 'DISTINCT' : '' ) . "
  854. " . ( !empty( $params->select ) ? ( is_array( $params->select ) ? implode( ', ', $params->select ) : $params->select ) : '*' ) . "
  855. FROM {$params->table} AS `t`
  856. " . ( !empty( $params->join ) ? ( is_array( $params->join ) ? implode( "\n ", $params->join ) : $params->join ) : '' ) . "
  857. " . ( !empty( $params->where ) ? 'WHERE ' . ( is_array( $params->where ) ? implode( ' AND ', $params->where ) : $params->where ) : '' ) . "
  858. " . ( !empty( $params->groupby ) ? 'GROUP BY ' . ( is_array( $params->groupby ) ? implode( ', ', $params->groupby ) : $params->groupby ) : '' ) . "
  859. " . ( !empty( $params->having ) ? 'HAVING ' . ( is_array( $params->having ) ? implode( ' AND ', $params->having ) : $params->having ) : '' ) . "
  860. " . ( !empty( $params->orderby ) ? 'ORDER BY ' . ( is_array( $params->orderby ) ? implode( ', ', $params->orderby ) : $params->orderby ) : '' ) . "
  861. " . ( ( 0 < $params->page && 0 < $params->limit ) ? 'LIMIT ' . $params->offset . ', ' . ( $params->limit ) : '' ) . "
  862. ";
  863. $this->total_sql = "
  864. SELECT
  865. " . ( $params->distinct ? 'DISTINCT' : '' ) . "
  866. COUNT(*)
  867. FROM {$params->table} AS `t`
  868. " . ( !empty( $params->join ) ? ( is_array( $params->join ) ? implode( "\n ", $params->join ) : $params->join ) : '' ) . "
  869. " . ( !empty( $params->where ) ? 'WHERE ' . ( is_array( $params->where ) ? implode( ' AND ', $params->where ) : $params->where ) : '' ) . "
  870. " . ( !empty( $params->groupby ) ? 'GROUP BY ' . ( is_array( $params->groupby ) ? implode( ', ', $params->groupby ) : $params->groupby ) : '' ) . "
  871. " . ( !empty( $params->having ) ? 'HAVING ' . ( is_array( $params->having ) ? implode( ' AND ', $params->having ) : $params->having ) : '' ) . "
  872. ";
  873. }
  874. // Rewrite
  875. else {
  876. $sql = ' ' . trim( str_replace( array( "\n", "\r" ), ' ', $params->sql ) );
  877. $sql = preg_replace( array(
  878. '/\sSELECT\sSQL_CALC_FOUND_ROWS\s/i',
  879. '/\sSELECT\s/i'
  880. ),
  881. array(
  882. ' SELECT ',
  883. ' SELECT SQL_CALC_FOUND_ROWS '
  884. ),
  885. $sql );
  886. // Insert variables based on existing statements
  887. if ( false === stripos( $sql, '%%SELECT%%' ) )
  888. $sql = preg_replace( '/\sSELECT\sSQL_CALC_FOUND_ROWS\s/i', ' SELECT SQL_CALC_FOUND_ROWS %%SELECT%% ', $sql );
  889. if ( false === stripos( $sql, '%%WHERE%%' ) )
  890. $sql = preg_replace( '/\sWHERE\s(?!.*\sWHERE\s)/i', ' WHERE %%WHERE%% ', $sql );
  891. if ( false === stripos( $sql, '%%GROUPBY%%' ) )
  892. $sql = preg_replace( '/\sGROUP BY\s(?!.*\sGROUP BY\s)/i', ' GROUP BY %%GROUPBY%% ', $sql );
  893. if ( false === stripos( $sql, '%%HAVING%%' ) )
  894. $sql = preg_replace( '/\sHAVING\s(?!.*\sHAVING\s)/i', ' HAVING %%HAVING%% ', $sql );
  895. if ( false === stripos( $sql, '%%ORDERBY%%' ) )
  896. $sql = preg_replace( '/\sORDER BY\s(?!.*\sORDER BY\s)/i', ' ORDER BY %%ORDERBY%% ', $sql );
  897. // Insert variables based on other existing statements
  898. if ( false === stripos( $sql, '%%JOIN%%' ) ) {
  899. if ( false !== stripos( $sql, ' WHERE ' ) )
  900. $sql = preg_replace( '/\sWHERE\s(?!.*\sWHERE\s)/i', ' %%JOIN%% WHERE ', $sql );
  901. elseif ( false !== stripos( $sql, ' GROUP BY ' ) )
  902. $sql = preg_replace( '/\sGROUP BY\s(?!.*\sGROUP BY\s)/i', ' %%WHERE%% GROUP BY ', $sql );
  903. elseif ( false !== stripos( $sql, ' ORDER BY ' ) )
  904. $sql = preg_replace( '/\ORDER BY\s(?!.*\ORDER BY\s)/i', ' %%WHERE%% ORDER BY ', $sql );
  905. else
  906. $sql .= ' %%JOIN%% ';
  907. }
  908. if ( false === stripos( $sql, '%%WHERE%%' ) ) {
  909. if ( false !== stripos( $sql, ' GROUP BY ' ) )
  910. $sql = preg_replace( '/\sGROUP BY\s(?!.*\sGROUP BY\s)/i', ' %%WHERE%% GROUP BY ', $sql );
  911. elseif ( false !== stripos( $sql, ' ORDER BY ' ) )
  912. $sql = preg_replace( '/\ORDER BY\s(?!.*\ORDER BY\s)/i', ' %%WHERE%% ORDER BY ', $sql );
  913. else
  914. $sql .= ' %%WHERE%% ';
  915. }
  916. if ( false === stripos( $sql, '%%GROUPBY%%' ) ) {
  917. if ( false !== stripos( $sql, ' HAVING ' ) )
  918. $sql = preg_replace( '/\sHAVING\s(?!.*\sHAVING\s)/i', ' %%GROUPBY%% HAVING ', $sql );
  919. elseif ( false !== stripos( $sql, ' ORDER BY ' ) )
  920. $sql = preg_replace( '/\ORDER BY\s(?!.*\ORDER BY\s)/i', ' %%GROUPBY%% ORDER BY ', $sql );
  921. else
  922. $sql .= ' %%GROUPBY%% ';
  923. }
  924. if ( false === stripos( $sql, '%%HAVING%%' ) ) {
  925. if ( false !== stripos( $sql, ' ORDER BY ' ) )
  926. $sql = preg_replace( '/\ORDER BY\s(?!.*\ORDER BY\s)/i', ' %%HAVING%% ORDER BY ', $sql );
  927. else
  928. $sql .= ' %%HAVING%% ';
  929. }
  930. if ( false === stripos( $sql, '%%ORDERBY%%' ) )
  931. $sql .= ' %%ORDERBY%% ';
  932. if ( false === stripos( $sql, '%%LIMIT%%' ) )
  933. $sql .= ' %%LIMIT%% ';
  934. // Replace variables
  935. if ( 0 < strlen( $params->select ) ) {
  936. if ( false === stripos( $sql, '%%SELECT%% FROM ' ) )
  937. $sql = str_ireplace( '%%SELECT%%', $params->select . ', ', $sql );
  938. else
  939. $sql = str_ireplace( '%%SELECT%%', $params->select, $sql );
  940. }
  941. if ( 0 < strlen( $params->join ) )
  942. $sql = str_ireplace( '%%JOIN%%', $params->join, $sql );
  943. if ( 0 < strlen( $params->where ) ) {
  944. if ( false !== stripos( $sql, ' WHERE ' ) ) {
  945. if ( false !== stripos( $sql, ' WHERE %%WHERE%% ' ) )
  946. $sql = str_ireplace( '%%WHERE%%', $params->where . ' AND ', $sql );
  947. else
  948. $sql = str_ireplace( '%%WHERE%%', ' AND ' . $params->where, $sql );
  949. }
  950. else
  951. $sql = str_ireplace( '%%WHERE%%', ' WHERE ' . $params->where, $sql );
  952. }
  953. if ( 0 < strlen( $params->groupby ) ) {
  954. if ( false !== stripos( $sql, ' GROUP BY ' ) ) {
  955. if ( false !== stripos( $sql, ' GROUP BY %%GROUPBY%% ' ) )
  956. $sql = str_ireplace( '%%GROUPBY%%', $params->groupby . ', ', $sql );
  957. else
  958. $sql = str_ireplace( '%%GROUPBY%%', ', ' . $params->groupby, $sql );
  959. }
  960. else
  961. $sql = str_ireplace( '%%GROUPBY%%', ' GROUP BY ' . $params->groupby, $sql );
  962. }
  963. if ( 0 < strlen( $params->having ) && false !== stripos( $sql, ' GROUP BY ' ) ) {
  964. if ( false !== stripos( $sql, ' HAVING ' ) ) {
  965. if ( false !== stripos( $sql, ' HAVING %%HAVING%% ' ) )
  966. $sql = str_ireplace( '%%HAVING%%', $params->having . ' AND ', $sql );
  967. else
  968. $sql = str_ireplace( '%%HAVING%%', ' AND ' . $params->having, $sql );
  969. }
  970. else
  971. $sql = str_ireplace( '%%HAVING%%', ' HAVING ' . $params->having, $sql );
  972. }
  973. if ( 0 < strlen( $params->orderby ) ) {
  974. if ( false !== stripos( $sql, ' ORDER BY ' ) ) {
  975. if ( false !== stripos( $sql, ' ORDER BY %%ORDERBY%% ' ) )
  976. $sql = str_ireplace( '%%ORDERBY%%', $params->groupby . ', ', $sql );
  977. else
  978. $sql = str_ireplace( '%%ORDERBY%%', ', ' . $params->groupby, $sql );
  979. }
  980. else
  981. $sql = str_ireplace( '%%ORDERBY%%', ' ORDER BY ' . $params->groupby, $sql );
  982. }
  983. if ( 0 < $params->page && 0 < $params->limit ) {
  984. $start = ( $params->page - 1 ) * $params->limit;
  985. $end = $start + $params->limit;
  986. $sql .= 'LIMIT ' . (int) $start . ', ' . (int) $end;
  987. }
  988. // Clear any unused variables
  989. $sql = str_ireplace( array(
  990. '%%SELECT%%',
  991. '%%JOIN%%',
  992. '%%WHERE%%',
  993. '%%GROUPBY%%',
  994. '%%HAVING%%',
  995. '%%ORDERBY%%',
  996. '%%LIMIT%%'
  997. ), '', $sql );
  998. $sql = str_replace( array( '``', '`' ), array( ' ', ' ' ), $sql );
  999. }
  1000. return $sql;
  1001. }
  1002. /**
  1003. * Fetch the total row count returned
  1004. *
  1005. * @return int Number of rows returned by select()
  1006. * @since 2.0.0
  1007. */
  1008. public function total () {
  1009. return (int) $this->total;
  1010. }
  1011. /**
  1012. * Fetch the total row count total
  1013. *
  1014. * @return int Number of rows found by select()
  1015. * @since 2.0.0
  1016. */
  1017. public function total_found () {
  1018. if(false === $this->total_found_calculated)
  1019. $this->calculate_totals();
  1020. return (int) $this->total_found;
  1021. }
  1022. /**
  1023. * Fetch the zebra switch
  1024. *
  1025. * @return bool Zebra state
  1026. * @since 1.12
  1027. */
  1028. public function zebra () {
  1029. $zebra = true;
  1030. if ( 0 < ( $this->row_number % 2 ) ) // Odd numbers
  1031. $zebra = false;
  1032. return $zebra;
  1033. }
  1034. /**
  1035. * Create a Table
  1036. *
  1037. * @param string $table Table name
  1038. * @param string $fields
  1039. * @param boolean $if_not_exists Check if the table exists.
  1040. *
  1041. * @return array|bool|mixed|null|void
  1042. *
  1043. * @uses PodsData::query
  1044. *
  1045. * @since 2.0.0
  1046. */
  1047. public static function table_create ( $table, $fields, $if_not_exists = false ) {
  1048. /**
  1049. * @var $wpdb wpdb
  1050. */
  1051. global $wpdb;
  1052. $sql = "CREATE TABLE";
  1053. if ( true === $if_not_exists )
  1054. $sql .= " IF NOT EXISTS";
  1055. $sql .= " `{$wpdb->prefix}" . self::$prefix . "{$table}` ({$fields})";
  1056. if ( !empty( $wpdb->charset ) )
  1057. $sql .= " DEFAULT CHARACTER SET {$wpdb->charset}";
  1058. if ( !empty( $wpdb->collate ) )
  1059. $sql .= " COLLATE {$wpdb->collate}";
  1060. return self::query( $sql );
  1061. }
  1062. /**
  1063. * Alter a Table
  1064. *
  1065. * @param string $table Table name
  1066. * @param string $changes
  1067. *
  1068. * @return array|bool|mixed|null|void
  1069. *
  1070. * @uses PodsData::query
  1071. *
  1072. * @since 2.0.0
  1073. */
  1074. public static function table_alter ( $table, $changes ) {
  1075. /**
  1076. * @var $wpdb wpdb
  1077. */
  1078. global $wpdb;
  1079. $sql = "ALTER TABLE `{$wpdb->prefix}" . self::$prefix . "{$table}` {$changes}";
  1080. return self::query( $sql );
  1081. }
  1082. /**
  1083. * Truncate a Table
  1084. *
  1085. * @param string $table Table name
  1086. *
  1087. * @return array|bool|mixed|null|void
  1088. *
  1089. * @uses PodsData::query
  1090. *
  1091. * @since 2.0.0
  1092. */
  1093. public static function table_truncate ( $table ) {
  1094. /**
  1095. * @var $wpdb wpdb
  1096. */
  1097. global $wpdb;
  1098. $sql = "TRUNCATE TABLE `{$wpdb->prefix}" . self::$prefix . "{$table}`";
  1099. return self::query( $sql );
  1100. }
  1101. /**
  1102. * Drop a Table
  1103. *
  1104. * @param string $table Table name
  1105. *
  1106. * @uses PodsData::query
  1107. *
  1108. * @return array|bool|mixed|null|void
  1109. *
  1110. * @uses PodsData::query
  1111. *
  1112. * @since 2.0.0
  1113. */
  1114. public static function table_drop ( $table ) {
  1115. /**
  1116. * @var $wpdb wpdb
  1117. */
  1118. global $wpdb;
  1119. $sql = "DROP TABLE `{$wpdb->prefix}" . self::$prefix . "{$table}`";
  1120. return self::query( $sql );
  1121. }
  1122. /**
  1123. * Reorder Items
  1124. *
  1125. * @param string $table Table name
  1126. * @param string $weight_field
  1127. * @param string $id_field
  1128. * @param array $ids
  1129. *
  1130. * @return bool
  1131. *
  1132. * @uses PodsData::update
  1133. *
  1134. * @since 2.0.0
  1135. */
  1136. public function reorder ( $table, $weight_field, $id_field, $ids ) {
  1137. $success = false;
  1138. $ids = (array) $ids;
  1139. list( $table, $weight_field, $id_field, $ids ) = $this->do_hook( 'reorder', array(
  1140. $table,
  1141. $weight_field,
  1142. $id_field,
  1143. $ids
  1144. ) );
  1145. if ( !empty( $ids ) ) {
  1146. $success = true;
  1147. foreach ( $ids as $weight => $id ) {
  1148. $updated = $this->update( $table, array( $weight_field => $weight ), array( $id_field => $id ), array( '%d' ), array( '%d' ) );
  1149. if ( false === $updated )
  1150. $success = false;
  1151. }
  1152. }
  1153. return $success;
  1154. }
  1155. /**
  1156. * Fetch a new row for the current pod_data
  1157. *
  1158. * @param int $row Row number to fetch
  1159. *
  1160. * @return mixed
  1161. *
  1162. * @since 2.0.0
  1163. */
  1164. public function fetch ( $row = null ) {
  1165. global $wpdb;
  1166. $id = $row;
  1167. if ( null === $row ) {
  1168. $this->row_number++;
  1169. $this->row = false;
  1170. if ( isset( $this->data[ $this->row_number ] ) )
  1171. $this->row = get_object_vars( $this->data[ $this->row_number ] );
  1172. }
  1173. else {
  1174. $this->row_number = -1;
  1175. $mode = 'id';
  1176. $id = pods_absint( $row );
  1177. if ( !is_numeric( $row ) || 0 === strpos( $row, '0' ) || $row != preg_replace( '/[^0-9]/', '', $row ) ) {
  1178. $mode = 'slug';
  1179. $id = $row;
  1180. }
  1181. $row = false;
  1182. if ( !empty( $this->pod ) )
  1183. $row = pods_cache_get( $id, 'pods_items_' . $this->pod );
  1184. $get_table_data = false;
  1185. $current_row_id = false;
  1186. if ( false !== $row && is_array( $row ) )
  1187. $this->row = $row;
  1188. elseif ( in_array( $this->pod_data[ 'type' ], array( 'post_type', 'media' ) ) ) {
  1189. if ( 'post_type' == $this->pod_data[ 'type' ] ) {
  1190. $post_type = $this->pod_data[ 'object' ];
  1191. if ( empty( $post_type ) )
  1192. $post_type = $this->pod_data[ 'name' ];
  1193. }
  1194. else
  1195. $post_type = 'attachment';
  1196. if ( 'id' == $mode ) {
  1197. $this->row = get_post( $id, ARRAY_A );
  1198. if ( is_array( $this->row ) && $this->row[ 'post_type' ] != $post_type )
  1199. $this->row = false;
  1200. }
  1201. else {
  1202. $args = array(
  1203. 'post_type' => $post_type,
  1204. 'name' => $id,
  1205. 'numberposts' => 5
  1206. );
  1207. $find = get_posts( $args );
  1208. if ( !empty( $find ) )
  1209. $this->row = get_object_vars( $find[ 0 ] );
  1210. }
  1211. if ( empty( $this->row ) )
  1212. $this->row = false;
  1213. $current_row_id = $this->row['ID'];
  1214. $get_table_data = true;
  1215. }
  1216. elseif ( 'taxonomy' == $this->pod_data[ 'type' ] ) {
  1217. $taxonomy = $this->pod_data[ 'object' ];
  1218. if ( empty( $taxonomy ) )
  1219. $taxonomy = $this->pod_data[ 'name' ];
  1220. if ( 'id' == $mode )
  1221. $this->row = get_term( $id, $taxonomy, ARRAY_A );
  1222. else
  1223. $this->row = get_term_by( 'slug', $id, $taxonomy, ARRAY_A );
  1224. if ( empty( $this->row ) )
  1225. $this->row = false;
  1226. $current_row_id = $this->row['term_id'];
  1227. $get_table_data = true;
  1228. }
  1229. elseif ( 'user' == $this->pod_data[ 'type' ] ) {
  1230. if ( 'id' == $mode )
  1231. $this->row = get_userdata( $id );
  1232. else
  1233. $this->row = get_user_by( 'slug', $id );
  1234. if ( empty( $this->row ) )
  1235. $this->row = false;
  1236. else
  1237. $this->row = get_object_vars( $this->row );
  1238. $current_row_id = $this->row['ID'];
  1239. $get_table_data = true;
  1240. }
  1241. elseif ( 'comment' == $this->pod_data[ 'type' ] ) {
  1242. $this->row = get_comment( $id, ARRAY_A );
  1243. // No slug handling here
  1244. if ( empty( $this->row ) )
  1245. $this->row = false;
  1246. $current_row_id = $this->row['comment_ID'];
  1247. $get_table_data = true;
  1248. }
  1249. else {
  1250. $params = array(
  1251. 'table' => $this->table,
  1252. 'where' => "`t`.`{$this->field_id}` = " . (int) $id,
  1253. 'orderby' => "`t`.`{$this->field_id}` DESC",
  1254. 'page' => 1,
  1255. 'limit' => 1,
  1256. 'search' => false
  1257. );
  1258. if ( 'slug' == $mode && !empty( $this->field_slug ) ) {
  1259. $id = esc_sql( $id );
  1260. $params[ 'where' ] = "`t`.`{$this->field_slug}` = '{$id}'";
  1261. }
  1262. $this->row = $this->select( $params );
  1263. if ( empty( $this->row ) )
  1264. $this->row = false;
  1265. else
  1266. $this->row = get_object_vars( (object) @current( (array) $this->row ) );
  1267. }
  1268. if ( 'table' == $this->pod_data[ 'storage' ] && false !== $get_table_data && is_numeric($current_row_id)) {
  1269. $params = array(
  1270. 'table' => $wpdb->prefix . "pods_",
  1271. 'where' => "`t`.`id` = {$current_row_id}",
  1272. 'orderby' => "`t`.`id` DESC",
  1273. 'page' => 1,
  1274. 'limit' => 1,
  1275. 'search' => false,
  1276. 'strict' => true
  1277. );
  1278. if ( empty( $this->pod_data[ 'object' ] ) )
  1279. $params[ 'table' ] .= $this->pod_data[ 'name' ];
  1280. else
  1281. $params[ 'table' ] .= $this->pod_data[ 'object' ];
  1282. $row = $this->select( $params );
  1283. if ( !empty( $row ) ) {
  1284. $row = get_object_vars( (object) @current( (array) $row ) );
  1285. if ( is_array( $this->row ) && !empty( $this->row ) )
  1286. $this->row = array_merge( $row, $this->row );
  1287. else
  1288. $this->row = $row;
  1289. }
  1290. }
  1291. if ( !empty( $this->pod ) )
  1292. pods_cache_set( $id, $this->row, 0, 'pods_items_' . $this->pod );
  1293. }
  1294. $this->row = $this->do_hook( 'fetch', $this->row, $id, $this->row_number );
  1295. return $this->row;
  1296. }
  1297. /**
  1298. * Reset the current data
  1299. *
  1300. * @param int $row Row number to reset to
  1301. *
  1302. * @return mixed
  1303. *
  1304. * @since 2.0.0
  1305. */
  1306. public function reset ( $row = null ) {
  1307. $row = pods_absint( $row );
  1308. $this->row = false;
  1309. if ( isset( $this->data[ $row ] ) )
  1310. $this->row = get_object_vars( $this->data[ $row ] );
  1311. if ( empty( $row ) )
  1312. $this->row_number = -1;
  1313. else
  1314. $this->row_number = $row - 1;
  1315. return $this->row;
  1316. }
  1317. /**
  1318. * @static
  1319. *
  1320. * Do a query on the database
  1321. *
  1322. * @param string|array $sql The SQL to execute
  1323. * @param string $error Error to throw on problems
  1324. * @param null $results_error (optional)
  1325. * @param null $no_results_error (optional)
  1326. *
  1327. * @return array|bool|mixed|null|void Result of the query
  1328. *
  1329. * @since 2.0.0
  1330. */
  1331. public static function query ( $sql, $error = 'Database Error', $results_error = null, $no_results_error = null ) {
  1332. /**
  1333. * @var $wpdb wpdb
  1334. */
  1335. global $wpdb;
  1336. if ( $wpdb->show_errors )
  1337. self::$display_errors = true;
  1338. $display_errors = self::$display_errors;
  1339. if ( is_object( $error ) ) {
  1340. if ( isset( $error->display_errors ) && false === $error->display_errors )
  1341. $display_errors = false;
  1342. $error = 'Database Error';
  1343. }
  1344. elseif ( is_bool( $error ) ) {
  1345. $display_errors = $error;
  1346. if ( false !== $error )
  1347. $error = 'Database Error';
  1348. }
  1349. $params = (object) array(
  1350. 'sql' => $sql,
  1351. 'error' => $error,
  1352. 'results_error' => $results_error,
  1353. 'no_results_error' => $no_results_error,
  1354. 'display_errors' => $display_errors
  1355. );
  1356. // Handle Preparations of Values (sprintf format)
  1357. if ( is_array( $sql ) ) {
  1358. if ( isset( $sql[ 0 ] ) && 1 < count( $sql ) ) {
  1359. if ( 2 == count( $sql ) ) {
  1360. if ( !is_array( $sql[ 1 ] ) )
  1361. $sql[ 1 ] = array( $sql[ 1 ] );
  1362. $params->sql = self::prepare( $sql[ 0 ], $sql[ 1 ] );
  1363. }
  1364. elseif ( 3 == count( $sql ) )
  1365. $params->sql = self::prepare( $sql[ 0 ], array( $sql[ 1 ], $sql[ 2 ] ) );
  1366. else
  1367. $params->sql = self::prepare( $sql[ 0 ], array( $sql[ 1 ], $sql[ 2 ], $sql[ 3 ] ) );
  1368. }
  1369. else
  1370. $params = array_merge( $params, $sql );
  1371. if ( 1 == pods_var( 'pods_debug_sql_all', 'get', 0 ) && is_user_logged_in() && ( is_super_admin() || current_user_can( 'delete_users' ) || current_user_can( 'pods' ) ) )
  1372. echo '<textarea cols="100" rows="24">' . $params->sql . '</textarea>';
  1373. }
  1374. $params->sql = trim( $params->sql );
  1375. // Run Query
  1376. $params->sql = self::do_hook( 'query', $params->sql, $params );
  1377. $result = $wpdb->query( $params->sql );
  1378. $result = self::do_hook( 'query_result', $result, $params );
  1379. if ( false === $result && !empty( $params->error ) && !empty( $wpdb->last_error ) )
  1380. return pods_error( "{$params->error}; SQL: {$params->sql}; Response: {$wpdb->last_error}", $params->display_errors );
  1381. if ( 'INSERT' == strtoupper( substr( $params->sql, 0, 6 ) ) || 'REPLACE' == strtoupper( substr( $params->sql, 0, 7 ) ) )
  1382. $result = $wpdb->insert_id;
  1383. elseif ( 'SELECT' == strtoupper( substr( $params->sql, 0, 6 ) ) ) {
  1384. $result = (array) $wpdb->last_result;
  1385. if ( !empty( $result ) && !empty( $params->results_error ) )
  1386. return pods_error( $params->results_error, $params->display_errors );
  1387. elseif ( empty( $result ) && !empty( $params->no_results_error ) )
  1388. return pods_error( $params->no_results_error, $params->display_errors );
  1389. }
  1390. return $result;
  1391. }
  1392. /**
  1393. * Gets all tables in the WP database, optionally exclude WP core
  1394. * tables, and/or Pods table by settings the parameters to false.
  1395. *
  1396. * @param boolean $wp_core
  1397. * @param boolean $pods_tables restrict Pods 2.x tables
  1398. *
  1399. * @return array
  1400. *
  1401. * @since 2.0.0
  1402. */
  1403. public static function get_tables ( $wp_core = true, $pods_tables = true ) {
  1404. global $wpdb;
  1405. $core_wp_tables = array(
  1406. $wpdb->options,
  1407. $wpdb->comments,
  1408. $wpdb->commentmeta,
  1409. $wpdb->posts,
  1410. $wpdb->postmeta,
  1411. $wpdb->users,
  1412. $wpdb->usermeta,
  1413. $wpdb->links,
  1414. $wpdb->terms,
  1415. $wpdb->term_taxonomy,
  1416. $wpdb->term_relationships
  1417. );
  1418. $showTables = mysql_list_tables( DB_NAME );
  1419. $finalTables = array();
  1420. while ( $table = mysql_fetch_row( $showTables ) ) {
  1421. if ( !$pods_tables && 0 === ( strpos( $table[ 0 ], $wpdb->prefix . rtrim( self::$prefix, '_' ) ) ) ) // don't include pods tables
  1422. continue;
  1423. elseif ( !$wp_core && in_array( $table[ 0 ], $core_wp_tables ) )
  1424. continue;
  1425. else
  1426. $finalTables[] = $table[ 0 ];
  1427. }
  1428. return $finalTables;
  1429. }
  1430. /**
  1431. * Gets column information from a table
  1432. *
  1433. * @param string $table Table Name
  1434. *
  1435. * @return array
  1436. *
  1437. * @since 2.0.0
  1438. */
  1439. public static function get_table_columns ( $table ) {
  1440. global $wpdb;
  1441. self::query( "SHOW COLUMNS FROM `{$table}` " );
  1442. $table_columns = $wpdb->last_result;
  1443. $table_cols_and_types = array();
  1444. foreach ( $table_columns as $table_col ) {
  1445. // Get only the type, not the attributes
  1446. if ( false === strpos( $table_col->Type, '(' ) )
  1447. $modified_type = $table_col->Type;
  1448. else
  1449. $modified_type = substr( $table_col->Type, 0, ( strpos( $table_col->Type, '(' ) ) );
  1450. $table_cols_and_types[ $table_col->Field ] = $modified_type;
  1451. }
  1452. return $table_cols_and_types;
  1453. }
  1454. /**
  1455. * Gets column data information from a table
  1456. *
  1457. * @param string $column_name Column name
  1458. * @param string $table Table name
  1459. *
  1460. * @return array
  1461. *
  1462. * @since 2.0.0
  1463. */
  1464. public static function get_column_data ( $column_name, $table ) {
  1465. $describe_data = mysql_query( 'DESCRIBE ' . $table );
  1466. $column_data = array();
  1467. while ( $column_row = mysql_fetch_assoc( $describe_data ) ) {
  1468. $column_data[] = $column_row;
  1469. }
  1470. foreach ( $column_data as $single_column ) {
  1471. if ( $column_name == $single_column[ 'Field' ] )
  1472. return $single_column;
  1473. }
  1474. return $column_data;
  1475. }
  1476. /**
  1477. * Prepare values for the DB
  1478. *
  1479. * @param string $sql SQL to prepare
  1480. * @param array $data Data to add to the sql prepare statement
  1481. *
  1482. * @return bool|null|string
  1483. *
  1484. * @since 2.0.0
  1485. */
  1486. public static function prepare ( $sql, $data ) {
  1487. /**
  1488. * @var $wpdb wpdb
  1489. */
  1490. global $wpdb;
  1491. list( $sql, $data ) = self::do_hook( 'prepare', array( $sql, $data ) );
  1492. return $wpdb->prepare( $sql, $data );
  1493. }
  1494. /**
  1495. * Get the string to use in a query for WHERE/HAVING, uses WP_Query meta_query arguments
  1496. *
  1497. * @param array $fields Array of field matches for querying
  1498. * @param array $pod Related Pod
  1499. *
  1500. * @return string|null Query string for WHERE/HAVING
  1501. *
  1502. * @static
  1503. * @since 2.3.0
  1504. */
  1505. public static function query_fields ( $fields, $pod = null ) {
  1506. $query_fields = array();
  1507. $relation = 'AND';
  1508. if ( isset( $fields[ 'relation' ] ) ) {
  1509. $relation = strtoupper( trim( pods_var( 'relation', $fields, 'AND', null, true ) ) );
  1510. if ( 'AND' != $relation )
  1511. $relation = 'OR';
  1512. unset( $fields[ 'relation' ] );
  1513. }
  1514. foreach ( $fields as $field => $match ) {
  1515. $query_field = self::query_field( $field, $match, $pod );
  1516. if ( !empty( $query_field ) )
  1517. $query_fields[] = $query_field;
  1518. }
  1519. if ( !empty( $query_fields ) )
  1520. $query_fields = '( ' . implode( ' ' . $relation . ' ', $query_fields ) . ' )';
  1521. else
  1522. $query_fields = null;
  1523. return $query_fields;
  1524. }
  1525. /**
  1526. * Get the string to use in a query for matching, uses WP_Query meta_query arguments
  1527. *
  1528. * @param string|int $field Field name or array index
  1529. * @param array|string $q Query array (meta_query) or string for matching
  1530. * @param array $pod Related Pod
  1531. *
  1532. * @return string|null Query field string
  1533. *
  1534. * @see PodsData::query_fields
  1535. * @static
  1536. * @since 2.3.0
  1537. */
  1538. public static function query_field ( $field, $q, $pod = null ) {
  1539. global $wpdb;
  1540. $simple_tableless_objects = PodsForm::field_method( 'pick', 'simple_objects' );
  1541. $field_query = null;
  1542. // Plain queries
  1543. if ( is_numeric( $field ) && !is_array( $q ) )
  1544. return $q;
  1545. // key => value queries
  1546. if ( !is_numeric( $field ) && ( !is_array( $q ) || !isset( $q[ 'key' ] ) ) ) {
  1547. $new_q = array(
  1548. 'key' => $field,
  1549. 'value' => '',
  1550. 'compare' => '='
  1551. );
  1552. if ( is_array( $q ) ) {
  1553. $new_q[ 'compare' ] = 'IN';
  1554. if ( is_array( $q ) && isset( $q[ 'compare' ] ) ) {
  1555. $new_q[ 'compare' ] = $q[ 'compare' ];
  1556. unset( $q[ 'compare' ] );
  1557. }
  1558. }
  1559. $new_q[ 'value' ] = $q;
  1560. $q = $new_q;
  1561. }
  1562. $field = trim( pods_var_raw( 'key', $q, $field, null, true ) );
  1563. $field_type = strtoupper( trim( pods_var_raw( 'type', $q, 'CHAR', null, true ) ) );
  1564. $field_value = pods_var_raw( 'value', $q );
  1565. $field_compare = strtoupper( trim( pods_var_raw( 'compare', $q, ( is_array( $field_value ? 'IN' : '=' ) ), null, true ) ) );
  1566. $field_string = '%s';
  1567. // Deprecated WP type
  1568. if ( 'NUMERIC' == $field_type )
  1569. $field_type = 'SIGNED';
  1570. // Restrict to supported types
  1571. elseif ( !in_array( $field_type, array( 'BINARY', 'CHAR', 'DATE', 'DATETIME', 'DECIMAL', 'SIGNED', 'TIME', 'UNSIGNED' ) ) )
  1572. $field_type = 'CHAR';
  1573. // Handle field naming if Pod-based
  1574. if ( !empty( $pod ) && false === strpos( $field, '`' ) && false === strpos( $field, '.' ) && false === strpos( $field, '(' ) && false === strpos( $field, ' ' ) ) {
  1575. $key = '';
  1576. $tableless_field_types = apply_filters( 'pods_tableless_field_types', array( 'pick', 'file', 'avatar', 'taxonomy' ) );
  1577. if ( isset( $pod[ 'fields' ][ $field ] ) && in_array( $pod[ 'fields' ][ $field ][ 'type' ], $tableless_field_types ) ) {
  1578. if ( in_array( $pod[ 'fields' ][ $field ][ 'pick_object' ], $simple_tableless_objects ) ) {
  1579. if ( 'table' == $pod[ 'storage' ] )
  1580. $key = "`t`.`{$field}`";
  1581. else
  1582. $key = "`{$field}`.`meta_value`";
  1583. }
  1584. else {
  1585. $table = pods_api()->get_table_info( $pod[ 'fields' ][ $field ][ 'pick_object' ], $pod[ 'fields' ][ $field ][ 'pick_val' ] );
  1586. if ( !empty( $table ) )
  1587. $key = "`{$field}`.`" . $table[ 'field_index' ] . '`';
  1588. }
  1589. }
  1590. if ( empty( $key ) ) {
  1591. if ( !in_array( $pod[ 'type' ], array( 'pod', 'table' ) ) ) {
  1592. if ( isset( $pod[ 'object_fields' ][ $field ] ) )
  1593. $key = "`t`.`{$field}`";
  1594. elseif ( isset( $pod[ 'fields' ][ $field ] ) ) {
  1595. if ( 'table' == $pod[ 'storage' ] )
  1596. $key = "`d`.`{$field}`";
  1597. else
  1598. $key = "`{$field}`.`meta_value`";
  1599. }
  1600. else {
  1601. foreach ( $pod[ 'object_fields' ] as $object_field => $object_field_opt ) {
  1602. if ( $object_field == $field || in_array( $field, $object_field_opt[ 'alias' ] ) ) {
  1603. $key = "`t`.`{$object_field}`";
  1604. break;
  1605. }
  1606. }
  1607. }
  1608. }
  1609. elseif ( isset( $pod[ 'fields' ][ $field ] ) ) {
  1610. if ( 'table' == $pod[ 'storage' ] )
  1611. $key = "`t`.`{$field}`";
  1612. else
  1613. $key = "`{$field}`.`meta_value`";
  1614. }
  1615. if ( empty( $key ) )
  1616. $key = "`{$field}`";
  1617. }
  1618. $field = $key;
  1619. }
  1620. elseif ( false === strpos( $field, '`' ) && false === strpos( $field, '(' ) && false === strpos( $field, ' ' ) )
  1621. $field = '`' . str_replace( '.', '`.`', $field ) . '`';
  1622. // Cast field if needed
  1623. if ( 'CHAR' != $field_type )
  1624. $field = 'CAST( ' . $field . ' AS ' . $field_type .' )';
  1625. // Sanitize as integer if needed
  1626. if ( in_array( $field_type, array( 'UNSIGNED', 'SIGNED' ) ) )
  1627. $field_string = '%d';
  1628. // Restrict to supported comparisons
  1629. if ( !in_array( $field_compare, array( '=', '!=', '>', '>=', '<', '<=', 'LIKE', 'NOT LIKE', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN', 'NOT EXISTS' ) ) )
  1630. $field_compare = '=';
  1631. // Restrict to supported array comparisons
  1632. if ( is_array( $field_value ) && !in_array( $field_compare, array( 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN' ) ) ) {
  1633. if ( in_array( $field_compare, array( '!=', 'NOT LIKE' ) ) )
  1634. $field_compare = 'NOT IN';
  1635. else
  1636. $field_compare = 'IN';
  1637. }
  1638. // Restrict to supported string comparisons
  1639. elseif ( !is_array( $field_value ) && in_array( $field_compare, array( 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN' ) ) ) {
  1640. $check_value = preg_split( '/[,\s]+/', $field_value );
  1641. if ( 1 < count( $check_value ) )
  1642. $field_value = $check_value;
  1643. elseif ( in_array( $field_compare, array( 'NOT IN', 'NOT BETWEEN' ) ) )
  1644. $field_compare = '!=';
  1645. else
  1646. $field_compare = '=';
  1647. }
  1648. // Restrict to two values, force = and != if only one value provided
  1649. if ( in_array( $field_compare, array( 'BETWEEN', 'NOT BETWEEN' ) ) ) {
  1650. $field_value = array_slice( $field_value, 0, 2 );
  1651. if ( 1 == count( $field_value ) ) {
  1652. if ( 'NOT IN' == $field_compare )
  1653. $field_compare = '!=';
  1654. else
  1655. $field_compare = '=';
  1656. }
  1657. }
  1658. // Make the query
  1659. if ( in_array( $field_compare, array( '=', '!=', '>', '>=', '<', '<=' ) ) )
  1660. $field_query = $wpdb->prepare( $field . ' ' . $field_compare . ' ' . $field_string, $field_value );
  1661. elseif ( in_array( $field_compare, array( 'LIKE', 'NOT LIKE' ) ) )
  1662. $field_query = $wpdb->prepare( $field . ' ' . $field_compare . ' ' . $field_string, $field_value );
  1663. elseif ( in_array( $field_compare, array( 'IN', 'NOT IN' ) ) )
  1664. $field_query = $wpdb->prepare( $field . ' ' . $field_compare . ' ( ' . substr( str_repeat( ', ' . $field_string, count( $field_value ) ), 1 ) . ' )', $field_value );
  1665. elseif ( in_array( $field_compare, array( 'BETWEEN', 'NOT BETWEEN' ) ) )
  1666. $field_query = $wpdb->prepare( $field . ' ' . $field_compare . ' ' . $field_string . ' AND ' . $field_string, $field_value );
  1667. elseif ( 'EXISTS' == $field_compare )
  1668. $field_query = $field . ' IS NOT NULL';
  1669. elseif ( 'NOT EXISTS' == $field_compare )
  1670. $field_query = $field . ' IS NULL';
  1671. $field_query = apply_filters( 'pods_data_field_query', $field_query, $field, $field_compare, $field_string, $field_value, $q );
  1672. return $field_query;
  1673. }
  1674. /**
  1675. * Setup fields for traversal
  1676. *
  1677. * @param array $fields Associative array of fields data
  1678. *
  1679. * @return array Traverse feed
  1680. * @param object $params (optional) Parameters from build()
  1681. *
  1682. * @since 2.0.0
  1683. */
  1684. function traverse_build ( $fields = null, $params = null ) {
  1685. if ( null === $fields )
  1686. $fields = $this->fields;
  1687. $feed = array();
  1688. foreach ( $fields as $field => $data ) {
  1689. if ( !is_array( $data ) )
  1690. $field = $data;
  1691. if ( 0 < strlen( pods_var( 'filter_' . $field ) ) )
  1692. $feed[ 'traverse_' . $field ] = array( $field );
  1693. }
  1694. return $feed;
  1695. }
  1696. /**
  1697. * Recursively join tables based on fields
  1698. *
  1699. * @param array $traverse_recurse Array of traversal options
  1700. *
  1701. * @return array Array of table joins
  1702. *
  1703. * @since 2.0.0
  1704. */
  1705. function traverse_recurse ( $traverse_recurse ) {
  1706. global $wpdb;
  1707. $defaults = array(
  1708. 'pod' => null,
  1709. 'fields' => array(),
  1710. 'joined' => 't',
  1711. 'depth' => 0,
  1712. 'joined_id' => 'id',
  1713. 'joined_index' => 'id',
  1714. 'params' => new stdClass(),
  1715. 'last_table_info' => array()
  1716. );
  1717. $traverse_recurse = array_merge( $defaults, $traverse_recurse );
  1718. $joins = array();
  1719. if ( empty( $traverse_recurse[ 'pod' ] ) ) {
  1720. if ( !empty( $traverse_recurse[ 'params' ] ) && !empty( $traverse_recurse[ 'params' ]->table ) && 0 === strpos( $traverse_recurse[ 'params' ]->table, $wpdb->prefix ) ) {
  1721. if ( $wpdb->posts == $traverse_recurse[ 'params' ]->table )
  1722. $traverse_recurse[ 'pod' ] = 'post_type';
  1723. elseif ( $wpdb->terms == $traverse_recurse[ 'params' ]->table )
  1724. $traverse_recurse[ 'pod' ] = 'taxonomy';
  1725. elseif ( $wpdb->users == $traverse_recurse[ 'params' ]->table )
  1726. $traverse_recurse[ 'pod' ] = 'user';
  1727. elseif ( $wpdb->comments == $traverse_recurse[ 'params' ]->table )
  1728. $traverse_recurse[ 'pod' ] = 'comment';
  1729. else
  1730. return $joins;
  1731. $pod_data = array();
  1732. if ( in_array( $traverse_recurse[ 'pod' ], array( 'user', 'comment' ) ) ) {
  1733. $pod = $this->api->load_pod( array( 'name' => $traverse_recurse[ 'pod' ] ) );
  1734. if ( !empty( $pod ) && $pod[ 'type' ] == $pod )
  1735. $pod_data = $pod;
  1736. }
  1737. if ( empty( $pod_data ) ) {
  1738. $pod_data = array(
  1739. 'id' => 0,
  1740. 'name' => '_table_' . $traverse_recurse[ 'pod' ],
  1741. 'type' => $traverse_recurse[ 'pod' ],
  1742. 'storage' => ( 'taxonomy' == $traverse_recurse[ 'pod' ] ? 'none' : 'meta' ),
  1743. 'fields' => $this->api->get_wp_object_fields( $traverse_recurse[ 'pod' ] )
  1744. );
  1745. }
  1746. $traverse_recurse[ 'pod' ] = $pod_data[ 'name' ];
  1747. }
  1748. else
  1749. return $joins;
  1750. }
  1751. else {
  1752. $pod_data = $this->api->load_pod( array( 'name' => $traverse_recurse[ 'pod' ] ), false );
  1753. if ( empty( $pod_data ) )
  1754. return $joins;
  1755. }
  1756. $tableless_field_types = apply_filters( 'pods_tableless_field_types', array( 'pick', 'file', 'avatar', 'taxonomy' ) );
  1757. $simple_tableless_objects = PodsForm::field_method( 'pick', 'simple_objects' );
  1758. if ( !isset( $this->traversal[ $traverse_recurse[ 'pod' ] ] ) )
  1759. $this->traversal[ $traverse_recurse[ 'pod' ] ] = array();
  1760. if ( empty( $traverse_recurse[ 'fields' ] ) || !isset( $traverse_recurse[ 'fields' ][ $traverse_recurse[ 'depth' ] ] ) || empty( $traverse_recurse[ 'fields' ][ $traverse_recurse[ 'depth' ] ] ) )
  1761. return $joins;
  1762. $field = $traverse_recurse[ 'fields' ][ $traverse_recurse[ 'depth' ] ];
  1763. $ignore_aliases = array(
  1764. 'wpml_languages',
  1765. 'polylang_languages'
  1766. );
  1767. $ignore_aliases = $this->do_hook( 'traverse_recurse_ignore_aliases', $ignore_aliases, $field, $traverse_recurse );
  1768. if ( in_array( $field, $ignore_aliases ) )
  1769. return $joins;
  1770. $meta_data_table = false;
  1771. if ( !isset( $pod_data[ 'fields' ][ $field ] ) && 'd' == $field && isset( $traverse_recurse[ 'fields' ][ $traverse_recurse[ 'depth' ] - 1 ] ) ) {
  1772. $field = $traverse_recurse[ 'fields' ][ $traverse_recurse[ 'depth' ] - 1 ];
  1773. $pod_data[ 'fields' ][ $field ] = array(
  1774. 'id' => 0,
  1775. 'name' => $field,
  1776. 'type' => 'pick',
  1777. 'pick_object' => $traverse_recurse[ 'last_table_info' ][ 'pod' ][ 'type' ],
  1778. 'pick_val' => $traverse_recurse[ 'last_table_info' ][ 'pod' ][ 'name' ]
  1779. );
  1780. $meta_data_table = true;
  1781. }
  1782. // Fallback to meta table if the pod type supports it
  1783. if ( !isset( $pod_data[ 'fields' ][ $field ] ) ) {
  1784. $last = end( $traverse_recurse[ 'fields' ] );
  1785. if ( 'post_type' == $pod_data[ 'type' ] && !isset( $pod_data[ 'object_fields' ] ) )
  1786. $pod_data[ 'object_fields' ] = $this->api->get_wp_object_fields( 'post_type', $pod_data );
  1787. if ( 'post_type' == $pod_data[ 'type' ] && isset( $pod_data[ 'object_fields'][ $field ] ) && in_array( $pod_data[ 'object_fields' ][ $field ][ 'type' ], $tableless_field_types ) )
  1788. $pod_data[ 'fields' ][ $field ] = $pod_data[ 'object_fields' ][ $field ];
  1789. elseif ( in_array( $pod_data[ 'type' ], array( 'post_type', 'media', 'user', 'comment' ) ) && 'meta_value' == $last )
  1790. $pod_data[ 'fields' ][ $field ] = PodsForm::field_setup( array( 'name' => $field ) );
  1791. else {
  1792. if ( 'post_type' == $pod_data[ 'type' ] ) {
  1793. $pod_data[ 'object_fields' ] = $this->api->get_wp_object_fields( 'post_type', $pod_data, true );
  1794. if ( 'post_type' == $pod_data[ 'type' ] && isset( $pod_data[ 'object_fields' ][ $field ] ) && in_array( $pod_data[ 'object_fields' ][ $field ][ 'type' ], $tableless_field_types ) )
  1795. $pod_data[ 'fields' ][ $field ] = $pod_data[ 'object_fields' ][ $field ];
  1796. else
  1797. return $joins;
  1798. }
  1799. else
  1800. return $joins;
  1801. }
  1802. }
  1803. $traverse = $pod_data[ 'fields' ][ $field ];
  1804. if ( 'taxonomy' == $traverse[ 'type' ] )
  1805. $traverse[ 'table_info' ] = $this->api->get_table_info( $traverse[ 'type' ], $traverse[ 'name' ] );
  1806. elseif ( !in_array( $traverse[ 'type' ], $tableless_field_types ) )
  1807. $traverse[ 'table_info' ] = $this->api->get_table_info( $pod_data[ 'type' ], $pod_data[ 'name' ], $pod_data[ 'name' ], $pod_data );
  1808. elseif ( empty( $traverse[ 'table_info' ] ) ) {
  1809. if ( in_array( $traverse[ 'pick_object' ], $simple_tableless_objects ) && !empty( $traverse_recurse[ 'last_table_info' ] ) ) {
  1810. $traverse[ 'table_info' ] = $traverse_recurse[ 'last_table_info' ];
  1811. if ( !empty( $traverse[ 'table_info' ][ 'meta_table' ] ) )
  1812. $meta_data_table = true;
  1813. }
  1814. else
  1815. $traverse[ 'table_info' ] = $this->api->get_table_info( $traverse[ 'pick_object' ], $traverse[ 'pick_val' ] );
  1816. }
  1817. if ( isset( $this->traversal[ $traverse_recurse[ 'pod' ] ][ $traverse[ 'name' ] ] ) )
  1818. $traverse = array_merge( $traverse, (array) $this->traversal[ $traverse_recurse[ 'pod' ] ][ $traverse[ 'name' ] ] );
  1819. $traverse = $this->do_hook( 'traverse', $traverse, compact( 'pod', 'fields', 'joined', 'depth', 'joined_id', 'params' ) );
  1820. if ( empty( $traverse ) )
  1821. return $joins;
  1822. $traverse = pods_sanitize( $traverse );
  1823. $traverse[ 'id' ] = (int) $traverse[ 'id' ];
  1824. if ( empty( $traverse[ 'id' ] ) )
  1825. $traverse[ 'id' ] = $field;
  1826. $table_info = $traverse[ 'table_info' ];
  1827. $this->traversal[ $traverse_recurse[ 'pod' ] ][ $field ] = $traverse;
  1828. $field_joined = $field;
  1829. if ( 0 < $traverse_recurse[ 'depth' ] && 't' != $traverse_recurse[ 'joined' ] ) {
  1830. if ( $meta_data_table && ( 'pick' != $traverse[ 'type' ] || !in_array( pods_var( 'pick_object', $traverse ), $simple_tableless_objects ) ) )
  1831. $field_joined = $traverse_recurse[ 'joined' ] . '_d';
  1832. else
  1833. $field_joined = $traverse_recurse[ 'joined' ] . '_' . $field;
  1834. }
  1835. $rel_alias = 'rel_' . $field_joined;
  1836. /*if ( !empty( $this->search ) && 1 == 0 ) {
  1837. if ( 0 < strlen( pods_var( 'filter_' . $field_joined, 'get' ) ) ) {
  1838. $val = absint( pods_var( 'filter_' . $field_joined, 'get' ) );
  1839. $search = "`{$field_joined}`.`{$table_info[ 'field_id' ]}` = {$val}";
  1840. if ( 'text' == $this->search_mode ) {
  1841. $val = pods_var( 'filter_' . $field_joined, 'get' );
  1842. $search = "`{$field_joined}`.`{$traverse[ 'name' ]}` = '{$val}'";
  1843. }
  1844. elseif ( 'text_like' == $this->search_mode ) {
  1845. $val = pods_sanitize( like_escape( pods_var_raw( 'filter_' . $field_joined ) ) );
  1846. $search = "`{$field_joined}`.`{$traverse[ 'name' ]}` LIKE '%{$val}%'";
  1847. }
  1848. $this->search_where[] = " {$search} ";
  1849. }
  1850. }*/
  1851. $the_join = null;
  1852. $joined_id = $table_info[ 'field_id' ];
  1853. $joined_index = $table_info[ 'field_index' ];
  1854. if ( 'taxonomy' == $traverse[ 'type' ] ) {
  1855. $rel_tt_alias = 'rel_tt_' . $field_joined;
  1856. if ( $meta_data_table ) {
  1857. $the_join = "
  1858. LEFT JOIN `{$table_info[ 'pod_table' ]}` AS `{$field_joined}` ON
  1859. `{$field_joined}`.`{$table_info[ 'pod_field_id' ]}` = `{$traverse_recurse[ 'rel_alias' ]}`.`{$traverse_recurse[ 'joined_id' ]}`
  1860. ";
  1861. }
  1862. else {
  1863. $the_join = "
  1864. LEFT JOIN `{$wpdb->term_relationships}` AS `{$rel_alias}` ON
  1865. `{$rel_alias}`.`object_id` = `{$traverse_recurse[ 'joined' ]}`.`ID`
  1866. LEFT JOIN `{$wpdb->term_taxonomy}` AS `{$rel_tt_alias}` ON
  1867. `{$rel_tt_alias}`.`taxonomy` = '{$traverse[ 'name' ]}'
  1868. AND `{$rel_tt_alias}`.`term_taxonomy_id` = `{$rel_alias}`.`term_taxonomy_id`
  1869. LEFT JOIN `{$table_info[ 'table' ]}` AS `{$field_joined}` ON
  1870. `{$field_joined}`.`{$table_info[ 'field_id' ]}` = `{$rel_tt_alias}`.`{$table_info[ 'field_id' ]}`
  1871. ";
  1872. $joined_id = $table_info[ 'field_id' ];
  1873. $joined_index = $table_info[ 'field_index' ];
  1874. }
  1875. }
  1876. elseif ( in_array( $traverse[ 'type' ], $tableless_field_types ) && ( 'pick' != $traverse[ 'type' ] || !in_array( pods_var( 'pick_object', $traverse ), $simple_tableless_objects ) ) ) {
  1877. if ( defined( 'PODS_TABLELESS' ) && PODS_TABLELESS ) {
  1878. $the_join = "
  1879. LEFT JOIN `{$table_info[ 'meta_table' ]}` AS `{$rel_alias}` ON
  1880. `{$rel_alias}`.`{$table_info[ 'meta_field_index' ]}` = '{$traverse[ 'name' ]}'
  1881. AND `{$rel_alias}`.`{$table_info[ 'meta_field_id' ]}` = `{$traverse_recurse[ 'joined' ]}`.`{$traverse_recurse[ 'joined_id' ]}`
  1882. LEFT JOIN `{$table_info[ 'meta_table' ]}` AS `{$field_joined}` ON
  1883. `{$field_joined}`.`{$table_info[ 'meta_field_index' ]}` = '{$traverse[ 'name' ]}'
  1884. AND `{$field_joined}`.`{$table_info[ 'meta_field_id' ]}` = CONVERT( `{$rel_alias}`.`{$table_info[ 'meta_field_value' ]}`, SIGNED )
  1885. ";
  1886. $joined_id = $table_info[ 'meta_field_id' ];
  1887. $joined_index = $table_info[ 'meta_field_index' ];
  1888. }
  1889. elseif ( $meta_data_table ) {
  1890. $the_join = "
  1891. LEFT JOIN `{$table_info[ 'pod_table' ]}` AS `{$field_joined}` ON
  1892. `{$field_joined}`.`{$table_info[ 'pod_field_id' ]}` = `{$traverse_recurse[ 'rel_alias' ]}`.`{$traverse_recurse[ 'joined_id' ]}`
  1893. ";
  1894. }
  1895. else {
  1896. $the_join = "
  1897. LEFT JOIN `@wp_podsrel` AS `{$rel_alias}` ON
  1898. `{$rel_alias}`.`field_id` = {$traverse[ 'id' ]}
  1899. AND `{$rel_alias}`.`item_id` = `{$traverse_recurse[ 'joined' ]}`.`id`
  1900. LEFT JOIN `{$table_info[ 'table' ]}` AS `{$field_joined}` ON
  1901. `{$field_joined}`.`{$table_info[ 'field_id' ]}` = `{$rel_alias}`.`related_item_id`
  1902. ";
  1903. }
  1904. }
  1905. elseif ( 'meta' == $pod_data[ 'storage' ] ) {
  1906. if ( ( $traverse_recurse[ 'depth' ] + 2 ) == count( $traverse_recurse[ 'fields' ] ) && ( 'pick' != $traverse[ 'type' ] || !in_array( pods_var( 'pick_object', $traverse ), $simple_tableless_objects ) ) ) {
  1907. $the_join = "
  1908. LEFT JOIN `{$table_info[ 'table' ]}` AS `{$field_joined}` ON
  1909. `{$field_joined}`.`{$table_info[ 'field_index' ]}` = '{$traverse[ 'name' ]}'
  1910. AND `{$field_joined}`.`{$table_info[ 'field_id' ]}` = `{$traverse_recurse[ 'joined' ]}`.`{$traverse_recurse[ 'joined_id' ]}`
  1911. ";
  1912. $table_info[ 'recurse' ] = false;
  1913. }
  1914. else {
  1915. $the_join = "
  1916. LEFT JOIN `{$table_info[ 'meta_table' ]}` AS `{$field_joined}` ON
  1917. `{$field_joined}`.`{$table_info[ 'meta_field_index' ]}` = '{$traverse[ 'name' ]}'
  1918. AND `{$field_joined}`.`{$table_info[ 'meta_field_id' ]}` = `{$traverse_recurse[ 'joined' ]}`.`{$traverse_recurse[ 'joined_id' ]}`
  1919. ";
  1920. $joined_id = $table_info[ 'meta_field_id' ];
  1921. $joined_index = $table_info[ 'meta_field_index' ];
  1922. }
  1923. }
  1924. $traverse_recursive = array(
  1925. 'pod' => $table_info[ 'pod' ][ 'name' ],
  1926. 'fields' => $traverse_recurse[ 'fields' ],
  1927. 'joined' => $field_joined,
  1928. 'depth' => ( $traverse_recurse[ 'depth' ] + 1 ),
  1929. 'joined_id' => $joined_id,
  1930. 'joined_index' => $joined_index,
  1931. 'params' => $traverse_recurse[ 'params' ],
  1932. 'rel_alias' => $rel_alias,
  1933. 'last_table_info' => $table_info
  1934. );
  1935. $the_join = $this->do_hook( 'traverse_the_join', $the_join, $traverse_recurse, $traverse_recursive );
  1936. if ( empty( $the_join ) )
  1937. return $joins;
  1938. $joins[ $traverse_recurse[ 'pod' ] . '_' . $traverse_recurse[ 'depth' ] . '_' . $traverse[ 'id' ] ] = $the_join;
  1939. if ( ( $traverse_recurse[ 'depth' ] + 1 ) < count( $traverse_recurse[ 'fields' ] ) && null !== $table_info[ 'pod' ] && false !== $table_info[ 'recurse' ] )
  1940. $joins = array_merge( $joins, $this->traverse_recurse( $traverse_recursive ) );
  1941. return $joins;
  1942. }
  1943. /**
  1944. * Recursively join tables based on fields
  1945. *
  1946. * @param array $fields Fields to recurse
  1947. * @param null $all_fields (optional) If $fields is empty then traverse all fields, argument does not need to be passed
  1948. * @param object $params (optional) Parameters from build()
  1949. *
  1950. * @return array Array of joins
  1951. */
  1952. function traverse ( $fields = null, $all_fields = null, $params = null ) {
  1953. $joins = array();
  1954. if ( null === $fields )
  1955. $fields = $this->traverse_build( $all_fields, $params );
  1956. foreach ( (array) $fields as $field_group ) {
  1957. $traverse_recurse = array(
  1958. 'pod' => $this->pod,
  1959. 'fields' => $fields,
  1960. 'params' => $params,
  1961. 'last_table_info' => $this->pod_data
  1962. );
  1963. if ( is_array( $field_group ) ) {
  1964. $traverse_recurse[ 'fields' ] = $field_group;
  1965. $joins = array_merge( $joins, $this->traverse_recurse( $traverse_recurse ) );
  1966. }
  1967. else {
  1968. $joins = array_merge( $joins, $this->traverse_recurse( $traverse_recurse ) );
  1969. $joins = array_filter( $joins );
  1970. return $joins;
  1971. }
  1972. }
  1973. $joins = array_filter( $joins );
  1974. return $joins;
  1975. }
  1976. /**
  1977. * Handle filters / actions for the class
  1978. *
  1979. * @since 2.0.0
  1980. */
  1981. private static function do_hook () {
  1982. $args = func_get_args();
  1983. if ( empty( $args ) )
  1984. return false;
  1985. $name = array_shift( $args );
  1986. return pods_do_hook( 'data', $name, $args );
  1987. }
  1988. /**
  1989. * Get the complete sql
  1990. *
  1991. * @since 2.0.5
  1992. */
  1993. public function get_sql ( $sql ) {
  1994. global $wpdb;
  1995. if ( empty( $sql ) )
  1996. $sql = $this->sql;
  1997. $sql = str_replace( '@wp_users', $wpdb->users, $sql );
  1998. $sql = str_replace( '@wp_', $wpdb->prefix, $sql );
  1999. $sql = str_replace( '{prefix}', '@wp_', $sql );
  2000. $sql = str_replace( '{/prefix/}', '{prefix}', $sql );
  2001. return $sql;
  2002. }
  2003. }