LinkableBehaviorTest.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  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. $objController = new Controller(new CakeRequest('/'), new CakeResponse());
  224. $objController->layout = 'ajax';
  225. $objController->uses = array('User');
  226. $objController->constructClasses();
  227. $objController->request->url = '/';
  228. $objController->paginate = array(
  229. 'fields' => array(
  230. 'username'
  231. ),
  232. 'contain' => false,
  233. 'link' => array(
  234. 'Profile' => array(
  235. 'fields' => array(
  236. 'biography'
  237. )
  238. )
  239. ),
  240. 'limit' => 2
  241. );
  242. $arrayResult = $objController->paginate('User');
  243. $this->assertEquals(4, $objController->params['paging']['User']['count'], 'Paging: total records count: %s');
  244. // Pagination with order on a row from table joined with Linkable
  245. $objController->paginate = array(
  246. 'fields' => array(
  247. 'id'
  248. ),
  249. 'contain' => false,
  250. 'link' => array(
  251. 'Profile' => array(
  252. 'fields' => array(
  253. 'user_id'
  254. )
  255. )
  256. ),
  257. 'limit' => 2,
  258. 'order' => 'Profile.user_id DESC'
  259. );
  260. $arrayResult = $objController->paginate('User');
  261. $arrayExpected = array(
  262. 0 => array(
  263. 'User' => array(
  264. 'id' => 4
  265. ),
  266. 'Profile' => array ('user_id' => 4)
  267. ),
  268. 1 => array(
  269. 'User' => array(
  270. 'id' => 3
  271. ),
  272. 'Profile' => array ('user_id' => 3)
  273. )
  274. );
  275. $this->assertEquals($arrayExpected, $arrayResult, 'Paging with order on join table row: %s');
  276. // Pagination without specifying any fields
  277. $objController->paginate = array(
  278. 'contain' => false,
  279. 'link' => array(
  280. 'Profile'
  281. ),
  282. 'limit' => 2,
  283. 'order' => 'Profile.user_id DESC'
  284. );
  285. $arrayResult = $objController->paginate('User');
  286. $this->assertEquals(4, $objController->params['paging']['User']['count'], 'Paging without any field lists: total records count: %s');
  287. }
  288. /**
  289. * Series of tests that assert if Linkable can adapt to assocations that
  290. * have aliases different from their standard model names
  291. */
  292. public function _testNonstandardAssociationNames() {
  293. $this->Tag = ClassRegistry::init('Tag');
  294. $arrayExpected = array(
  295. 'Tag' => array(
  296. 'name' => 'Test I'
  297. ),
  298. 'Parent' => array(
  299. 'name' => 'General'
  300. )
  301. );
  302. $arrayResult = $this->Tag->find('first', array(
  303. 'fields' => array(
  304. 'name'
  305. ),
  306. 'conditions' => array(
  307. 'Tag.id' => 2
  308. ),
  309. 'link' => array(
  310. 'Parent' => array(
  311. 'fields' => array(
  312. 'name'
  313. )
  314. )
  315. )
  316. ));
  317. $this->assertEquals($arrayExpected, $arrayResult, 'Association with non-standard name: %s');
  318. $this->LegacyProduct = ClassRegistry::init('LegacyProduct');
  319. $arrayExpected = array(
  320. 'LegacyProduct' => array(
  321. 'name' => 'Velocipede'
  322. ),
  323. 'Maker' => array(
  324. 'company_name' => 'Vintage Stuff Manufactory'
  325. ),
  326. 'Transporter' => array(
  327. 'company_name' => 'Joe & Co Crate Shipping Company'
  328. )
  329. );
  330. $arrayResult = $this->LegacyProduct->find('first', array(
  331. 'fields' => array(
  332. 'name'
  333. ),
  334. 'conditions' => array(
  335. 'LegacyProduct.product_id' => 1
  336. ),
  337. 'link' => array(
  338. 'Maker' => array(
  339. 'fields' => array(
  340. 'company_name'
  341. )
  342. ),
  343. 'Transporter' => array(
  344. 'fields' => array(
  345. 'company_name'
  346. )
  347. )
  348. )
  349. ));
  350. $this->assertEquals($arrayExpected, $arrayResult, 'belongsTo associations with custom foreignKey: %s');
  351. $arrayExpected = array(
  352. 'ProductsMade' => array(
  353. 'name' => 'Velocipede'
  354. ),
  355. 'Maker' => array(
  356. 'company_name' => 'Vintage Stuff Manufactory'
  357. )
  358. );
  359. $arrayResult = $this->LegacyProduct->Maker->find('first', array(
  360. 'fields' => array(
  361. 'company_name'
  362. ),
  363. 'conditions' => array(
  364. 'Maker.company_id' => 1
  365. ),
  366. 'link' => array(
  367. 'ProductsMade' => array(
  368. 'fields' => array(
  369. 'name'
  370. )
  371. )
  372. )
  373. ));
  374. $this->assertEquals($arrayExpected, $arrayResult, 'hasMany association with custom foreignKey: %s');
  375. }
  376. public function _testAliasedBelongsToWithSameModelAsHasMany() {
  377. $this->OrderItem = ClassRegistry::init('OrderItem');
  378. $arrayExpected = array(
  379. 0 => array(
  380. 'OrderItem' => array(
  381. 'id' => 50,
  382. 'active_shipment_id' => 320
  383. ),
  384. 'ActiveShipment' => array(
  385. 'id' => 320,
  386. 'ship_date' => '2011-01-07',
  387. 'order_item_id' => 50
  388. )
  389. )
  390. );
  391. $arrayResult = $this->OrderItem->find('all', array(
  392. 'recursive' => -1,
  393. 'conditions' => array(
  394. 'ActiveShipment.ship_date' => date('2011-01-07'),
  395. ),
  396. 'link' => array('ActiveShipment'),
  397. ));
  398. $this->assertEquals($arrayExpected, $arrayResult, 'belongsTo association with alias (requested), with hasMany to the same model without alias: %s');
  399. }
  400. }
  401. class TestModel extends CakeTestModel {
  402. public $recursive = 0;
  403. public $actsAs = array(
  404. 'Containable',
  405. 'Tools.Linkable',
  406. );
  407. }
  408. class User extends TestModel {
  409. public $hasOne = array(
  410. 'Profile'
  411. );
  412. public $hasMany = array(
  413. 'Comment',
  414. 'BlogPost'
  415. );
  416. }
  417. class Profile extends TestModel {
  418. public $belongsTo = array(
  419. 'User'
  420. );
  421. }
  422. class BlogPost extends TestModel {
  423. public $belongsTo = array(
  424. 'User'
  425. );
  426. public $hasAndBelongsToMany = array(
  427. 'Tag'
  428. );
  429. }
  430. class BlogPostTag extends TestModel {
  431. }
  432. class Tag extends TestModel {
  433. public $hasAndBelongsToMany = array(
  434. 'BlogPost'
  435. );
  436. public $belongsTo = array(
  437. 'Parent' => array(
  438. 'className' => 'Tag',
  439. 'foreignKey' => 'parent_id'
  440. )
  441. );
  442. }
  443. class LegacyProduct extends TestModel {
  444. public $primaryKey = 'product_id';
  445. public $belongsTo = array(
  446. 'Maker' => array(
  447. 'className' => 'LegacyCompany',
  448. 'foreignKey' => 'the_company_that_builds_it_id'
  449. ),
  450. 'Transporter' => array(
  451. 'className' => 'LegacyCompany',
  452. 'foreignKey' => 'the_company_that_delivers_it_id'
  453. )
  454. );
  455. }
  456. class LegacyCompany extends TestModel {
  457. public $primaryKey = 'company_id';
  458. public $hasMany = array(
  459. 'ProductsMade' => array(
  460. 'className' => 'LegacyProduct',
  461. 'foreignKey' => 'the_company_that_builds_it_id'
  462. )
  463. );
  464. }
  465. class Shipment extends TestModel {
  466. public $belongsTo = array(
  467. 'OrderItem'
  468. );
  469. }
  470. class OrderItem extends TestModel {
  471. public $hasMany = array(
  472. 'Shipment'
  473. );
  474. public $belongsTo = array(
  475. 'ActiveShipment' => array(
  476. 'className' => 'Shipment',
  477. 'foreignKey' => 'active_shipment_id',
  478. ),
  479. );
  480. }