TranslateBehaviorTest.php 36 KB

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