LinkableBehaviorTest.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  1. <?php
  2. App::uses('Model', 'Model');
  3. App::uses('Controller', 'Controller');
  4. class LinkableBehaviorTest extends CakeTestCase {
  5. public $fixtures = array(
  6. 'plugin.tools.user',
  7. 'plugin.tools.profile',
  8. 'plugin.tools.generic',
  9. 'plugin.tools.comment',
  10. 'plugin.tools.blog_post',
  11. 'plugin.tools.blog_posts_tag',
  12. 'plugin.tools.tag',
  13. 'plugin.tools.legacy_product',
  14. 'plugin.tools.legacy_company',
  15. 'plugin.tools.shipment',
  16. 'plugin.tools.order_item',
  17. );
  18. public $User;
  19. public function startTest() {
  20. $this->User = ClassRegistry::init('User');
  21. }
  22. public function endTest() {
  23. unset($this->User);
  24. }
  25. public function testBelongsTo() {
  26. $arrayExpected = array(
  27. 'User' => array('id' => 1, 'username' => 'CakePHP'),
  28. 'Profile' => array ('id' => 1, 'user_id' => 1, 'biography' => 'CakePHP is a rapid development framework for PHP that provides an extensible architecture for developing, maintaining, and deploying applications.')
  29. );
  30. $arrayResult = $this->User->find('first', array(
  31. 'contain' => array(
  32. 'Profile'
  33. )
  34. ));
  35. $this->assertTrue(isset($arrayResult['Profile']), 'belongsTo association via Containable: %s');
  36. $this->assertEquals($arrayExpected, $arrayResult, 'belongsTo association via Containable: %s');
  37. // Same association, but this time with Linkable
  38. $arrayResult = $this->User->find('first', array(
  39. 'fields' => array(
  40. 'id',
  41. 'username'
  42. ),
  43. 'contain' => false,
  44. 'link' => array(
  45. 'Profile' => array(
  46. 'fields' => array(
  47. 'id',
  48. 'user_id',
  49. 'biography'
  50. )
  51. )
  52. )
  53. ));
  54. $this->assertTrue(isset($arrayResult['Profile']), 'belongsTo association via Linkable: %s');
  55. $this->assertTrue(!empty($arrayResult['Profile']), 'belongsTo association via Linkable: %s');
  56. $this->assertEquals($arrayExpected, $arrayResult, 'belongsTo association via Linkable: %s');
  57. // Linkable association, no field lists
  58. $arrayResult = $this->User->find('first', array(
  59. 'contain' => false,
  60. 'link' => array(
  61. 'Profile'
  62. )
  63. ));
  64. $this->assertTrue(isset($arrayResult['Profile']), 'belongsTo association via Linkable (automatic fields): %s');
  65. $this->assertEquals($arrayExpected, $arrayResult, 'belongsTo association via Linkable (automatic fields): %s');
  66. // On-the-fly association via Linkable
  67. $arrayExpected = array(
  68. 'User' => array('id' => 1, 'username' => 'CakePHP'),
  69. 'Generic' => array('id' => 1, 'text' => '')
  70. );
  71. $arrayResult = $this->User->find('first', array(
  72. 'contain' => false,
  73. 'link' => array(
  74. 'Generic' => array(
  75. 'class' => 'Generic',
  76. 'conditions' => array('exactly' => 'User.id = Generic.id'),
  77. 'fields' => array(
  78. 'id',
  79. 'text'
  80. )
  81. )
  82. )
  83. ));
  84. $this->assertTrue(isset($arrayResult['Generic']), 'On-the-fly belongsTo association via Linkable: %s');
  85. $this->assertEquals($arrayExpected, $arrayResult, 'On-the-fly belongsTo association via Linkable: %s');
  86. // On-the-fly association via Linkable, with order on the associations' row and using array conditions instead of plain string
  87. $arrayExpected = array(
  88. 'User' => array('id' => 4, 'username' => 'CodeIgniter'),
  89. 'Generic' => array('id' => 4, 'text' => '')
  90. );
  91. $arrayResult = $this->User->find('first', array(
  92. 'contain' => false,
  93. 'link' => array(
  94. 'Generic' => array(
  95. 'class' => 'Generic',
  96. 'conditions' => array('exactly' => array('User.id = Generic.id')),
  97. 'fields' => array(
  98. 'id',
  99. 'text'
  100. )
  101. )
  102. ),
  103. 'order' => 'Generic.id DESC'
  104. ));
  105. $this->assertEquals($arrayExpected, $arrayResult, 'On-the-fly belongsTo association via Linkable, with order: %s');
  106. }
  107. public function testHasMany() {
  108. // hasMany association via Containable. Should still work when Linkable is loaded
  109. $arrayExpected = array(
  110. 'User' => array('id' => 1, 'username' => 'CakePHP'),
  111. 'Comment' => array(
  112. 0 => array(
  113. 'id' => 1,
  114. 'user_id' => 1,
  115. 'body' => 'Text'
  116. ),
  117. 1 => array(
  118. 'id' => 2,
  119. 'user_id' => 1,
  120. 'body' => 'Text'
  121. ),
  122. )
  123. );
  124. $arrayResult = $this->User->find('first', array(
  125. 'contain' => array(
  126. 'Comment'
  127. ),
  128. 'order' => 'User.id ASC'
  129. ));
  130. $this->assertTrue(isset($arrayResult['Comment']), 'hasMany association via Containable: %s');
  131. $this->assertEquals($arrayExpected, $arrayResult, 'hasMany association via Containable: %s');
  132. // Same association, but this time with Linkable
  133. $arrayExpected = array(
  134. 'User' => array('id' => 1, 'username' => 'CakePHP'),
  135. 'Comment' => array(
  136. 'id' => 1,
  137. 'user_id' => 1,
  138. 'body' => 'Text'
  139. )
  140. );
  141. $arrayResult = $this->User->find('first', array(
  142. 'fields' => array(
  143. 'id',
  144. 'username'
  145. ),
  146. 'contain' => false,
  147. 'link' => array(
  148. 'Comment' => array(
  149. 'fields' => array(
  150. 'id',
  151. 'user_id',
  152. 'body'
  153. )
  154. )
  155. ),
  156. 'order' => 'User.id ASC',
  157. 'group' => 'User.id'
  158. ));
  159. $this->assertEquals($arrayExpected, $arrayResult, 'hasMany association via Linkable: %s');
  160. }
  161. public function testComplexAssociations() {
  162. $this->BlogPost = ClassRegistry::init('BlogPost');
  163. $arrayExpected = array(
  164. 'BlogPost' => array('id' => 1, 'title' => 'Post 1', 'user_id' => 1),
  165. 'Tag' => array('name' => 'General'),
  166. 'Profile' => array('biography' => 'CakePHP is a rapid development framework for PHP that provides an extensible architecture for developing, maintaining, and deploying applications.'),
  167. 'MainTag' => array('name' => 'General'),
  168. 'Generic' => array('id' => 1,'text' => ''),
  169. 'User' => array('id' => 1, 'username' => 'CakePHP')
  170. );
  171. $arrayResult = $this->BlogPost->find('first', array(
  172. 'conditions' => array(
  173. 'MainTag.id' => 1
  174. ),
  175. 'link' => array(
  176. 'User' => array(
  177. 'Profile' => array(
  178. 'fields' => array(
  179. 'biography'
  180. ),
  181. 'Generic' => array(
  182. 'class' => 'Generic',
  183. 'conditions' => array('exactly' => 'User.id = Generic.id'),
  184. )
  185. )
  186. ),
  187. 'Tag' => array(
  188. 'table' => 'tags',
  189. 'fields' => array(
  190. 'name'
  191. )
  192. ),
  193. 'MainTag' => array(
  194. 'class' => 'Tag',
  195. 'conditions' => array('exactly' => 'BlogPostsTag.blog_post_id = BlogPost.id'),
  196. 'fields' => array(
  197. 'MainTag.name'
  198. )
  199. )
  200. )
  201. ));
  202. $this->assertEquals($arrayExpected, $arrayResult, 'Complex find: %s');
  203. // Linkable and Containable combined
  204. $arrayExpected = array(
  205. 'BlogPost' => array('id' => 1, 'title' => 'Post 1', 'user_id' => 1),
  206. 'Tag' => array(
  207. array('id' => 1, 'name' => 'General', 'parent_id' => null, 'BlogPostsTag' => array('id' => 1, 'blog_post_id' => 1, 'tag_id' => 1, 'main' => 0)),
  208. array('id' => 2, 'name' => 'Test I', 'parent_id' => 1, 'BlogPostsTag' => array('id' => 2, 'blog_post_id' => 1, 'tag_id' => 2, 'main' => 1))
  209. ),
  210. 'User' => array('id' => 1, 'username' => 'CakePHP')
  211. );
  212. $arrayResult = $this->BlogPost->find('first', array(
  213. 'contain' => array(
  214. 'Tag'
  215. ),
  216. 'link' => array(
  217. 'User'
  218. )
  219. ));
  220. $this->assertEquals($arrayExpected, $arrayResult, 'Linkable and Containable combined: %s');
  221. }
  222. public function _testPagination()
  223. {
  224. $objController = new Controller(new CakeRequest('/'), new CakeResponse());
  225. $objController->layout = 'ajax';
  226. $objController->uses = array('User');
  227. $objController->constructClasses();
  228. $objController->request->url = '/';
  229. $objController->paginate = array(
  230. 'fields' => array(
  231. 'username'
  232. ),
  233. 'contain' => false,
  234. 'link' => array(
  235. 'Profile' => array(
  236. 'fields' => array(
  237. 'biography'
  238. )
  239. )
  240. ),
  241. 'limit' => 2
  242. );
  243. $arrayResult = $objController->paginate('User');
  244. $this->assertEquals(4, $objController->params['paging']['User']['count'], 'Paging: total records count: %s');
  245. // Pagination with order on a row from table joined with Linkable
  246. $objController->paginate = array(
  247. 'fields' => array(
  248. 'id'
  249. ),
  250. 'contain' => false,
  251. 'link' => array(
  252. 'Profile' => array(
  253. 'fields' => array(
  254. 'user_id'
  255. )
  256. )
  257. ),
  258. 'limit' => 2,
  259. 'order' => 'Profile.user_id DESC'
  260. );
  261. $arrayResult = $objController->paginate('User');
  262. $arrayExpected = array(
  263. 0 => array(
  264. 'User' => array(
  265. 'id' => 4
  266. ),
  267. 'Profile' => array ('user_id' => 4)
  268. ),
  269. 1 => array(
  270. 'User' => array(
  271. 'id' => 3
  272. ),
  273. 'Profile' => array ('user_id' => 3)
  274. )
  275. );
  276. $this->assertEquals($arrayExpected, $arrayResult, 'Paging with order on join table row: %s');
  277. // Pagination without specifying any fields
  278. $objController->paginate = array(
  279. 'contain' => false,
  280. 'link' => array(
  281. 'Profile'
  282. ),
  283. 'limit' => 2,
  284. 'order' => 'Profile.user_id DESC'
  285. );
  286. $arrayResult = $objController->paginate('User');
  287. $this->assertEquals(4, $objController->params['paging']['User']['count'], 'Paging without any field lists: total records count: %s');
  288. }
  289. /**
  290. * Series of tests that assert if Linkable can adapt to assocations that
  291. * have aliases different from their standard model names
  292. */
  293. public function _testNonstandardAssociationNames() {
  294. $this->Tag = ClassRegistry::init('Tag');
  295. $arrayExpected = array(
  296. 'Tag' => array(
  297. 'name' => 'Test I'
  298. ),
  299. 'Parent' => array(
  300. 'name' => 'General'
  301. )
  302. );
  303. $arrayResult = $this->Tag->find('first', array(
  304. 'fields' => array(
  305. 'name'
  306. ),
  307. 'conditions' => array(
  308. 'Tag.id' => 2
  309. ),
  310. 'link' => array(
  311. 'Parent' => array(
  312. 'fields' => array(
  313. 'name'
  314. )
  315. )
  316. )
  317. ));
  318. $this->assertEquals($arrayExpected, $arrayResult, 'Association with non-standard name: %s');
  319. $this->LegacyProduct = ClassRegistry::init('LegacyProduct');
  320. $arrayExpected = array(
  321. 'LegacyProduct' => array(
  322. 'name' => 'Velocipede'
  323. ),
  324. 'Maker' => array(
  325. 'company_name' => 'Vintage Stuff Manufactory'
  326. ),
  327. 'Transporter' => array(
  328. 'company_name' => 'Joe & Co Crate Shipping Company'
  329. )
  330. );
  331. $arrayResult = $this->LegacyProduct->find('first', array(
  332. 'fields' => array(
  333. 'name'
  334. ),
  335. 'conditions' => array(
  336. 'LegacyProduct.product_id' => 1
  337. ),
  338. 'link' => array(
  339. 'Maker' => array(
  340. 'fields' => array(
  341. 'company_name'
  342. )
  343. ),
  344. 'Transporter' => array(
  345. 'fields' => array(
  346. 'company_name'
  347. )
  348. )
  349. )
  350. ));
  351. $this->assertEquals($arrayExpected, $arrayResult, 'belongsTo associations with custom foreignKey: %s');
  352. $arrayExpected = array(
  353. 'ProductsMade' => array(
  354. 'name' => 'Velocipede'
  355. ),
  356. 'Maker' => array(
  357. 'company_name' => 'Vintage Stuff Manufactory'
  358. )
  359. );
  360. $arrayResult = $this->LegacyProduct->Maker->find('first', array(
  361. 'fields' => array(
  362. 'company_name'
  363. ),
  364. 'conditions' => array(
  365. 'Maker.company_id' => 1
  366. ),
  367. 'link' => array(
  368. 'ProductsMade' => array(
  369. 'fields' => array(
  370. 'name'
  371. )
  372. )
  373. )
  374. ));
  375. $this->assertEquals($arrayExpected, $arrayResult, 'hasMany association with custom foreignKey: %s');
  376. }
  377. public function _testAliasedBelongsToWithSameModelAsHasMany() {
  378. $this->OrderItem = ClassRegistry::init('OrderItem');
  379. $arrayExpected = array(
  380. 0 => array(
  381. 'OrderItem' => array(
  382. 'id' => 50,
  383. 'active_shipment_id' => 320
  384. ),
  385. 'ActiveShipment' => array(
  386. 'id' => 320,
  387. 'ship_date' => '2011-01-07',
  388. 'order_item_id' => 50
  389. )
  390. )
  391. );
  392. $arrayResult = $this->OrderItem->find('all', array(
  393. 'recursive' => -1,
  394. 'conditions' => array(
  395. 'ActiveShipment.ship_date' => date('2011-01-07'),
  396. ),
  397. 'link' => array('ActiveShipment'),
  398. ));
  399. $this->assertEquals($arrayExpected, $arrayResult, 'belongsTo association with alias (requested), with hasMany to the same model without alias: %s');
  400. }
  401. }
  402. class TestModel extends CakeTestModel {
  403. public $recursive = 0;
  404. public $actsAs = array(
  405. 'Containable',
  406. 'Tools.Linkable',
  407. );
  408. }
  409. class User extends TestModel {
  410. public $hasOne = array(
  411. 'Profile'
  412. );
  413. public $hasMany = array(
  414. 'Comment',
  415. 'BlogPost'
  416. );
  417. }
  418. class Profile extends TestModel {
  419. public $belongsTo = array(
  420. 'User'
  421. );
  422. }
  423. class BlogPost extends TestModel {
  424. public $belongsTo = array(
  425. 'User'
  426. );
  427. public $hasAndBelongsToMany = array(
  428. 'Tag'
  429. );
  430. }
  431. class BlogPostTag extends TestModel {
  432. }
  433. class Tag extends TestModel {
  434. public $hasAndBelongsToMany = array(
  435. 'BlogPost'
  436. );
  437. public $belongsTo = array(
  438. 'Parent' => array(
  439. 'className' => 'Tag',
  440. 'foreignKey' => 'parent_id'
  441. )
  442. );
  443. }
  444. class LegacyProduct extends TestModel {
  445. public $primaryKey = 'product_id';
  446. public $belongsTo = array(
  447. 'Maker' => array(
  448. 'className' => 'LegacyCompany',
  449. 'foreignKey' => 'the_company_that_builds_it_id'
  450. ),
  451. 'Transporter' => array(
  452. 'className' => 'LegacyCompany',
  453. 'foreignKey' => 'the_company_that_delivers_it_id'
  454. )
  455. );
  456. }
  457. class LegacyCompany extends TestModel {
  458. public $primaryKey = 'company_id';
  459. public $hasMany = array(
  460. 'ProductsMade' => array(
  461. 'className' => 'LegacyProduct',
  462. 'foreignKey' => 'the_company_that_builds_it_id'
  463. )
  464. );
  465. }
  466. class Shipment extends TestModel {
  467. public $belongsTo = array(
  468. 'OrderItem'
  469. );
  470. }
  471. class OrderItem extends TestModel {
  472. public $hasMany = array(
  473. 'Shipment'
  474. );
  475. public $belongsTo = array(
  476. 'ActiveShipment' => array(
  477. 'className' => 'Shipment',
  478. 'foreignKey' => 'active_shipment_id',
  479. ),
  480. );
  481. }