LinkableBehaviorTest.php 14 KB

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