TranslateBehaviorTest.php 35 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  11. * @link http://cakephp.org CakePHP(tm) Project
  12. * @since 3.0.0
  13. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Test\TestCase\ORM\Behavior;
  16. use Cake\Collection\Collection;
  17. use Cake\Event\Event;
  18. use Cake\I18n\I18n;
  19. use Cake\ORM\Behavior\TranslateBehavior;
  20. use Cake\ORM\Behavior\Translate\TranslateTrait;
  21. use Cake\ORM\Entity;
  22. use Cake\ORM\TableRegistry;
  23. use Cake\TestSuite\TestCase;
  24. /**
  25. * Stub entity class
  26. */
  27. class Article extends Entity
  28. {
  29. use TranslateTrait;
  30. }
  31. /**
  32. * Translate behavior test case
  33. */
  34. class TranslateBehaviorTest extends TestCase
  35. {
  36. /**
  37. * fixtures
  38. *
  39. * @var array
  40. */
  41. public $fixtures = [
  42. 'core.articles',
  43. 'core.authors',
  44. 'core.comments',
  45. 'core.translates'
  46. ];
  47. public function tearDown()
  48. {
  49. parent::tearDown();
  50. I18n::locale(I18n::defaultLocale());
  51. TableRegistry::clear();
  52. }
  53. /**
  54. * Returns an array with all the translations found for a set of records
  55. *
  56. * @param array|\Traversable $data
  57. * @return Collection
  58. */
  59. protected function _extractTranslations($data)
  60. {
  61. return (new Collection($data))->map(function ($row) {
  62. $translations = $row->get('_translations');
  63. if (!$translations) {
  64. return [];
  65. }
  66. return array_map(function ($t) {
  67. return $t->toArray();
  68. }, $translations);
  69. });
  70. }
  71. /**
  72. * Tests that custom translation tables are respected
  73. *
  74. * @return void
  75. */
  76. public function testCustomTranslationTable()
  77. {
  78. $table = TableRegistry::get('Articles');
  79. $table->addBehavior('Translate', [
  80. 'translationTable' => '\TestApp\Model\Table\I18nTable',
  81. 'fields' => ['title', 'body']
  82. ]);
  83. $items = $table->associations();
  84. $i18n = $items->getByProperty('_i18n');
  85. $this->assertEquals('\TestApp\Model\Table\I18nTable', $i18n->name());
  86. $this->assertEquals('custom_i18n_table', $i18n->target()->table());
  87. $this->assertEquals('test_custom_i18n_datasource', $i18n->target()->connection()->configName());
  88. }
  89. /**
  90. * Tests that the strategy can be changed for i18n
  91. *
  92. * @return void
  93. */
  94. public function testStrategy()
  95. {
  96. $table = TableRegistry::get('Articles');
  97. $table->addBehavior('Translate', [
  98. 'strategy' => 'select',
  99. 'fields' => ['title', 'body']
  100. ]);
  101. $items = $table->associations();
  102. $i18n = $items->getByProperty('_i18n');
  103. $this->assertEquals('select', $i18n->strategy());
  104. }
  105. /**
  106. * Tests that fields from a translated model are overridden
  107. *
  108. * @return void
  109. */
  110. public function testFindSingleLocale()
  111. {
  112. $table = TableRegistry::get('Articles');
  113. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  114. $table->locale('eng');
  115. $results = $table->find()->combine('title', 'body', 'id')->toArray();
  116. $expected = [
  117. 1 => ['Title #1' => 'Content #1'],
  118. 2 => ['Title #2' => 'Content #2'],
  119. 3 => ['Title #3' => 'Content #3'],
  120. ];
  121. $this->assertSame($expected, $results);
  122. }
  123. /**
  124. * Tests that fields from a translated model use the I18n class locale
  125. * and that it propogates to associated models
  126. *
  127. * @return void
  128. */
  129. public function testFindSingleLocaleAssociatedEnv()
  130. {
  131. I18n::locale('eng');
  132. $table = TableRegistry::get('Articles');
  133. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  134. $table->hasMany('Comments');
  135. $table->Comments->addBehavior('Translate', ['fields' => ['comment']]);
  136. $results = $table->find()
  137. ->select(['id', 'title', 'body'])
  138. ->contain(['Comments' => ['fields' => ['article_id', 'comment']]])
  139. ->hydrate(false)
  140. ->toArray();
  141. $expected = [
  142. [
  143. 'id' => 1,
  144. 'title' => 'Title #1',
  145. 'body' => 'Content #1',
  146. 'comments' => [
  147. ['article_id' => 1, 'comment' => 'Comment #1', '_locale' => 'eng'],
  148. ['article_id' => 1, 'comment' => 'Comment #2', '_locale' => 'eng'],
  149. ['article_id' => 1, 'comment' => 'Comment #3', '_locale' => 'eng'],
  150. ['article_id' => 1, 'comment' => 'Comment #4', '_locale' => 'eng']
  151. ],
  152. '_locale' => 'eng'
  153. ],
  154. [
  155. 'id' => 2,
  156. 'title' => 'Title #2',
  157. 'body' => 'Content #2',
  158. 'comments' => [
  159. ['article_id' => 2, 'comment' => 'First Comment for Second Article', '_locale' => 'eng'],
  160. ['article_id' => 2, 'comment' => 'Second Comment for Second Article', '_locale' => 'eng']
  161. ],
  162. '_locale' => 'eng'
  163. ],
  164. [
  165. 'id' => 3,
  166. 'title' => 'Title #3',
  167. 'body' => 'Content #3',
  168. 'comments' => [],
  169. '_locale' => 'eng'
  170. ]
  171. ];
  172. $this->assertSame($expected, $results);
  173. I18n::locale('spa');
  174. $results = $table->find()
  175. ->select(['id', 'title', 'body'])
  176. ->contain([
  177. 'Comments' => [
  178. 'fields' => ['article_id', 'comment'],
  179. 'sort' => ['Comments.id' => 'ASC']
  180. ]
  181. ])
  182. ->hydrate(false)
  183. ->toArray();
  184. $expected = [
  185. [
  186. 'id' => 1,
  187. 'title' => 'First Article',
  188. 'body' => 'Contenido #1',
  189. 'comments' => [
  190. ['article_id' => 1, 'comment' => 'First Comment for First Article', '_locale' => 'spa'],
  191. ['article_id' => 1, 'comment' => 'Second Comment for First Article', '_locale' => 'spa'],
  192. ['article_id' => 1, 'comment' => 'Third Comment for First Article', '_locale' => 'spa'],
  193. ['article_id' => 1, 'comment' => 'Comentario #4', '_locale' => 'spa']
  194. ],
  195. '_locale' => 'spa'
  196. ],
  197. [
  198. 'id' => 2,
  199. 'title' => 'Second Article',
  200. 'body' => 'Second Article Body',
  201. 'comments' => [
  202. ['article_id' => 2, 'comment' => 'First Comment for Second Article', '_locale' => 'spa'],
  203. ['article_id' => 2, 'comment' => 'Second Comment for Second Article', '_locale' => 'spa']
  204. ],
  205. '_locale' => 'spa'
  206. ],
  207. [
  208. 'id' => 3,
  209. 'title' => 'Third Article',
  210. 'body' => 'Third Article Body',
  211. 'comments' => [],
  212. '_locale' => 'spa'
  213. ]
  214. ];
  215. $this->assertSame($expected, $results);
  216. }
  217. /**
  218. * Tests that fields from a translated model are not overridden if translation
  219. * is null
  220. *
  221. * @return void
  222. */
  223. public function testFindSingleLocaleWithNullTranslation()
  224. {
  225. $table = TableRegistry::get('Comments');
  226. $table->addBehavior('Translate', ['fields' => ['comment']]);
  227. $table->locale('spa');
  228. $results = $table->find()
  229. ->where(['Comments.id' => 6])
  230. ->combine('id', 'comment')->toArray();
  231. $expected = [6 => 'Second Comment for Second Article'];
  232. $this->assertSame($expected, $results);
  233. }
  234. /**
  235. * Tests that overriding fields with the translate behavior works when
  236. * using conditions and that all other columns are preserved
  237. *
  238. * @return void
  239. */
  240. public function testFindSingleLocaleWithConditions()
  241. {
  242. $table = TableRegistry::get('Articles');
  243. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  244. $table->locale('eng');
  245. $results = $table->find()
  246. ->where(['Articles.id' => 2])
  247. ->all();
  248. $this->assertCount(1, $results);
  249. $row = $results->first();
  250. $expected = [
  251. 'id' => 2,
  252. 'title' => 'Title #2',
  253. 'body' => 'Content #2',
  254. 'author_id' => 3,
  255. 'published' => 'Y',
  256. '_locale' => 'eng'
  257. ];
  258. $this->assertEquals($expected, $row->toArray());
  259. }
  260. /**
  261. * Tests that translating fields work when other formatters are used
  262. *
  263. * @return void
  264. */
  265. public function testFindList()
  266. {
  267. $table = TableRegistry::get('Articles');
  268. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  269. $table->locale('eng');
  270. $results = $table->find('list')->toArray();
  271. $expected = [1 => 'Title #1', 2 => 'Title #2', 3 => 'Title #3'];
  272. $this->assertSame($expected, $results);
  273. }
  274. /**
  275. * Tests that the query count return the correct results
  276. *
  277. * @return void
  278. */
  279. public function testFindCount()
  280. {
  281. $table = TableRegistry::get('Articles');
  282. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  283. $table->locale('eng');
  284. $this->assertEquals(3, $table->find()->count());
  285. }
  286. /**
  287. * Tests that it is possible to get all translated fields at once
  288. *
  289. * @return void
  290. */
  291. public function testFindTranslations()
  292. {
  293. $table = TableRegistry::get('Articles');
  294. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  295. $results = $table->find('translations');
  296. $expected = [
  297. [
  298. 'eng' => ['title' => 'Title #1', 'body' => 'Content #1', 'description' => 'Description #1', 'locale' => 'eng'],
  299. 'deu' => ['title' => 'Titel #1', 'body' => 'Inhalt #1', 'locale' => 'deu'],
  300. 'cze' => ['title' => 'Titulek #1', 'body' => 'Obsah #1', 'locale' => 'cze'],
  301. 'spa' => ['body' => 'Contenido #1', 'locale' => 'spa', 'description' => '']
  302. ],
  303. [
  304. 'eng' => ['title' => 'Title #2', 'body' => 'Content #2', 'locale' => 'eng'],
  305. 'deu' => ['title' => 'Titel #2', 'body' => 'Inhalt #2', 'locale' => 'deu'],
  306. 'cze' => ['title' => 'Titulek #2', 'body' => 'Obsah #2', 'locale' => 'cze']
  307. ],
  308. [
  309. 'eng' => ['title' => 'Title #3', 'body' => 'Content #3', 'locale' => 'eng'],
  310. 'deu' => ['title' => 'Titel #3', 'body' => 'Inhalt #3', 'locale' => 'deu'],
  311. 'cze' => ['title' => 'Titulek #3', 'body' => 'Obsah #3', 'locale' => 'cze']
  312. ]
  313. ];
  314. $translations = $this->_extractTranslations($results);
  315. $this->assertEquals($expected, $translations->toArray());
  316. $expected = [
  317. 1 => ['First Article' => 'First Article Body'],
  318. 2 => ['Second Article' => 'Second Article Body'],
  319. 3 => ['Third Article' => 'Third Article Body']
  320. ];
  321. $grouped = $results->combine('title', 'body', 'id');
  322. $this->assertEquals($expected, $grouped->toArray());
  323. }
  324. /**
  325. * Tests that it is possible to request just a few translations
  326. *
  327. * @return void
  328. */
  329. public function testFindFilteredTranslations()
  330. {
  331. $table = TableRegistry::get('Articles');
  332. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  333. $results = $table->find('translations', ['locales' => ['deu', 'cze']]);
  334. $expected = [
  335. [
  336. 'deu' => ['title' => 'Titel #1', 'body' => 'Inhalt #1', 'locale' => 'deu'],
  337. 'cze' => ['title' => 'Titulek #1', 'body' => 'Obsah #1', 'locale' => 'cze']
  338. ],
  339. [
  340. 'deu' => ['title' => 'Titel #2', 'body' => 'Inhalt #2', 'locale' => 'deu'],
  341. 'cze' => ['title' => 'Titulek #2', 'body' => 'Obsah #2', 'locale' => 'cze']
  342. ],
  343. [
  344. 'deu' => ['title' => 'Titel #3', 'body' => 'Inhalt #3', 'locale' => 'deu'],
  345. 'cze' => ['title' => 'Titulek #3', 'body' => 'Obsah #3', 'locale' => 'cze']
  346. ]
  347. ];
  348. $translations = $this->_extractTranslations($results);
  349. $this->assertEquals($expected, $translations->toArray());
  350. $expected = [
  351. 1 => ['First Article' => 'First Article Body'],
  352. 2 => ['Second Article' => 'Second Article Body'],
  353. 3 => ['Third Article' => 'Third Article Body']
  354. ];
  355. $grouped = $results->combine('title', 'body', 'id');
  356. $this->assertEquals($expected, $grouped->toArray());
  357. }
  358. /**
  359. * Tests that it is possible to combine find('list') and find('translations')
  360. *
  361. * @return void
  362. */
  363. public function testFindTranslationsList()
  364. {
  365. $table = TableRegistry::get('Articles');
  366. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  367. $results = $table
  368. ->find('list', [
  369. 'keyField' => 'title',
  370. 'valueField' => '_translations.deu.title',
  371. 'groupField' => 'id'
  372. ])
  373. ->find('translations', ['locales' => ['deu']]);
  374. $expected = [
  375. 1 => ['First Article' => 'Titel #1'],
  376. 2 => ['Second Article' => 'Titel #2'],
  377. 3 => ['Third Article' => 'Titel #3']
  378. ];
  379. $this->assertEquals($expected, $results->toArray());
  380. }
  381. /**
  382. * Tests that you can both override fields and find all translations
  383. *
  384. * @return void
  385. */
  386. public function testFindTranslationsWithFieldOverriding()
  387. {
  388. $table = TableRegistry::get('Articles');
  389. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  390. $table->locale('cze');
  391. $results = $table->find('translations', ['locales' => ['deu', 'cze']]);
  392. $expected = [
  393. [
  394. 'deu' => ['title' => 'Titel #1', 'body' => 'Inhalt #1', 'locale' => 'deu'],
  395. 'cze' => ['title' => 'Titulek #1', 'body' => 'Obsah #1', 'locale' => 'cze']
  396. ],
  397. [
  398. 'deu' => ['title' => 'Titel #2', 'body' => 'Inhalt #2', 'locale' => 'deu'],
  399. 'cze' => ['title' => 'Titulek #2', 'body' => 'Obsah #2', 'locale' => 'cze']
  400. ],
  401. [
  402. 'deu' => ['title' => 'Titel #3', 'body' => 'Inhalt #3', 'locale' => 'deu'],
  403. 'cze' => ['title' => 'Titulek #3', 'body' => 'Obsah #3', 'locale' => 'cze']
  404. ]
  405. ];
  406. $translations = $this->_extractTranslations($results);
  407. $this->assertEquals($expected, $translations->toArray());
  408. $expected = [
  409. 1 => ['Titulek #1' => 'Obsah #1'],
  410. 2 => ['Titulek #2' => 'Obsah #2'],
  411. 3 => ['Titulek #3' => 'Obsah #3']
  412. ];
  413. $grouped = $results->combine('title', 'body', 'id');
  414. $this->assertEquals($expected, $grouped->toArray());
  415. }
  416. /**
  417. * Tests that fields can be overridden in a hasMany association
  418. *
  419. * @return void
  420. */
  421. public function testFindSingleLocaleHasMany()
  422. {
  423. $table = TableRegistry::get('Articles');
  424. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  425. $table->hasMany('Comments');
  426. $comments = $table->hasMany('Comments')->target();
  427. $comments->addBehavior('Translate', ['fields' => ['comment']]);
  428. $table->locale('eng');
  429. $comments->locale('eng');
  430. $results = $table->find()->contain(['Comments' => function ($q) {
  431. return $q->select(['id', 'comment', 'article_id']);
  432. }]);
  433. $list = new Collection($results->first()->comments);
  434. $expected = [
  435. 1 => 'Comment #1',
  436. 2 => 'Comment #2',
  437. 3 => 'Comment #3',
  438. 4 => 'Comment #4'
  439. ];
  440. $this->assertEquals($expected, $list->combine('id', 'comment')->toArray());
  441. }
  442. /**
  443. * Test that it is possible to bring translations from hasMany relations
  444. *
  445. * @return void
  446. */
  447. public function testTranslationsHasMany()
  448. {
  449. $table = TableRegistry::get('Articles');
  450. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  451. $table->hasMany('Comments');
  452. $comments = $table->hasMany('Comments')->target();
  453. $comments->addBehavior('Translate', ['fields' => ['comment']]);
  454. $results = $table->find('translations')->contain([
  455. 'Comments' => function ($q) {
  456. return $q->find('translations')->select(['id', 'comment', 'article_id']);
  457. }
  458. ]);
  459. $comments = $results->first()->comments;
  460. $expected = [
  461. [
  462. 'eng' => ['comment' => 'Comment #1', 'locale' => 'eng']
  463. ],
  464. [
  465. 'eng' => ['comment' => 'Comment #2', 'locale' => 'eng']
  466. ],
  467. [
  468. 'eng' => ['comment' => 'Comment #3', 'locale' => 'eng']
  469. ],
  470. [
  471. 'eng' => ['comment' => 'Comment #4', 'locale' => 'eng'],
  472. 'spa' => ['comment' => 'Comentario #4', 'locale' => 'spa']
  473. ]
  474. ];
  475. $translations = $this->_extractTranslations($comments);
  476. $this->assertEquals($expected, $translations->toArray());
  477. }
  478. /**
  479. * Tests that it is possible to both override fields with a translation and
  480. * also find separately other translations
  481. *
  482. * @return void
  483. */
  484. public function testTranslationsHasManyWithOverride()
  485. {
  486. $table = TableRegistry::get('Articles');
  487. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  488. $table->hasMany('Comments');
  489. $comments = $table->hasMany('Comments')->target();
  490. $comments->addBehavior('Translate', ['fields' => ['comment']]);
  491. $table->locale('cze');
  492. $comments->locale('eng');
  493. $results = $table->find('translations')->contain([
  494. 'Comments' => function ($q) {
  495. return $q->find('translations')->select(['id', 'comment', 'article_id']);
  496. }
  497. ]);
  498. $comments = $results->first()->comments;
  499. $expected = [
  500. 1 => 'Comment #1',
  501. 2 => 'Comment #2',
  502. 3 => 'Comment #3',
  503. 4 => 'Comment #4'
  504. ];
  505. $list = new Collection($comments);
  506. $this->assertEquals($expected, $list->combine('id', 'comment')->toArray());
  507. $expected = [
  508. [
  509. 'eng' => ['comment' => 'Comment #1', 'locale' => 'eng']
  510. ],
  511. [
  512. 'eng' => ['comment' => 'Comment #2', 'locale' => 'eng']
  513. ],
  514. [
  515. 'eng' => ['comment' => 'Comment #3', 'locale' => 'eng']
  516. ],
  517. [
  518. 'eng' => ['comment' => 'Comment #4', 'locale' => 'eng'],
  519. 'spa' => ['comment' => 'Comentario #4', 'locale' => 'spa']
  520. ]
  521. ];
  522. $translations = $this->_extractTranslations($comments);
  523. $this->assertEquals($expected, $translations->toArray());
  524. $this->assertEquals('Titulek #1', $results->first()->title);
  525. $this->assertEquals('Obsah #1', $results->first()->body);
  526. }
  527. /**
  528. * Tests that it is possible to translate belongsTo associations
  529. *
  530. * @return void
  531. */
  532. public function testFindSingleLocaleBelongsto()
  533. {
  534. $table = TableRegistry::get('Articles');
  535. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  536. $authors = $table->belongsTo('Authors')->target();
  537. $authors->addBehavior('Translate', ['fields' => ['name']]);
  538. $table->locale('eng');
  539. $authors->locale('eng');
  540. $results = $table->find()
  541. ->select(['title', 'body'])
  542. ->order(['title' => 'asc'])
  543. ->contain(['Authors' => function ($q) {
  544. return $q->select(['id', 'name']);
  545. }]);
  546. $expected = [
  547. [
  548. 'title' => 'Title #1',
  549. 'body' => 'Content #1',
  550. 'author' => ['id' => 1, 'name' => 'May-rianoh', '_locale' => 'eng'],
  551. '_locale' => 'eng'
  552. ],
  553. [
  554. 'title' => 'Title #2',
  555. 'body' => 'Content #2',
  556. 'author' => ['id' => 3, 'name' => 'larry', '_locale' => 'eng'],
  557. '_locale' => 'eng'
  558. ],
  559. [
  560. 'title' => 'Title #3',
  561. 'body' => 'Content #3',
  562. 'author' => ['id' => 1, 'name' => 'May-rianoh', '_locale' => 'eng'],
  563. '_locale' => 'eng'
  564. ]
  565. ];
  566. $results = array_map(function ($r) {
  567. return $r->toArray();
  568. }, $results->toArray());
  569. $this->assertEquals($expected, $results);
  570. }
  571. /**
  572. * Tests that updating an existing record translations work
  573. *
  574. * @return void
  575. */
  576. public function testUpdateSingleLocale()
  577. {
  578. $table = TableRegistry::get('Articles');
  579. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  580. $table->locale('eng');
  581. $article = $table->find()->first();
  582. $this->assertEquals(1, $article->get('id'));
  583. $article->set('title', 'New translated article');
  584. $table->save($article);
  585. $this->assertNull($article->get('_i18n'));
  586. $article = $table->find()->first();
  587. $this->assertEquals(1, $article->get('id'));
  588. $this->assertEquals('New translated article', $article->get('title'));
  589. $this->assertEquals('Content #1', $article->get('body'));
  590. $table->locale(false);
  591. $article = $table->find()->first();
  592. $this->assertEquals(1, $article->get('id'));
  593. $this->assertEquals('First Article', $article->get('title'));
  594. $table->locale('eng');
  595. $article->set('title', 'Wow, such translated article');
  596. $article->set('body', 'A translated body');
  597. $table->save($article);
  598. $this->assertNull($article->get('_i18n'));
  599. $article = $table->find()->first();
  600. $this->assertEquals(1, $article->get('id'));
  601. $this->assertEquals('Wow, such translated article', $article->get('title'));
  602. $this->assertEquals('A translated body', $article->get('body'));
  603. }
  604. /**
  605. * Tests adding new translation to a record
  606. *
  607. * @return void
  608. */
  609. public function testInsertNewTranslations()
  610. {
  611. $table = TableRegistry::get('Articles');
  612. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  613. $table->locale('fra');
  614. $article = $table->find()->first();
  615. $this->assertEquals(1, $article->get('id'));
  616. $article->set('title', 'Le titre');
  617. $table->save($article);
  618. $this->assertEquals('fra', $article->get('_locale'));
  619. $article = $table->find()->first();
  620. $this->assertEquals(1, $article->get('id'));
  621. $this->assertEquals('Le titre', $article->get('title'));
  622. $this->assertEquals('First Article Body', $article->get('body'));
  623. $article->set('title', 'Un autre titre');
  624. $article->set('body', 'Le contenu');
  625. $table->save($article);
  626. $this->assertNull($article->get('_i18n'));
  627. $article = $table->find()->first();
  628. $this->assertEquals('Un autre titre', $article->get('title'));
  629. $this->assertEquals('Le contenu', $article->get('body'));
  630. }
  631. /**
  632. * Tests that it is possible to use the _locale property to specify the language
  633. * to use for saving an entity
  634. *
  635. * @return void
  636. */
  637. public function testUpdateTranslationWithLocaleInEntity()
  638. {
  639. $table = TableRegistry::get('Articles');
  640. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  641. $article = $table->find()->first();
  642. $this->assertEquals(1, $article->get('id'));
  643. $article->set('_locale', 'fra');
  644. $article->set('title', 'Le titre');
  645. $table->save($article);
  646. $this->assertNull($article->get('_i18n'));
  647. $article = $table->find()->first();
  648. $this->assertEquals(1, $article->get('id'));
  649. $this->assertEquals('First Article', $article->get('title'));
  650. $this->assertEquals('First Article Body', $article->get('body'));
  651. $table->locale('fra');
  652. $article = $table->find()->first();
  653. $this->assertEquals(1, $article->get('id'));
  654. $this->assertEquals('Le titre', $article->get('title'));
  655. $this->assertEquals('First Article Body', $article->get('body'));
  656. }
  657. /**
  658. * Tests that translations are added to the whitelist of associations to be
  659. * saved
  660. *
  661. * @return void
  662. */
  663. public function testSaveTranslationWithAssociationWhitelist()
  664. {
  665. $table = TableRegistry::get('Articles');
  666. $table->hasMany('Comments');
  667. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  668. $table->locale('fra');
  669. $article = $table->find()->first();
  670. $this->assertEquals(1, $article->get('id'));
  671. $article->set('title', 'Le titre');
  672. $table->save($article, ['associated' => ['Comments']]);
  673. $this->assertNull($article->get('_i18n'));
  674. $article = $table->find()->first();
  675. $this->assertEquals('Le titre', $article->get('title'));
  676. }
  677. /**
  678. * Tests that after deleting a translated entity, all translations are also removed
  679. *
  680. * @return void
  681. */
  682. public function testDelete()
  683. {
  684. $table = TableRegistry::get('Articles');
  685. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  686. $article = $table->find()->first();
  687. $this->assertTrue($table->delete($article));
  688. $translations = TableRegistry::get('I18n')->find()
  689. ->where(['model' => 'Articles', 'foreign_key' => $article->id])
  690. ->count();
  691. $this->assertEquals(0, $translations);
  692. }
  693. /**
  694. * Tests saving multiple translations at once when the translations already
  695. * exist in the database
  696. *
  697. * @return void
  698. */
  699. public function testSaveMultipleTranslations()
  700. {
  701. $table = TableRegistry::get('Articles');
  702. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  703. $article = $results = $table->find('translations')->first();
  704. $translations = $article->get('_translations');
  705. $translations['deu']->set('title', 'Another title');
  706. $translations['eng']->set('body', 'Another body');
  707. $article->set('_translations', $translations);
  708. $table->save($article);
  709. $this->assertNull($article->get('_i18n'));
  710. $article = $results = $table->find('translations')->first();
  711. $translations = $article->get('_translations');
  712. $this->assertEquals('Another title', $translations['deu']->get('title'));
  713. $this->assertEquals('Another body', $translations['eng']->get('body'));
  714. }
  715. /**
  716. * Tests saving multiple existing translations and adding new ones
  717. *
  718. * @return void
  719. */
  720. public function testSaveMultipleNewTranslations()
  721. {
  722. $table = TableRegistry::get('Articles');
  723. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  724. $article = $results = $table->find('translations')->first();
  725. $translations = $article->get('_translations');
  726. $translations['deu']->set('title', 'Another title');
  727. $translations['eng']->set('body', 'Another body');
  728. $translations['spa'] = new Entity(['title' => 'Titulo']);
  729. $translations['fre'] = new Entity(['title' => 'Titre']);
  730. $article->set('_translations', $translations);
  731. $table->save($article);
  732. $this->assertNull($article->get('_i18n'));
  733. $article = $results = $table->find('translations')->first();
  734. $translations = $article->get('_translations');
  735. $this->assertEquals('Another title', $translations['deu']->get('title'));
  736. $this->assertEquals('Another body', $translations['eng']->get('body'));
  737. $this->assertEquals('Titulo', $translations['spa']->get('title'));
  738. $this->assertEquals('Titre', $translations['fre']->get('title'));
  739. }
  740. /**
  741. * Tests that iterating a resultset twice when using the translations finder
  742. * will not cause any errors nor information loss
  743. *
  744. * @return void
  745. */
  746. public function testUseCountInFindTranslations()
  747. {
  748. $table = TableRegistry::get('Articles');
  749. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  750. $articles = $results = $table->find('translations');
  751. $all = $articles->all();
  752. $this->assertCount(3, $all);
  753. $article = $all->first();
  754. $this->assertNotEmpty($article->get('_translations'));
  755. }
  756. /**
  757. * Tests that multiple translations saved when having a default locale
  758. * are correctly saved
  759. *
  760. * @return void
  761. */
  762. public function testSavingWithNonDefaultLocale()
  763. {
  764. $table = TableRegistry::get('Articles');
  765. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  766. $table->entityClass(__NAMESPACE__ . '\Article');
  767. I18n::locale('fra');
  768. $translations = [
  769. 'fra' => ['title' => 'Un article'],
  770. 'spa' => ['title' => 'Un artículo']
  771. ];
  772. $article = $table->get(1);
  773. foreach ($translations as $lang => $data) {
  774. $article->translation($lang)->set($data, ['guard' => false]);
  775. }
  776. $table->save($article);
  777. $article = $table->find('translations')->where(['Articles.id' => 1])->first();
  778. $this->assertEquals('Un article', $article->translation('fra')->title);
  779. $this->assertEquals('Un artículo', $article->translation('spa')->title);
  780. }
  781. /**
  782. * Tests that translation queries are added to union queries as well.
  783. *
  784. * @return void
  785. */
  786. public function testTranslationWithUnionQuery()
  787. {
  788. $table = TableRegistry::get('Comments');
  789. $table->addBehavior('Translate', ['fields' => ['comment']]);
  790. $table->locale('spa');
  791. $query = $table->find()->where(['Comments.id' => 6]);
  792. $query2 = $table->find()->where(['Comments.id' => 5]);
  793. $query->union($query2);
  794. $results = $query->sortBy('id', SORT_ASC)->toList();
  795. $this->assertCount(2, $results);
  796. $this->assertEquals('First Comment for Second Article', $results[0]->comment);
  797. $this->assertEquals('Second Comment for Second Article', $results[1]->comment);
  798. }
  799. /**
  800. * Tests the use of `referenceName` config option.
  801. *
  802. * @return void
  803. */
  804. public function testAutoReferenceName()
  805. {
  806. $table = TableRegistry::get('Articles');
  807. $table->hasMany('OtherComments', ['className' => 'Comments']);
  808. $table->OtherComments->addBehavior(
  809. 'Translate',
  810. ['fields' => ['comment']]
  811. );
  812. $items = $table->OtherComments->associations();
  813. $association = $items->getByProperty('comment_translation');
  814. $this->assertNotEmpty($association, 'Translation association not found');
  815. $found = false;
  816. foreach ($association->conditions() as $key => $value) {
  817. if (strpos($key, 'comment_translation.model') !== false) {
  818. $found = true;
  819. $this->assertEquals('Comments', $value);
  820. break;
  821. }
  822. }
  823. $this->assertTrue($found, '`referenceName` field condition on a Translation association was not found');
  824. }
  825. /**
  826. * Tests the use of unconventional `referenceName` config option.
  827. *
  828. * @return void
  829. */
  830. public function testChangingReferenceName()
  831. {
  832. $table = TableRegistry::get('Articles');
  833. $table->alias('FavoritePost');
  834. $table->addBehavior(
  835. 'Translate',
  836. ['fields' => ['body'], 'referenceName' => 'Posts']
  837. );
  838. $items = $table->associations();
  839. $association = $items->getByProperty('body_translation');
  840. $this->assertNotEmpty($association, 'Translation association not found');
  841. $found = false;
  842. foreach ($association->conditions() as $key => $value) {
  843. if (strpos($key, 'body_translation.model') !== false) {
  844. $found = true;
  845. $this->assertEquals('Posts', $value);
  846. break;
  847. }
  848. }
  849. $this->assertTrue($found, '`referenceName` field condition on a Translation association was not found');
  850. }
  851. /**
  852. * Tests that onlyTranslated will remove records from the result set
  853. * if they are not fully translated
  854. *
  855. * @return void
  856. */
  857. public function testFilterUntranslated()
  858. {
  859. $table = TableRegistry::get('Articles');
  860. $table->addBehavior('Translate', [
  861. 'fields' => ['title', 'body'],
  862. 'onlyTranslated' => true
  863. ]);
  864. $table->locale('eng');
  865. $results = $table->find()->where(['Articles.id' => 1])->all();
  866. $this->assertCount(1, $results);
  867. $table->locale('fr');
  868. $results = $table->find()->where(['Articles.id' => 1])->all();
  869. $this->assertCount(0, $results);
  870. }
  871. /**
  872. * Tests that records not translated in the current locale will not be
  873. * present in the results for the translations finder, and also proves
  874. * that this can be overridden.
  875. *
  876. * @return void
  877. */
  878. public function testFilterUntranslatedWithFinder()
  879. {
  880. $table = TableRegistry::get('Comments');
  881. $table->addBehavior('Translate', [
  882. 'fields' => ['comment'],
  883. 'onlyTranslated' => true
  884. ]);
  885. $table->locale('eng');
  886. $results = $table->find('translations')->all();
  887. $this->assertCount(4, $results);
  888. $table->locale('spa');
  889. $results = $table->find('translations')->all();
  890. $this->assertCount(1, $results);
  891. $table->locale('spa');
  892. $results = $table->find('translations', ['filterByCurrentLocale' => false])->all();
  893. $this->assertCount(6, $results);
  894. $table->locale('spa');
  895. $results = $table->find('translations')->all();
  896. $this->assertCount(1, $results);
  897. }
  898. /**
  899. * Tests that allowEmptyTranslations takes effect
  900. *
  901. * @return void
  902. */
  903. public function testEmptyTranslations()
  904. {
  905. $table = TableRegistry::get('Articles');
  906. $table->addBehavior('Translate', [
  907. 'fields' => ['title', 'body', 'description'],
  908. 'allowEmptyTranslations' => false,
  909. ]);
  910. $table->locale('spa');
  911. $result = $table->find()->first();
  912. $this->assertNull($result->description);
  913. }
  914. }