/tests/Propel/Tests/Runtime/ActiveQuery/CriteriaReplaceNameTest.php

https://github.com/propelorm/Propel2 · PHP · 205 lines · 125 code · 23 blank · 57 comment · 1 complexity · a40d793b2576dc94a8a41294fd791bf5 MD5 · raw file

  1. <?php
  2. /**
  3. * MIT License. This file is part of the Propel package.
  4. * For the full copyright and license information, please view the LICENSE
  5. * file that was distributed with this source code.
  6. */
  7. namespace Propel\Tests\Runtime\ActiveQuery;
  8. use Propel\Runtime\ActiveQuery\ModelCriteria;
  9. use Propel\Tests\Bookstore\AuthorQuery;
  10. use Propel\Tests\Bookstore\BookQuery;
  11. use Propel\Tests\TestCase;
  12. /**
  13. * Tests name replacement in conditions. The tests were scattered across several classes and are somewhat redundant.
  14. */
  15. class CriteriaReplaceNameTest extends TestCase
  16. {
  17. private const PROJECT_ROOT = __DIR__ . '/../../../../..';
  18. /**
  19. * Provides test data
  20. *
  21. * @return string[][]|NULL[][]
  22. */
  23. public static function NamespacedBookReplaceNamesDataProvider()
  24. {
  25. return [
  26. ['Foo\\Bar\\NamespacedBook.Title = ?', 'Title', 'namespaced_book.title = ?'], // basic case
  27. ['Foo\\Bar\\NamespacedBook.Title=?', 'Title', 'namespaced_book.title=?'], // without spaces
  28. ['Foo\\Bar\\NamespacedBook.Id<= ?', 'Id', 'namespaced_book.id<= ?'], // with non-equal comparator
  29. ['Foo\\Bar\\NamespacedBook.AuthorId LIKE ?', 'AuthorId', 'namespaced_book.author_id LIKE ?'], // with SQL keyword separator
  30. ['(Foo\\Bar\\NamespacedBook.AuthorId) LIKE ?', 'AuthorId', '(namespaced_book.author_id) LIKE ?'], // with parenthesis
  31. ['(Foo\\Bar\\NamespacedBook.Id*1.5)=1', 'Id', '(namespaced_book.id*1.5)=1'], // ignore numbers
  32. // dealing with quotes
  33. ["Foo\\Bar\\NamespacedBook.Id + ' ' + Foo\\Bar\\NamespacedBook.AuthorId", null, "namespaced_book.id + ' ' + namespaced_book.author_id"],
  34. ["'Foo\\Bar\\NamespacedBook.Id' + Foo\\Bar\\NamespacedBook.AuthorId", null, "'Foo\\Bar\\NamespacedBook.Id' + namespaced_book.author_id"],
  35. ["Foo\\Bar\\NamespacedBook.Id + 'Foo\\Bar\\NamespacedBook.AuthorId'", null, "namespaced_book.id + 'Foo\\Bar\\NamespacedBook.AuthorId'"],
  36. ];
  37. }
  38. /**
  39. * Provides test data
  40. *
  41. * @return string[][]|NULL[][]
  42. */
  43. public static function BookReplaceNamesDataProvider()
  44. {
  45. return [
  46. ['Propel\Tests\Bookstore\Book.Title = ?', 'Title', 'book.title = ?'], // basic case
  47. ['Propel\Tests\Bookstore\Book.Title=?', 'Title', 'book.title=?'], // without spaces
  48. ['Propel\Tests\Bookstore\Book.Id<= ?', 'Id', 'book.id<= ?'], // with non-equal comparator
  49. ['Propel\Tests\Bookstore\Book.AuthorId LIKE ?', 'AuthorId', 'book.author_id LIKE ?'], // with SQL keyword separator
  50. ['(Propel\Tests\Bookstore\Book.AuthorId) LIKE ?', 'AuthorId', '(book.author_id) LIKE ?'], // with parenthesis
  51. ['(Propel\Tests\Bookstore\Book.Id*1.5)=1', 'Id', '(book.id*1.5)=1'], // ignore numbers
  52. // dealing with quotes
  53. ['Book.Title = ?', 'Title', 'book.title = ?'], // basic case
  54. ['Book.Title=?', 'Title', 'book.title=?'], // without spaces
  55. ['Book.Id<= ?', 'Id', 'book.id<= ?'], // with non-equal comparator
  56. ['Book.AuthorId LIKE ?', 'AuthorId', 'book.author_id LIKE ?'], // with SQL keyword separator
  57. ['(Book.AuthorId) LIKE ?', 'AuthorId', '(book.author_id) LIKE ?'], // with parenthesis
  58. ['(Book.Id*1.5)=1', 'Id', '(book.id*1.5)=1'], // ignore numbers
  59. // dealing with quotes
  60. ["Book.Id + ' ' + Book.AuthorId", null, "book.id + ' ' + book.author_id"],
  61. ["'Book.Id' + Book.AuthorId", null, "'Book.Id' + book.author_id"],
  62. ["Book.Id + 'Book.AuthorId'", null, "book.id + 'Book.AuthorId'"],
  63. ['1=1', null, '1=1'], // with no name
  64. ['', null, ''], // with empty string
  65. ];
  66. }
  67. /**
  68. * Provides test data
  69. *
  70. * @return string[][]
  71. */
  72. public static function BookstoreContestReplaceNamesDataProvider()
  73. {
  74. return [
  75. ['BookstoreContest.PrizeBookId = ?', 'PrizeBookId', 'contest.bookstore_contest.prize_book_id = ?'], // basic case
  76. ['BookstoreContest.PrizeBookId=?', 'PrizeBookId', 'contest.bookstore_contest.prize_book_id=?'], // without spaces
  77. ['BookstoreContest.Id<= ?', 'Id', 'contest.bookstore_contest.id<= ?'], // with non-equal comparator
  78. ['BookstoreContest.BookstoreId LIKE ?', 'BookstoreId', 'contest.bookstore_contest.bookstore_id LIKE ?'], // with SQL keyword separator
  79. ['(BookstoreContest.BookstoreId) LIKE ?', 'BookstoreId', '(contest.bookstore_contest.bookstore_id) LIKE ?'], // with parenthesis
  80. ['(BookstoreContest.Id*1.5)=1', 'Id', '(contest.bookstore_contest.id*1.5)=1'], // ignore numbers
  81. ];
  82. }
  83. /**
  84. * @dataProvider NamespacedBookReplaceNamesDataProvider
  85. *
  86. * @return void
  87. */
  88. public function testReplaceNameFromNamespacedBook(string $origClause, ?string $columnPhpName, string $modifiedClause)
  89. {
  90. include self::PROJECT_ROOT . '/tests/Fixtures/namespaced/build/conf/bookstore_namespaced-conf.php';
  91. $c = new ModelCriteria('bookstore_namespaced', 'Foo\\Bar\\NamespacedBook');
  92. $this->runTestReplaceName($c, $origClause, $columnPhpName, $modifiedClause);
  93. }
  94. /**
  95. * @dataProvider BookReplaceNamesDataProvider
  96. *
  97. * @return void
  98. */
  99. public function testReplaceNameFromBook(string $origClause, ?string $columnPhpName, string $modifiedClause)
  100. {
  101. include self::PROJECT_ROOT . '/tests/Fixtures/bookstore/build/conf/bookstore-conf.php';
  102. $c = new ModelCriteria('bookstore', 'Propel\Tests\Bookstore\Book');
  103. $this->runTestReplaceName($c, $origClause, $columnPhpName, $modifiedClause);
  104. }
  105. /**
  106. * @dataProvider BookstoreContestReplaceNamesDataProvider
  107. *
  108. * @return void
  109. */
  110. public function testReplaceNameFromBookstoreContest(string $origClause, ?string $columnPhpName, string $modifiedClause)
  111. {
  112. include self::PROJECT_ROOT . '/tests/Fixtures/bookstore/build/conf/bookstore-conf.php';
  113. $c = new ModelCriteria('bookstore-schemas', '\Propel\Tests\BookstoreSchemas\BookstoreContest');
  114. $this->runTestReplaceName($c, $origClause, $columnPhpName, $modifiedClause);
  115. }
  116. /**
  117. * @return void
  118. */
  119. protected function runTestReplaceName(ModelCriteria $c, string $origClause, ?string $columnPhpName, string $modifiedClause)
  120. {
  121. $c->replaceNames($origClause);
  122. $replacedColumns = $c->replacedColumns;
  123. if ($columnPhpName) {
  124. $this->assertCount(1, $replacedColumns);
  125. $columnMap = $c->getTableMap()->getColumnByPhpName($columnPhpName);
  126. $this->assertEquals([$columnMap], $replacedColumns);
  127. }
  128. $this->assertEquals($modifiedClause, $origClause);
  129. }
  130. /**
  131. * Provides test data
  132. *
  133. * @return string[][]|string[][][]
  134. */
  135. public static function ReplaceMultipleNamesDataProvider()
  136. {
  137. return [
  138. ['(Propel\Tests\Bookstore\Book.Id+Book.Id)=1', ['Id', 'Id'], '(book.id+book.id)=1'], // match multiple names
  139. ['CONCAT(Propel\Tests\Bookstore\Book.Title,"Book.Id")= ?', ['Title'], 'CONCAT(book.title,"Book.Id")= ?'], // ignore names in strings
  140. ['CONCAT(Propel\Tests\Bookstore\Book.Title," Book.Id ")= ?', ['Title'], 'CONCAT(book.title," Book.Id ")= ?'], // ignore names in strings
  141. ['MATCH (Propel\Tests\Bookstore\Book.Title,Book.isbn) AGAINST (?)', ['Title', 'ISBN'], 'MATCH (book.title,book.isbn) AGAINST (?)'],
  142. ];
  143. }
  144. /**
  145. * @dataProvider ReplaceMultipleNamesDataProvider
  146. *
  147. * @return void
  148. */
  149. public function testReplaceMultipleNames($origClause, $expectedColumns, $modifiedClause)
  150. {
  151. $c = new ModelCriteria('bookstore', 'Propel\Tests\Bookstore\Book');
  152. $c->replaceNames($origClause);
  153. $replacedColumns = $c->replacedColumns;
  154. $this->assertCount(count($expectedColumns), $replacedColumns);
  155. foreach ($replacedColumns as $index => $replacedColumn) {
  156. $expectedColumnName = $expectedColumns[$index];
  157. $expectedColumnMap = $c->getTableMap()->getColumnByPhpName($expectedColumnName);
  158. $this->assertEquals($expectedColumnMap, $replacedColumn);
  159. }
  160. $this->assertEquals($modifiedClause, $origClause);
  161. }
  162. /**
  163. * @return void
  164. */
  165. public function testReplaceNamesFromSubquery()
  166. {
  167. $numberOfBooksQuery = BookQuery::create()
  168. ->addAsColumn('NumberOfBooks', 'COUNT(Book.Id)')
  169. ->select(['NumberOfBooks', 'AuthorId'])
  170. ->groupBy('Book.AuthorId');
  171. $joinCondition = 'Author.Id = numberOfBooks.AuthorId';
  172. $authorQuery = AuthorQuery::create()
  173. ->addSelectQuery($numberOfBooksQuery, 'numberOfBooks', false)
  174. ->where($joinCondition)
  175. ->withColumn('numberOfBooks.NumberOfBooks', 'NumberOfBooks');
  176. $authorQuery->replaceNames($joinCondition); // note that replaceNames() changes the input string
  177. $this->assertEquals('author.id = numberOfBooks.AuthorId', $joinCondition, 'Aliases from subquery should not be replaced');
  178. $authorIdColumnMap = $authorQuery->getTableMap()->getColumnByPhpName('Id');
  179. $replacedColumns = $authorQuery->replacedColumns;
  180. $this->assertEquals([$authorIdColumnMap], $replacedColumns, 'Only own column (AuthorId) should count as replaced column');
  181. }
  182. }