LinkableBehaviorTest.php 13 KB

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