taxQuery.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  1. <?php
  2. /**
  3. * @group taxonomy
  4. */
  5. class Tests_Term_Tax_Query extends WP_UnitTestCase {
  6. protected $q;
  7. public function setUp() {
  8. parent::setUp();
  9. unset( $this->q );
  10. $this->q = new WP_Query();
  11. }
  12. public function test_construct_with_relation_default() {
  13. $tq = new WP_Tax_Query( array() );
  14. $this->assertSame( 'AND', $tq->relation );
  15. }
  16. public function test_construct_with_relation_or_lowercase() {
  17. $tq = new WP_Tax_Query(
  18. array(
  19. 'relation' => 'or',
  20. )
  21. );
  22. $this->assertSame( 'OR', $tq->relation );
  23. }
  24. public function test_construct_with_relation_or_uppercase() {
  25. $tq = new WP_Tax_Query(
  26. array(
  27. 'relation' => 'OR',
  28. )
  29. );
  30. $this->assertSame( 'OR', $tq->relation );
  31. }
  32. public function test_construct_with_relation_other() {
  33. $tq = new WP_Tax_Query(
  34. array(
  35. 'relation' => 'foo',
  36. )
  37. );
  38. $this->assertSame( 'AND', $tq->relation );
  39. }
  40. public function test_construct_fill_missing_query_params() {
  41. $tq = new WP_Tax_Query(
  42. array(
  43. array(),
  44. )
  45. );
  46. $expected = array(
  47. 'taxonomy' => '',
  48. 'terms' => array(),
  49. 'include_children' => true,
  50. 'field' => 'term_id',
  51. 'operator' => 'IN',
  52. );
  53. $this->assertEquals( $expected, $tq->queries[0] );
  54. }
  55. public function test_construct_fill_missing_query_params_merge_with_passed_values() {
  56. $tq = new WP_Tax_Query(
  57. array(
  58. array(
  59. 'taxonomy' => 'foo',
  60. 'include_children' => false,
  61. 'foo' => 'bar',
  62. ),
  63. )
  64. );
  65. $expected = array(
  66. 'taxonomy' => 'foo',
  67. 'terms' => array(),
  68. 'include_children' => false,
  69. 'field' => 'term_id',
  70. 'operator' => 'IN',
  71. 'foo' => 'bar',
  72. );
  73. $this->assertEquals( $expected, $tq->queries[0] );
  74. }
  75. public function test_construct_cast_terms_to_array() {
  76. $tq = new WP_Tax_Query(
  77. array(
  78. array(
  79. 'terms' => 'foo',
  80. ),
  81. )
  82. );
  83. $this->assertSame( array( 'foo' ), $tq->queries[0]['terms'] );
  84. }
  85. /**
  86. * @ticket 30117
  87. */
  88. public function test_construct_empty_strings_array_members_should_be_discarded() {
  89. $q = new WP_Tax_Query(
  90. array(
  91. '',
  92. array(
  93. 'taxonomy' => 'post_tag',
  94. 'terms' => 'foo',
  95. ),
  96. )
  97. );
  98. $this->assertSame( 1, count( $q->queries ) );
  99. }
  100. public function test_transform_query_terms_empty() {
  101. $tq = new WP_Tax_Query(
  102. array(
  103. array(),
  104. )
  105. );
  106. $query = $tq->queries[0];
  107. $tq->transform_query( $tq->queries[0], 'term_id' );
  108. $this->assertSame( $query, $tq->queries[0] );
  109. }
  110. public function test_transform_query_field_same_as_resulting_field() {
  111. $tq = new WP_Tax_Query(
  112. array(
  113. array(
  114. 'field' => 'term_id',
  115. ),
  116. )
  117. );
  118. $query = $tq->queries[0];
  119. $tq->transform_query( $tq->queries[0], 'term_id' );
  120. $this->assertSame( $query, $tq->queries[0] );
  121. }
  122. public function test_transform_query_resulting_field_sanitized() {
  123. $t1 = self::factory()->category->create( array( 'slug' => 'foo' ) );
  124. $t2 = self::factory()->category->create( array( 'slug' => 'bar' ) );
  125. $p = self::factory()->post->create();
  126. wp_set_post_categories( $p, $t1 );
  127. $tq1 = new WP_Tax_Query(
  128. array(
  129. array(
  130. 'terms' => array( 'foo' ),
  131. 'field' => 'slug',
  132. ),
  133. )
  134. );
  135. $tq1->transform_query( $tq1->queries[0], 'term_taxonomy_id' );
  136. $tq2 = new WP_Tax_Query(
  137. array(
  138. array(
  139. 'terms' => array( 'foo' ),
  140. 'field' => 'slug',
  141. ),
  142. )
  143. );
  144. $tq2->transform_query( $tq2->queries[0], 'TERM_ ta%xonomy_id' );
  145. $this->assertSame( $tq1->queries[0], $tq2->queries[0] );
  146. }
  147. public function test_transform_query_field_slug() {
  148. $t1 = self::factory()->category->create( array( 'slug' => 'foo' ) );
  149. $p = self::factory()->post->create();
  150. $tt_ids = wp_set_post_categories( $p, $t1 );
  151. $tq = new WP_Tax_Query(
  152. array(
  153. array(
  154. 'taxonomy' => 'category',
  155. 'terms' => array( 'foo' ),
  156. 'field' => 'slug',
  157. ),
  158. )
  159. );
  160. $tq->transform_query( $tq->queries[0], 'term_taxonomy_id' );
  161. $this->assertEqualSets( $tt_ids, $tq->queries[0]['terms'] );
  162. $this->assertSame( 'term_taxonomy_id', $tq->queries[0]['field'] );
  163. }
  164. public function test_transform_query_field_name() {
  165. $t1 = self::factory()->category->create(
  166. array(
  167. 'slug' => 'foo',
  168. 'name' => 'Foo',
  169. )
  170. );
  171. $p = self::factory()->post->create();
  172. $tt_ids = wp_set_post_categories( $p, $t1 );
  173. $tq = new WP_Tax_Query(
  174. array(
  175. array(
  176. 'taxonomy' => 'category',
  177. 'terms' => array( 'Foo' ),
  178. 'field' => 'name',
  179. ),
  180. )
  181. );
  182. $tq->transform_query( $tq->queries[0], 'term_taxonomy_id' );
  183. $this->assertEqualSets( $tt_ids, $tq->queries[0]['terms'] );
  184. $this->assertSame( 'term_taxonomy_id', $tq->queries[0]['field'] );
  185. }
  186. public function test_transform_query_field_term_taxonomy_id() {
  187. $t1 = self::factory()->category->create(
  188. array(
  189. 'slug' => 'foo',
  190. 'name' => 'Foo',
  191. )
  192. );
  193. $p = self::factory()->post->create();
  194. $tt_ids = wp_set_post_categories( $p, $t1 );
  195. $tq = new WP_Tax_Query(
  196. array(
  197. array(
  198. 'taxonomy' => 'category',
  199. 'terms' => $tt_ids,
  200. 'field' => 'term_taxonomy_id',
  201. ),
  202. )
  203. );
  204. $tq->transform_query( $tq->queries[0], 'term_id' );
  205. $this->assertSame( array( $t1 ), $tq->queries[0]['terms'] );
  206. $this->assertSame( 'term_id', $tq->queries[0]['field'] );
  207. }
  208. public function test_transform_query_field_term_taxonomy_default() {
  209. $t1 = self::factory()->category->create(
  210. array(
  211. 'slug' => 'foo',
  212. 'name' => 'Foo',
  213. )
  214. );
  215. $p = self::factory()->post->create();
  216. $tt_ids = wp_set_post_categories( $p, $t1 );
  217. $tq = new WP_Tax_Query(
  218. array(
  219. array(
  220. 'taxonomy' => 'category',
  221. 'terms' => array( $t1 ),
  222. 'field' => 'foo', // Anything defaults to term_id.
  223. ),
  224. )
  225. );
  226. $tq->transform_query( $tq->queries[0], 'term_taxonomy_id' );
  227. $this->assertEquals( $tt_ids, $tq->queries[0]['terms'] );
  228. $this->assertSame( 'term_taxonomy_id', $tq->queries[0]['field'] );
  229. }
  230. public function test_transform_query_nonexistent_terms() {
  231. $tq = new WP_Tax_Query(
  232. array(
  233. array(
  234. 'terms' => array( 'foo' ),
  235. 'field' => 'slug',
  236. 'operator' => 'AND',
  237. ),
  238. )
  239. );
  240. $tq->transform_query( $tq->queries[0], 'term_taxonomy_id' );
  241. $this->assertWPError( $tq->queries[0] );
  242. }
  243. /**
  244. * @ticket 18105
  245. */
  246. public function test_get_sql_relation_or_operator_in() {
  247. register_taxonomy( 'wptests_tax', 'post' );
  248. $t1 = self::factory()->term->create(
  249. array(
  250. 'taxonomy' => 'wptests_tax',
  251. )
  252. );
  253. $t2 = self::factory()->term->create(
  254. array(
  255. 'taxonomy' => 'wptests_tax',
  256. )
  257. );
  258. $t3 = self::factory()->term->create(
  259. array(
  260. 'taxonomy' => 'wptests_tax',
  261. )
  262. );
  263. $tq = new WP_Tax_Query(
  264. array(
  265. 'relation' => 'OR',
  266. array(
  267. 'taxonomy' => 'wptests_tax',
  268. 'field' => 'term_id',
  269. 'terms' => $t1,
  270. ),
  271. array(
  272. 'taxonomy' => 'wptests_tax',
  273. 'field' => 'term_id',
  274. 'terms' => $t2,
  275. ),
  276. array(
  277. 'taxonomy' => 'wptests_tax',
  278. 'field' => 'term_id',
  279. 'terms' => $t3,
  280. ),
  281. )
  282. );
  283. global $wpdb;
  284. $sql = $tq->get_sql( $wpdb->posts, 'ID' );
  285. // Only one JOIN is required with OR + IN.
  286. $this->assertSame( 1, substr_count( $sql['join'], 'JOIN' ) );
  287. _unregister_taxonomy( 'wptests_tax' );
  288. }
  289. /**
  290. * @ticket 18105
  291. */
  292. public function test_get_sql_relation_and_operator_in() {
  293. register_taxonomy( 'wptests_tax', 'post' );
  294. $t1 = self::factory()->term->create(
  295. array(
  296. 'taxonomy' => 'wptests_tax',
  297. )
  298. );
  299. $t2 = self::factory()->term->create(
  300. array(
  301. 'taxonomy' => 'wptests_tax',
  302. )
  303. );
  304. $t3 = self::factory()->term->create(
  305. array(
  306. 'taxonomy' => 'wptests_tax',
  307. )
  308. );
  309. $tq = new WP_Tax_Query(
  310. array(
  311. 'relation' => 'AND',
  312. array(
  313. 'taxonomy' => 'wptests_tax',
  314. 'field' => 'term_id',
  315. 'terms' => $t1,
  316. ),
  317. array(
  318. 'taxonomy' => 'wptests_tax',
  319. 'field' => 'term_id',
  320. 'terms' => $t2,
  321. ),
  322. array(
  323. 'taxonomy' => 'wptests_tax',
  324. 'field' => 'term_id',
  325. 'terms' => $t3,
  326. ),
  327. )
  328. );
  329. global $wpdb;
  330. $sql = $tq->get_sql( $wpdb->posts, 'ID' );
  331. $this->assertSame( 3, substr_count( $sql['join'], 'JOIN' ) );
  332. _unregister_taxonomy( 'wptests_tax' );
  333. }
  334. /**
  335. * @ticket 18105
  336. */
  337. public function test_get_sql_nested_relation_or_operator_in() {
  338. register_taxonomy( 'wptests_tax', 'post' );
  339. $t1 = self::factory()->term->create(
  340. array(
  341. 'taxonomy' => 'wptests_tax',
  342. )
  343. );
  344. $t2 = self::factory()->term->create(
  345. array(
  346. 'taxonomy' => 'wptests_tax',
  347. )
  348. );
  349. $t3 = self::factory()->term->create(
  350. array(
  351. 'taxonomy' => 'wptests_tax',
  352. )
  353. );
  354. $tq = new WP_Tax_Query(
  355. array(
  356. 'relation' => 'OR',
  357. array(
  358. 'taxonomy' => 'wptests_tax',
  359. 'field' => 'term_id',
  360. 'terms' => $t1,
  361. ),
  362. array(
  363. 'relation' => 'OR',
  364. array(
  365. 'taxonomy' => 'wptests_tax',
  366. 'field' => 'term_id',
  367. 'terms' => $t2,
  368. ),
  369. array(
  370. 'taxonomy' => 'wptests_tax',
  371. 'field' => 'term_id',
  372. 'terms' => $t3,
  373. ),
  374. ),
  375. )
  376. );
  377. global $wpdb;
  378. $sql = $tq->get_sql( $wpdb->posts, 'ID' );
  379. $this->assertSame( 2, substr_count( $sql['join'], 'JOIN' ) );
  380. _unregister_taxonomy( 'wptests_tax' );
  381. }
  382. /**
  383. * @ticket 29738
  384. */
  385. public function test_get_sql_operator_not_in_empty_terms() {
  386. register_taxonomy( 'wptests_tax', 'post' );
  387. $tq = new WP_Tax_Query(
  388. array(
  389. 'relation' => 'OR',
  390. array(
  391. 'taxonomy' => 'wptests_tax',
  392. 'field' => 'term_id',
  393. 'operator' => 'NOT IN',
  394. 'terms' => array(),
  395. ),
  396. )
  397. );
  398. global $wpdb;
  399. $expected = array(
  400. 'join' => '',
  401. 'where' => '',
  402. );
  403. $this->assertSame( $expected, $tq->get_sql( $wpdb->posts, 'ID' ) );
  404. _unregister_taxonomy( 'wptests_tax' );
  405. }
  406. /**
  407. * @ticket 29738
  408. */
  409. public function test_get_sql_operator_and_empty_terms() {
  410. register_taxonomy( 'wptests_tax', 'post' );
  411. $tq = new WP_Tax_Query(
  412. array(
  413. 'relation' => 'OR',
  414. array(
  415. 'taxonomy' => 'wptests_tax',
  416. 'field' => 'term_id',
  417. 'operator' => 'AND',
  418. 'terms' => array(),
  419. ),
  420. )
  421. );
  422. global $wpdb;
  423. $expected = array(
  424. 'join' => '',
  425. 'where' => '',
  426. );
  427. $this->assertSame( $expected, $tq->get_sql( $wpdb->posts, 'ID' ) );
  428. _unregister_taxonomy( 'wptests_tax' );
  429. }
  430. }