TranslateBehaviorTest.php 68 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (https://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. (https://cakefoundation.org)
  11. * @link https://cakephp.org CakePHP(tm) Project
  12. * @since 3.0.0
  13. * @license https://opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Test\TestCase\ORM\Behavior;
  16. use Cake\Collection\Collection;
  17. use Cake\Datasource\EntityInterface;
  18. use Cake\I18n\I18n;
  19. use Cake\ORM\Behavior\Translate\TranslateTrait;
  20. use Cake\ORM\Entity;
  21. use Cake\ORM\Locator\TableLocator;
  22. use Cake\TestSuite\TestCase;
  23. use Cake\Validation\Validator;
  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.ArticlesTags',
  44. 'core.Authors',
  45. 'core.Groups',
  46. 'core.SpecialTags',
  47. 'core.Tags',
  48. 'core.Comments',
  49. 'core.Translates'
  50. ];
  51. public function tearDown()
  52. {
  53. parent::tearDown();
  54. I18n::setLocale(I18n::getDefaultLocale());
  55. $this->getTableLocator()->clear();
  56. }
  57. /**
  58. * Returns an array with all the translations found for a set of records
  59. *
  60. * @param array|\Traversable $data
  61. * @return Collection
  62. */
  63. protected function _extractTranslations($data)
  64. {
  65. return (new Collection($data))->map(function (EntityInterface $row) {
  66. $translations = $row->get('_translations');
  67. if (!$translations) {
  68. return [];
  69. }
  70. return array_map(function (EntityInterface $entity) {
  71. return $entity->toArray();
  72. }, $translations);
  73. });
  74. }
  75. /**
  76. * Tests that custom translation tables are respected
  77. *
  78. * @return void
  79. */
  80. public function testCustomTranslationTable()
  81. {
  82. $table = $this->getTableLocator()->get('Articles');
  83. $table->addBehavior('Translate', [
  84. 'translationTable' => '\TestApp\Model\Table\I18nTable',
  85. 'fields' => ['title', 'body']
  86. ]);
  87. $items = $table->associations();
  88. $i18n = $items->getByProperty('_i18n');
  89. $this->assertEquals('\TestApp\Model\Table\I18nTable', $i18n->getName());
  90. $this->assertInstanceOf('TestApp\Model\Table\I18nTable', $i18n->getTarget());
  91. $this->assertEquals('test_custom_i18n_datasource', $i18n->getTarget()->getConnection()->configName());
  92. $this->assertEquals('custom_i18n_table', $i18n->getTarget()->getTable());
  93. }
  94. /**
  95. * Tests that the strategy can be changed for i18n
  96. *
  97. * @return void
  98. */
  99. public function testStrategy()
  100. {
  101. $table = $this->getTableLocator()->get('Articles');
  102. $table->addBehavior('Translate', [
  103. 'strategy' => 'select',
  104. 'fields' => ['title', 'body']
  105. ]);
  106. $items = $table->associations();
  107. $i18n = $items->getByProperty('_i18n');
  108. $this->assertEquals('select', $i18n->getStrategy());
  109. }
  110. /**
  111. * Tests that fields from a translated model are overridden
  112. *
  113. * @return void
  114. */
  115. public function testFindSingleLocale()
  116. {
  117. $table = $this->getTableLocator()->get('Articles');
  118. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  119. $table->setLocale('eng');
  120. $results = $table->find()->combine('title', 'body', 'id')->toArray();
  121. $expected = [
  122. 1 => ['Title #1' => 'Content #1'],
  123. 2 => ['Title #2' => 'Content #2'],
  124. 3 => ['Title #3' => 'Content #3'],
  125. ];
  126. $this->assertSame($expected, $results);
  127. }
  128. /**
  129. * Test that iterating in a formatResults() does not drop data.
  130. *
  131. * @return void
  132. */
  133. public function testFindTranslationsFormatResultsIteration()
  134. {
  135. $table = $this->getTableLocator()->get('Articles');
  136. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  137. $table->setLocale('eng');
  138. $results = $table->find('translations')
  139. ->limit(1)
  140. ->formatResults(function ($results) {
  141. foreach ($results as $res) {
  142. $res->first = 'val';
  143. }
  144. foreach ($results as $res) {
  145. $res->second = 'loop';
  146. }
  147. return $results;
  148. })
  149. ->toArray();
  150. $this->assertCount(1, $results);
  151. $this->assertSame('Title #1', $results[0]->title);
  152. $this->assertSame('val', $results[0]->first);
  153. $this->assertSame('loop', $results[0]->second);
  154. $this->assertNotEmpty($results[0]->_translations);
  155. }
  156. /**
  157. * Tests that fields from a translated model use the I18n class locale
  158. * and that it propagates to associated models
  159. *
  160. * @return void
  161. */
  162. public function testFindSingleLocaleAssociatedEnv()
  163. {
  164. I18n::setLocale('eng');
  165. $table = $this->getTableLocator()->get('Articles');
  166. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  167. $table->hasMany('Comments');
  168. $table->Comments->addBehavior('Translate', ['fields' => ['comment']]);
  169. $results = $table->find()
  170. ->select(['id', 'title', 'body'])
  171. ->contain(['Comments' => ['fields' => ['article_id', 'comment']]])
  172. ->enableHydration(false)
  173. ->toArray();
  174. $expected = [
  175. [
  176. 'id' => 1,
  177. 'title' => 'Title #1',
  178. 'body' => 'Content #1',
  179. 'comments' => [
  180. ['article_id' => 1, 'comment' => 'Comment #1', '_locale' => 'eng'],
  181. ['article_id' => 1, 'comment' => 'Comment #2', '_locale' => 'eng'],
  182. ['article_id' => 1, 'comment' => 'Comment #3', '_locale' => 'eng'],
  183. ['article_id' => 1, 'comment' => 'Comment #4', '_locale' => 'eng']
  184. ],
  185. '_locale' => 'eng'
  186. ],
  187. [
  188. 'id' => 2,
  189. 'title' => 'Title #2',
  190. 'body' => 'Content #2',
  191. 'comments' => [
  192. ['article_id' => 2, 'comment' => 'First Comment for Second Article', '_locale' => 'eng'],
  193. ['article_id' => 2, 'comment' => 'Second Comment for Second Article', '_locale' => 'eng']
  194. ],
  195. '_locale' => 'eng'
  196. ],
  197. [
  198. 'id' => 3,
  199. 'title' => 'Title #3',
  200. 'body' => 'Content #3',
  201. 'comments' => [],
  202. '_locale' => 'eng'
  203. ]
  204. ];
  205. $this->assertSame($expected, $results);
  206. I18n::setLocale('spa');
  207. $results = $table->find()
  208. ->select(['id', 'title', 'body'])
  209. ->contain([
  210. 'Comments' => [
  211. 'fields' => ['article_id', 'comment'],
  212. 'sort' => ['Comments.id' => 'ASC']
  213. ]
  214. ])
  215. ->enableHydration(false)
  216. ->toArray();
  217. $expected = [
  218. [
  219. 'id' => 1,
  220. 'title' => 'First Article',
  221. 'body' => 'Contenido #1',
  222. 'comments' => [
  223. ['article_id' => 1, 'comment' => 'First Comment for First Article', '_locale' => 'spa'],
  224. ['article_id' => 1, 'comment' => 'Second Comment for First Article', '_locale' => 'spa'],
  225. ['article_id' => 1, 'comment' => 'Third Comment for First Article', '_locale' => 'spa'],
  226. ['article_id' => 1, 'comment' => 'Comentario #4', '_locale' => 'spa']
  227. ],
  228. '_locale' => 'spa'
  229. ],
  230. [
  231. 'id' => 2,
  232. 'title' => 'Second Article',
  233. 'body' => 'Second Article Body',
  234. 'comments' => [
  235. ['article_id' => 2, 'comment' => 'First Comment for Second Article', '_locale' => 'spa'],
  236. ['article_id' => 2, 'comment' => 'Second Comment for Second Article', '_locale' => 'spa']
  237. ],
  238. '_locale' => 'spa'
  239. ],
  240. [
  241. 'id' => 3,
  242. 'title' => 'Third Article',
  243. 'body' => 'Third Article Body',
  244. 'comments' => [],
  245. '_locale' => 'spa'
  246. ]
  247. ];
  248. $this->assertSame($expected, $results);
  249. }
  250. /**
  251. * Tests that fields from a translated model are not overridden if translation
  252. * is null
  253. *
  254. * @return void
  255. */
  256. public function testFindSingleLocaleWithNullTranslation()
  257. {
  258. $table = $this->getTableLocator()->get('Comments');
  259. $table->addBehavior('Translate', ['fields' => ['comment']]);
  260. $table->setLocale('spa');
  261. $results = $table->find()
  262. ->where(['Comments.id' => 6])
  263. ->combine('id', 'comment')->toArray();
  264. $expected = [6 => 'Second Comment for Second Article'];
  265. $this->assertSame($expected, $results);
  266. }
  267. /**
  268. * Tests that overriding fields with the translate behavior works when
  269. * using conditions and that all other columns are preserved
  270. *
  271. * @return void
  272. */
  273. public function testFindSingleLocaleWithgetConditions()
  274. {
  275. $table = $this->getTableLocator()->get('Articles');
  276. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  277. $table->setLocale('eng');
  278. $results = $table->find()
  279. ->where(['Articles.id' => 2])
  280. ->all();
  281. $this->assertCount(1, $results);
  282. $row = $results->first();
  283. $expected = [
  284. 'id' => 2,
  285. 'title' => 'Title #2',
  286. 'body' => 'Content #2',
  287. 'author_id' => 3,
  288. 'published' => 'Y',
  289. '_locale' => 'eng'
  290. ];
  291. $this->assertEquals($expected, $row->toArray());
  292. }
  293. /**
  294. * Tests the deprecated locale method.
  295. *
  296. * @group deprecated
  297. * @return void
  298. */
  299. public function testLocale()
  300. {
  301. $this->deprecated(function () {
  302. $table = $this->getTableLocator()->get('Articles');
  303. $table->addBehavior('Translate');
  304. $this->assertEquals('en_US', $table->locale());
  305. $table->locale('fr_FR');
  306. $this->assertEquals('fr_FR', $table->locale());
  307. $table->locale(false);
  308. $this->assertEquals('en_US', $table->locale());
  309. I18n::setLocale('fr_FR');
  310. $this->assertEquals('fr_FR', $table->locale());
  311. });
  312. }
  313. /**
  314. * Tests the locale setter/getter.
  315. *
  316. * @return void
  317. */
  318. public function testSetGetLocale()
  319. {
  320. $table = $this->getTableLocator()->get('Articles');
  321. $table->addBehavior('Translate');
  322. $this->assertEquals('en_US', $table->getLocale());
  323. $table->setLocale('fr_FR');
  324. $this->assertEquals('fr_FR', $table->getLocale());
  325. $table->setLocale(null);
  326. $this->assertEquals('en_US', $table->getLocale());
  327. I18n::setLocale('fr_FR');
  328. $this->assertEquals('fr_FR', $table->getLocale());
  329. }
  330. /**
  331. * Tests translationField method for translated fields.
  332. *
  333. * @return void
  334. */
  335. public function testTranslationFieldForTranslatedFields()
  336. {
  337. $table = $this->getTableLocator()->get('Articles');
  338. $table->addBehavior('Translate', [
  339. 'fields' => ['title', 'body'],
  340. 'defaultLocale' => 'en_US'
  341. ]);
  342. $expectedSameLocale = 'Articles.title';
  343. $expectedOtherLocale = 'Articles_title_translation.content';
  344. $field = $table->translationField('title');
  345. $this->assertSame($expectedSameLocale, $field);
  346. I18n::setLocale('es_ES');
  347. $field = $table->translationField('title');
  348. $this->assertSame($expectedOtherLocale, $field);
  349. I18n::setLocale('en');
  350. $field = $table->translationField('title');
  351. $this->assertSame($expectedOtherLocale, $field);
  352. $table->removeBehavior('Translate');
  353. $table->addBehavior('Translate', [
  354. 'fields' => ['title', 'body'],
  355. 'defaultLocale' => 'de_DE'
  356. ]);
  357. I18n::setLocale('de_DE');
  358. $field = $table->translationField('title');
  359. $this->assertSame($expectedSameLocale, $field);
  360. I18n::setLocale('en_US');
  361. $field = $table->translationField('title');
  362. $this->assertSame($expectedOtherLocale, $field);
  363. $table->setLocale('de_DE');
  364. $field = $table->translationField('title');
  365. $this->assertSame($expectedSameLocale, $field);
  366. $table->setLocale('es');
  367. $field = $table->translationField('title');
  368. $this->assertSame($expectedOtherLocale, $field);
  369. }
  370. /**
  371. * Tests translationField method for other fields.
  372. *
  373. * @return void
  374. */
  375. public function testTranslationFieldForOtherFields()
  376. {
  377. $table = $this->getTableLocator()->get('Articles');
  378. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  379. $expected = 'Articles.foo';
  380. $field = $table->translationField('foo');
  381. $this->assertSame($expected, $field);
  382. }
  383. /**
  384. * Tests that translating fields work when other formatters are used
  385. *
  386. * @return void
  387. */
  388. public function testFindList()
  389. {
  390. $table = $this->getTableLocator()->get('Articles');
  391. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  392. $table->setLocale('eng');
  393. $results = $table->find('list')->toArray();
  394. $expected = [1 => 'Title #1', 2 => 'Title #2', 3 => 'Title #3'];
  395. $this->assertSame($expected, $results);
  396. }
  397. /**
  398. * Tests that the query count return the correct results
  399. *
  400. * @return void
  401. */
  402. public function testFindCount()
  403. {
  404. $table = $this->getTableLocator()->get('Articles');
  405. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  406. $table->setLocale('eng');
  407. $this->assertEquals(3, $table->find()->count());
  408. }
  409. /**
  410. * Tests that it is possible to get all translated fields at once
  411. *
  412. * @return void
  413. */
  414. public function testFindTranslations()
  415. {
  416. $table = $this->getTableLocator()->get('Articles');
  417. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  418. $results = $table->find('translations');
  419. $expected = [
  420. [
  421. 'eng' => ['title' => 'Title #1', 'body' => 'Content #1', 'description' => 'Description #1', 'locale' => 'eng'],
  422. 'deu' => ['title' => 'Titel #1', 'body' => 'Inhalt #1', 'locale' => 'deu'],
  423. 'cze' => ['title' => 'Titulek #1', 'body' => 'Obsah #1', 'locale' => 'cze'],
  424. 'spa' => ['body' => 'Contenido #1', 'locale' => 'spa', 'description' => '']
  425. ],
  426. [
  427. 'eng' => ['title' => 'Title #2', 'body' => 'Content #2', 'locale' => 'eng'],
  428. 'deu' => ['title' => 'Titel #2', 'body' => 'Inhalt #2', 'locale' => 'deu'],
  429. 'cze' => ['title' => 'Titulek #2', 'body' => 'Obsah #2', 'locale' => 'cze']
  430. ],
  431. [
  432. 'eng' => ['title' => 'Title #3', 'body' => 'Content #3', 'locale' => 'eng'],
  433. 'deu' => ['title' => 'Titel #3', 'body' => 'Inhalt #3', 'locale' => 'deu'],
  434. 'cze' => ['title' => 'Titulek #3', 'body' => 'Obsah #3', 'locale' => 'cze']
  435. ]
  436. ];
  437. $translations = $this->_extractTranslations($results);
  438. $this->assertEquals($expected, $translations->toArray());
  439. $expected = [
  440. 1 => ['First Article' => 'First Article Body'],
  441. 2 => ['Second Article' => 'Second Article Body'],
  442. 3 => ['Third Article' => 'Third Article Body']
  443. ];
  444. $grouped = $results->combine('title', 'body', 'id');
  445. $this->assertEquals($expected, $grouped->toArray());
  446. }
  447. /**
  448. * Tests that it is possible to request just a few translations
  449. *
  450. * @return void
  451. */
  452. public function testFindFilteredTranslations()
  453. {
  454. $table = $this->getTableLocator()->get('Articles');
  455. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  456. $results = $table->find('translations', ['locales' => ['deu', 'cze']]);
  457. $expected = [
  458. [
  459. 'deu' => ['title' => 'Titel #1', 'body' => 'Inhalt #1', 'locale' => 'deu'],
  460. 'cze' => ['title' => 'Titulek #1', 'body' => 'Obsah #1', 'locale' => 'cze']
  461. ],
  462. [
  463. 'deu' => ['title' => 'Titel #2', 'body' => 'Inhalt #2', 'locale' => 'deu'],
  464. 'cze' => ['title' => 'Titulek #2', 'body' => 'Obsah #2', 'locale' => 'cze']
  465. ],
  466. [
  467. 'deu' => ['title' => 'Titel #3', 'body' => 'Inhalt #3', 'locale' => 'deu'],
  468. 'cze' => ['title' => 'Titulek #3', 'body' => 'Obsah #3', 'locale' => 'cze']
  469. ]
  470. ];
  471. $translations = $this->_extractTranslations($results);
  472. $this->assertEquals($expected, $translations->toArray());
  473. $expected = [
  474. 1 => ['First Article' => 'First Article Body'],
  475. 2 => ['Second Article' => 'Second Article Body'],
  476. 3 => ['Third Article' => 'Third Article Body']
  477. ];
  478. $grouped = $results->combine('title', 'body', 'id');
  479. $this->assertEquals($expected, $grouped->toArray());
  480. }
  481. /**
  482. * Tests that it is possible to combine find('list') and find('translations')
  483. *
  484. * @return void
  485. */
  486. public function testFindTranslationsList()
  487. {
  488. $table = $this->getTableLocator()->get('Articles');
  489. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  490. $results = $table
  491. ->find('list', [
  492. 'keyField' => 'title',
  493. 'valueField' => '_translations.deu.title',
  494. 'groupField' => 'id'
  495. ])
  496. ->find('translations', ['locales' => ['deu']]);
  497. $expected = [
  498. 1 => ['First Article' => 'Titel #1'],
  499. 2 => ['Second Article' => 'Titel #2'],
  500. 3 => ['Third Article' => 'Titel #3']
  501. ];
  502. $this->assertEquals($expected, $results->toArray());
  503. }
  504. /**
  505. * Tests that you can both override fields and find all translations
  506. *
  507. * @return void
  508. */
  509. public function testFindTranslationsWithFieldOverriding()
  510. {
  511. $table = $this->getTableLocator()->get('Articles');
  512. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  513. $table->setLocale('cze');
  514. $results = $table->find('translations', ['locales' => ['deu', 'cze']]);
  515. $expected = [
  516. [
  517. 'deu' => ['title' => 'Titel #1', 'body' => 'Inhalt #1', 'locale' => 'deu'],
  518. 'cze' => ['title' => 'Titulek #1', 'body' => 'Obsah #1', 'locale' => 'cze']
  519. ],
  520. [
  521. 'deu' => ['title' => 'Titel #2', 'body' => 'Inhalt #2', 'locale' => 'deu'],
  522. 'cze' => ['title' => 'Titulek #2', 'body' => 'Obsah #2', 'locale' => 'cze']
  523. ],
  524. [
  525. 'deu' => ['title' => 'Titel #3', 'body' => 'Inhalt #3', 'locale' => 'deu'],
  526. 'cze' => ['title' => 'Titulek #3', 'body' => 'Obsah #3', 'locale' => 'cze']
  527. ]
  528. ];
  529. $translations = $this->_extractTranslations($results);
  530. $this->assertEquals($expected, $translations->toArray());
  531. $expected = [
  532. 1 => ['Titulek #1' => 'Obsah #1'],
  533. 2 => ['Titulek #2' => 'Obsah #2'],
  534. 3 => ['Titulek #3' => 'Obsah #3']
  535. ];
  536. $grouped = $results->combine('title', 'body', 'id');
  537. $this->assertEquals($expected, $grouped->toArray());
  538. }
  539. /**
  540. * Tests that fields can be overridden in a hasMany association
  541. *
  542. * @return void
  543. */
  544. public function testFindSingleLocaleHasMany()
  545. {
  546. $table = $this->getTableLocator()->get('Articles');
  547. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  548. $table->hasMany('Comments');
  549. $comments = $table->hasMany('Comments')->getTarget();
  550. $comments->addBehavior('Translate', ['fields' => ['comment']]);
  551. $table->setLocale('eng');
  552. $comments->setLocale('eng');
  553. $results = $table->find()->contain(['Comments' => function ($q) {
  554. return $q->select(['id', 'comment', 'article_id']);
  555. }]);
  556. $list = new Collection($results->first()->comments);
  557. $expected = [
  558. 1 => 'Comment #1',
  559. 2 => 'Comment #2',
  560. 3 => 'Comment #3',
  561. 4 => 'Comment #4'
  562. ];
  563. $this->assertEquals($expected, $list->combine('id', 'comment')->toArray());
  564. }
  565. /**
  566. * Test that it is possible to bring translations from hasMany relations
  567. *
  568. * @return void
  569. */
  570. public function testTranslationsHasMany()
  571. {
  572. $table = $this->getTableLocator()->get('Articles');
  573. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  574. $table->hasMany('Comments');
  575. $comments = $table->hasMany('Comments')->getTarget();
  576. $comments->addBehavior('Translate', ['fields' => ['comment']]);
  577. $results = $table->find('translations')->contain([
  578. 'Comments' => function ($q) {
  579. return $q->find('translations')->select(['id', 'comment', 'article_id']);
  580. }
  581. ]);
  582. $comments = $results->first()->comments;
  583. $expected = [
  584. [
  585. 'eng' => ['comment' => 'Comment #1', 'locale' => 'eng']
  586. ],
  587. [
  588. 'eng' => ['comment' => 'Comment #2', 'locale' => 'eng']
  589. ],
  590. [
  591. 'eng' => ['comment' => 'Comment #3', 'locale' => 'eng']
  592. ],
  593. [
  594. 'eng' => ['comment' => 'Comment #4', 'locale' => 'eng'],
  595. 'spa' => ['comment' => 'Comentario #4', 'locale' => 'spa']
  596. ]
  597. ];
  598. $translations = $this->_extractTranslations($comments);
  599. $this->assertEquals($expected, $translations->toArray());
  600. }
  601. /**
  602. * Tests that it is possible to both override fields with a translation and
  603. * also find separately other translations
  604. *
  605. * @return void
  606. */
  607. public function testTranslationsHasManyWithOverride()
  608. {
  609. $table = $this->getTableLocator()->get('Articles');
  610. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  611. $table->hasMany('Comments');
  612. $comments = $table->hasMany('Comments')->getTarget();
  613. $comments->addBehavior('Translate', ['fields' => ['comment']]);
  614. $table->setLocale('cze');
  615. $comments->setLocale('eng');
  616. $results = $table->find('translations')->contain([
  617. 'Comments' => function ($q) {
  618. return $q->find('translations')->select(['id', 'comment', 'article_id']);
  619. }
  620. ]);
  621. $comments = $results->first()->comments;
  622. $expected = [
  623. 1 => 'Comment #1',
  624. 2 => 'Comment #2',
  625. 3 => 'Comment #3',
  626. 4 => 'Comment #4'
  627. ];
  628. $list = new Collection($comments);
  629. $this->assertEquals($expected, $list->combine('id', 'comment')->toArray());
  630. $expected = [
  631. [
  632. 'eng' => ['comment' => 'Comment #1', 'locale' => 'eng']
  633. ],
  634. [
  635. 'eng' => ['comment' => 'Comment #2', 'locale' => 'eng']
  636. ],
  637. [
  638. 'eng' => ['comment' => 'Comment #3', 'locale' => 'eng']
  639. ],
  640. [
  641. 'eng' => ['comment' => 'Comment #4', 'locale' => 'eng'],
  642. 'spa' => ['comment' => 'Comentario #4', 'locale' => 'spa']
  643. ]
  644. ];
  645. $translations = $this->_extractTranslations($comments);
  646. $this->assertEquals($expected, $translations->toArray());
  647. $this->assertEquals('Titulek #1', $results->first()->title);
  648. $this->assertEquals('Obsah #1', $results->first()->body);
  649. }
  650. /**
  651. * Tests that it is possible to translate belongsTo associations
  652. *
  653. * @return void
  654. */
  655. public function testFindSingleLocaleBelongsto()
  656. {
  657. $table = $this->getTableLocator()->get('Articles');
  658. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  659. $authors = $table->belongsTo('Authors')->getTarget();
  660. $authors->addBehavior('Translate', ['fields' => ['name']]);
  661. $table->setLocale('eng');
  662. $authors->setLocale('eng');
  663. $results = $table->find()
  664. ->select(['title', 'body'])
  665. ->order(['title' => 'asc'])
  666. ->contain(['Authors' => function ($q) {
  667. return $q->select(['id', 'name']);
  668. }]);
  669. $expected = [
  670. [
  671. 'title' => 'Title #1',
  672. 'body' => 'Content #1',
  673. 'author' => ['id' => 1, 'name' => 'May-rianoh', '_locale' => 'eng'],
  674. '_locale' => 'eng'
  675. ],
  676. [
  677. 'title' => 'Title #2',
  678. 'body' => 'Content #2',
  679. 'author' => ['id' => 3, 'name' => 'larry', '_locale' => 'eng'],
  680. '_locale' => 'eng'
  681. ],
  682. [
  683. 'title' => 'Title #3',
  684. 'body' => 'Content #3',
  685. 'author' => ['id' => 1, 'name' => 'May-rianoh', '_locale' => 'eng'],
  686. '_locale' => 'eng'
  687. ]
  688. ];
  689. $results = array_map(function ($r) {
  690. return $r->toArray();
  691. }, $results->toArray());
  692. $this->assertEquals($expected, $results);
  693. }
  694. /**
  695. * Tests that it is possible to translate belongsTo associations using loadInto
  696. *
  697. * @return void
  698. */
  699. public function testFindSingleLocaleBelongstoLoadInto()
  700. {
  701. $table = $this->getTableLocator()->get('Articles');
  702. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  703. $authors = $table->belongsTo('Authors')->getTarget();
  704. $authors->addBehavior('Translate', ['fields' => ['name']]);
  705. $table->setLocale('eng');
  706. $authors->setLocale('eng');
  707. $entity = $table->get(1);
  708. $result = $table->loadInto($entity, ['Authors']);
  709. $this->assertSame($entity, $result);
  710. $this->assertNotEmpty($entity->author);
  711. $this->assertNotEmpty($entity->author->name);
  712. $expected = $table->get(1, ['contain' => ['Authors']]);
  713. $this->assertEquals($expected, $result);
  714. $this->assertNotEmpty($entity->author);
  715. $this->assertNotEmpty($entity->author->name);
  716. }
  717. /**
  718. * Tests that it is possible to translate belongsToMany associations
  719. *
  720. * @return void
  721. */
  722. public function testFindSingleLocaleBelongsToMany()
  723. {
  724. $table = $this->getTableLocator()->get('Articles');
  725. $specialTags = $this->getTableLocator()->get('SpecialTags');
  726. $specialTags->addBehavior('Translate', ['fields' => ['extra_info']]);
  727. $table->belongsToMany('Tags', [
  728. 'through' => $specialTags
  729. ]);
  730. $specialTags->setLocale('eng');
  731. $result = $table->get(2, ['contain' => 'Tags']);
  732. $this->assertNotEmpty($result);
  733. $this->assertNotEmpty($result->tags);
  734. $this->assertEquals('Translated Info', $result->tags[0]->special_tags[0]->extra_info);
  735. }
  736. /**
  737. * Tests that updating an existing record translations work
  738. *
  739. * @return void
  740. */
  741. public function testUpdateSingleLocale()
  742. {
  743. $table = $this->getTableLocator()->get('Articles');
  744. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  745. $table->setLocale('eng');
  746. $article = $table->find()->first();
  747. $this->assertEquals(1, $article->get('id'));
  748. $article->set('title', 'New translated article');
  749. $table->save($article);
  750. $this->assertNull($article->get('_i18n'));
  751. $article = $table->find()->first();
  752. $this->assertEquals(1, $article->get('id'));
  753. $this->assertEquals('New translated article', $article->get('title'));
  754. $this->assertEquals('Content #1', $article->get('body'));
  755. $table->setLocale(false);
  756. $article = $table->find()->first();
  757. $this->assertEquals(1, $article->get('id'));
  758. $this->assertEquals('First Article', $article->get('title'));
  759. $table->setLocale('eng');
  760. $article->set('title', 'Wow, such translated article');
  761. $article->set('body', 'A translated body');
  762. $table->save($article);
  763. $this->assertNull($article->get('_i18n'));
  764. $article = $table->find()->first();
  765. $this->assertEquals(1, $article->get('id'));
  766. $this->assertEquals('Wow, such translated article', $article->get('title'));
  767. $this->assertEquals('A translated body', $article->get('body'));
  768. }
  769. /**
  770. * Tests adding new translation to a record
  771. *
  772. * @return void
  773. */
  774. public function testInsertNewTranslations()
  775. {
  776. $table = $this->getTableLocator()->get('Articles');
  777. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  778. $table->setLocale('fra');
  779. $article = $table->find()->first();
  780. $this->assertEquals(1, $article->get('id'));
  781. $article->set('title', 'Le titre');
  782. $table->save($article);
  783. $this->assertEquals('fra', $article->get('_locale'));
  784. $article = $table->find()->first();
  785. $this->assertEquals(1, $article->get('id'));
  786. $this->assertEquals('Le titre', $article->get('title'));
  787. $this->assertEquals('First Article Body', $article->get('body'));
  788. $article->set('title', 'Un autre titre');
  789. $article->set('body', 'Le contenu');
  790. $table->save($article);
  791. $this->assertNull($article->get('_i18n'));
  792. $article = $table->find()->first();
  793. $this->assertEquals('Un autre titre', $article->get('title'));
  794. $this->assertEquals('Le contenu', $article->get('body'));
  795. }
  796. /**
  797. * Tests adding new translation to a record
  798. *
  799. * @return void
  800. */
  801. public function testAllowEmptyFalse()
  802. {
  803. $table = $this->getTableLocator()->get('Articles');
  804. $table->addBehavior('Translate', ['fields' => ['title'], 'allowEmptyTranslations' => false]);
  805. $article = $table->find()->first();
  806. $this->assertEquals(1, $article->get('id'));
  807. $article = $table->patchEntity($article, [
  808. '_translations' => [
  809. 'fra' => [
  810. 'title' => ''
  811. ]
  812. ]
  813. ]);
  814. $table->save($article);
  815. // Remove the Behavior to unset the content != '' condition
  816. $table->removeBehavior('Translate');
  817. $noFra = $table->I18n->find()->where(['locale' => 'fra'])->first();
  818. $this->assertEmpty($noFra);
  819. }
  820. /**
  821. * Tests adding new translation to a record
  822. *
  823. * @return void
  824. */
  825. public function testMixedAllowEmptyFalse()
  826. {
  827. $table = $this->getTableLocator()->get('Articles');
  828. $table->addBehavior('Translate', ['fields' => ['title', 'body'], 'allowEmptyTranslations' => false]);
  829. $article = $table->find()->first();
  830. $this->assertEquals(1, $article->get('id'));
  831. $article = $table->patchEntity($article, [
  832. '_translations' => [
  833. 'fra' => [
  834. 'title' => '',
  835. 'body' => 'Bonjour'
  836. ]
  837. ]
  838. ]);
  839. $table->save($article);
  840. $fra = $table->I18n->find()
  841. ->where([
  842. 'locale' => 'fra',
  843. 'field' => 'body'
  844. ])
  845. ->first();
  846. $this->assertSame('Bonjour', $fra->content);
  847. // Remove the Behavior to unset the content != '' condition
  848. $table->removeBehavior('Translate');
  849. $noTitle = $table->I18n->find()
  850. ->where([
  851. 'locale' => 'fra',
  852. 'field' => 'title'
  853. ])
  854. ->first();
  855. $this->assertEmpty($noTitle);
  856. }
  857. /**
  858. * Tests adding new translation to a record
  859. *
  860. * @return void
  861. */
  862. public function testMultipleAllowEmptyFalse()
  863. {
  864. $table = $this->getTableLocator()->get('Articles');
  865. $table->addBehavior('Translate', ['fields' => ['title', 'body'], 'allowEmptyTranslations' => false]);
  866. $article = $table->find()->first();
  867. $this->assertEquals(1, $article->get('id'));
  868. $article = $table->patchEntity($article, [
  869. '_translations' => [
  870. 'fra' => [
  871. 'title' => '',
  872. 'body' => 'Bonjour'
  873. ],
  874. 'de' => [
  875. 'title' => 'Titel',
  876. 'body' => 'Hallo'
  877. ]
  878. ]
  879. ]);
  880. $table->save($article);
  881. $fra = $table->I18n->find()
  882. ->where([
  883. 'locale' => 'fra',
  884. 'field' => 'body'
  885. ])
  886. ->first();
  887. $this->assertSame('Bonjour', $fra->content);
  888. $deTitle = $table->I18n->find()
  889. ->where([
  890. 'locale' => 'de',
  891. 'field' => 'title'
  892. ])
  893. ->first();
  894. $this->assertSame('Titel', $deTitle->content);
  895. $deBody = $table->I18n->find()
  896. ->where([
  897. 'locale' => 'de',
  898. 'field' => 'body'
  899. ])
  900. ->first();
  901. $this->assertSame('Hallo', $deBody->content);
  902. // Remove the Behavior to unset the content != '' condition
  903. $table->removeBehavior('Translate');
  904. $noTitle = $table->I18n->find()
  905. ->where([
  906. 'locale' => 'fra',
  907. 'field' => 'title'
  908. ])
  909. ->first();
  910. $this->assertEmpty($noTitle);
  911. }
  912. /**
  913. * Tests that it is possible to use the _locale property to specify the language
  914. * to use for saving an entity
  915. *
  916. * @return void
  917. */
  918. public function testUpdateTranslationWithLocaleInEntity()
  919. {
  920. $table = $this->getTableLocator()->get('Articles');
  921. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  922. $article = $table->find()->first();
  923. $this->assertEquals(1, $article->get('id'));
  924. $article->set('_locale', 'fra');
  925. $article->set('title', 'Le titre');
  926. $table->save($article);
  927. $this->assertNull($article->get('_i18n'));
  928. $article = $table->find()->first();
  929. $this->assertEquals(1, $article->get('id'));
  930. $this->assertEquals('First Article', $article->get('title'));
  931. $this->assertEquals('First Article Body', $article->get('body'));
  932. $table->setLocale('fra');
  933. $article = $table->find()->first();
  934. $this->assertEquals(1, $article->get('id'));
  935. $this->assertEquals('Le titre', $article->get('title'));
  936. $this->assertEquals('First Article Body', $article->get('body'));
  937. }
  938. /**
  939. * Tests that translations are added to the whitelist of associations to be
  940. * saved
  941. *
  942. * @return void
  943. */
  944. public function testSaveTranslationWithAssociationWhitelist()
  945. {
  946. $table = $this->getTableLocator()->get('Articles');
  947. $table->hasMany('Comments');
  948. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  949. $table->setLocale('fra');
  950. $article = $table->find()->first();
  951. $this->assertEquals(1, $article->get('id'));
  952. $article->set('title', 'Le titre');
  953. $table->save($article, ['associated' => ['Comments']]);
  954. $this->assertNull($article->get('_i18n'));
  955. $article = $table->find()->first();
  956. $this->assertEquals('Le titre', $article->get('title'));
  957. }
  958. /**
  959. * Tests that after deleting a translated entity, all translations are also removed
  960. *
  961. * @return void
  962. */
  963. public function testDelete()
  964. {
  965. $table = $this->getTableLocator()->get('Articles');
  966. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  967. $article = $table->find()->first();
  968. $this->assertTrue($table->delete($article));
  969. $translations = $this->getTableLocator()->get('I18n')->find()
  970. ->where(['model' => 'Articles', 'foreign_key' => $article->id])
  971. ->count();
  972. $this->assertEquals(0, $translations);
  973. }
  974. /**
  975. * Tests saving multiple translations at once when the translations already
  976. * exist in the database
  977. *
  978. * @return void
  979. */
  980. public function testSaveMultipleTranslations()
  981. {
  982. $table = $this->getTableLocator()->get('Articles');
  983. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  984. $article = $results = $table->find('translations')->first();
  985. $translations = $article->get('_translations');
  986. $translations['deu']->set('title', 'Another title');
  987. $translations['eng']->set('body', 'Another body');
  988. $article->set('_translations', $translations);
  989. $table->save($article);
  990. $this->assertNull($article->get('_i18n'));
  991. $article = $results = $table->find('translations')->first();
  992. $translations = $article->get('_translations');
  993. $this->assertEquals('Another title', $translations['deu']->get('title'));
  994. $this->assertEquals('Another body', $translations['eng']->get('body'));
  995. }
  996. /**
  997. * Tests saving multiple existing translations and adding new ones
  998. *
  999. * @return void
  1000. */
  1001. public function testSaveMultipleNewTranslations()
  1002. {
  1003. $table = $this->getTableLocator()->get('Articles');
  1004. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  1005. $article = $results = $table->find('translations')->first();
  1006. $translations = $article->get('_translations');
  1007. $translations['deu']->set('title', 'Another title');
  1008. $translations['eng']->set('body', 'Another body');
  1009. $translations['spa'] = new Entity(['title' => 'Titulo']);
  1010. $translations['fre'] = new Entity(['title' => 'Titre']);
  1011. $article->set('_translations', $translations);
  1012. $table->save($article);
  1013. $this->assertNull($article->get('_i18n'));
  1014. $article = $results = $table->find('translations')->first();
  1015. $translations = $article->get('_translations');
  1016. $this->assertEquals('Another title', $translations['deu']->get('title'));
  1017. $this->assertEquals('Another body', $translations['eng']->get('body'));
  1018. $this->assertEquals('Titulo', $translations['spa']->get('title'));
  1019. $this->assertEquals('Titre', $translations['fre']->get('title'));
  1020. }
  1021. /**
  1022. * Tests that iterating a resultset twice when using the translations finder
  1023. * will not cause any errors nor information loss
  1024. *
  1025. * @return void
  1026. */
  1027. public function testUseCountInFindTranslations()
  1028. {
  1029. $table = $this->getTableLocator()->get('Articles');
  1030. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  1031. $articles = $results = $table->find('translations');
  1032. $all = $articles->all();
  1033. $this->assertCount(3, $all);
  1034. $article = $all->first();
  1035. $this->assertNotEmpty($article->get('_translations'));
  1036. }
  1037. /**
  1038. * Tests that multiple translations saved when having a default locale
  1039. * are correctly saved
  1040. *
  1041. * @return void
  1042. */
  1043. public function testSavingWithNonDefaultLocale()
  1044. {
  1045. $table = $this->getTableLocator()->get('Articles');
  1046. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  1047. $table->setEntityClass(__NAMESPACE__ . '\Article');
  1048. I18n::setLocale('fra');
  1049. $translations = [
  1050. 'fra' => ['title' => 'Un article'],
  1051. 'spa' => ['title' => 'Un artículo']
  1052. ];
  1053. $article = $table->get(1);
  1054. foreach ($translations as $lang => $data) {
  1055. $article->translation($lang)->set($data, ['guard' => false]);
  1056. }
  1057. $table->save($article);
  1058. $article = $table->find('translations')->where(['Articles.id' => 1])->first();
  1059. $this->assertEquals('Un article', $article->translation('fra')->title);
  1060. $this->assertEquals('Un artículo', $article->translation('spa')->title);
  1061. }
  1062. /**
  1063. * Tests that translation queries are added to union queries as well.
  1064. *
  1065. * @return void
  1066. */
  1067. public function testTranslationWithUnionQuery()
  1068. {
  1069. $table = $this->getTableLocator()->get('Comments');
  1070. $table->addBehavior('Translate', ['fields' => ['comment']]);
  1071. $table->setLocale('spa');
  1072. $query = $table->find()->where(['Comments.id' => 6]);
  1073. $query2 = $table->find()->where(['Comments.id' => 5]);
  1074. $query->union($query2);
  1075. $results = $query->sortBy('id', SORT_ASC)->toList();
  1076. $this->assertCount(2, $results);
  1077. $this->assertEquals('First Comment for Second Article', $results[0]->comment);
  1078. $this->assertEquals('Second Comment for Second Article', $results[1]->comment);
  1079. }
  1080. /**
  1081. * Tests the use of `referenceName` config option.
  1082. *
  1083. * @return void
  1084. */
  1085. public function testAutoReferenceName()
  1086. {
  1087. $table = $this->getTableLocator()->get('Articles');
  1088. $table->hasMany('OtherComments', ['className' => 'Comments']);
  1089. $table->OtherComments->addBehavior(
  1090. 'Translate',
  1091. ['fields' => ['comment']]
  1092. );
  1093. $items = $table->OtherComments->associations();
  1094. $association = $items->getByProperty('comment_translation');
  1095. $this->assertNotEmpty($association, 'Translation association not found');
  1096. $found = false;
  1097. foreach ($association->getConditions() as $key => $value) {
  1098. if (strpos($key, 'comment_translation.model') !== false) {
  1099. $found = true;
  1100. $this->assertEquals('Comments', $value);
  1101. break;
  1102. }
  1103. }
  1104. $this->assertTrue($found, '`referenceName` field condition on a Translation association was not found');
  1105. }
  1106. /**
  1107. * Tests the use of unconventional `referenceName` config option.
  1108. *
  1109. * @return void
  1110. */
  1111. public function testChangingReferenceName()
  1112. {
  1113. $table = $this->getTableLocator()->get('Articles');
  1114. $table->setAlias('FavoritePost');
  1115. $table->addBehavior(
  1116. 'Translate',
  1117. ['fields' => ['body'], 'referenceName' => 'Posts']
  1118. );
  1119. $items = $table->associations();
  1120. $association = $items->getByProperty('body_translation');
  1121. $this->assertNotEmpty($association, 'Translation association not found');
  1122. $found = false;
  1123. foreach ($association->getConditions() as $key => $value) {
  1124. if (strpos($key, 'body_translation.model') !== false) {
  1125. $found = true;
  1126. $this->assertEquals('Posts', $value);
  1127. break;
  1128. }
  1129. }
  1130. $this->assertTrue($found, '`referenceName` field condition on a Translation association was not found');
  1131. }
  1132. /**
  1133. * Tests that onlyTranslated will remove records from the result set
  1134. * if they are not fully translated
  1135. *
  1136. * @return void
  1137. */
  1138. public function testFilterUntranslated()
  1139. {
  1140. $table = $this->getTableLocator()->get('Articles');
  1141. $table->addBehavior('Translate', [
  1142. 'fields' => ['title', 'body'],
  1143. 'onlyTranslated' => true
  1144. ]);
  1145. $table->setLocale('eng');
  1146. $results = $table->find()->where(['Articles.id' => 1])->all();
  1147. $this->assertCount(1, $results);
  1148. $table->setLocale('fr');
  1149. $results = $table->find()->where(['Articles.id' => 1])->all();
  1150. $this->assertCount(0, $results);
  1151. }
  1152. /**
  1153. * Tests that records not translated in the current locale will not be
  1154. * present in the results for the translations finder, and also proves
  1155. * that this can be overridden.
  1156. *
  1157. * @return void
  1158. */
  1159. public function testFilterUntranslatedWithFinder()
  1160. {
  1161. $table = $this->getTableLocator()->get('Comments');
  1162. $table->addBehavior('Translate', [
  1163. 'fields' => ['comment'],
  1164. 'onlyTranslated' => true
  1165. ]);
  1166. $table->setLocale('eng');
  1167. $results = $table->find('translations')->all();
  1168. $this->assertCount(4, $results);
  1169. $table->setLocale('spa');
  1170. $results = $table->find('translations')->all();
  1171. $this->assertCount(1, $results);
  1172. $table->setLocale('spa');
  1173. $results = $table->find('translations', ['filterByCurrentLocale' => false])->all();
  1174. $this->assertCount(6, $results);
  1175. $table->setLocale('spa');
  1176. $results = $table->find('translations')->all();
  1177. $this->assertCount(1, $results);
  1178. }
  1179. /**
  1180. * Tests that allowEmptyTranslations takes effect
  1181. *
  1182. * @return void
  1183. */
  1184. public function testEmptyTranslations()
  1185. {
  1186. $table = $this->getTableLocator()->get('Articles');
  1187. $table->addBehavior('Translate', [
  1188. 'fields' => ['title', 'body', 'description'],
  1189. 'allowEmptyTranslations' => false,
  1190. ]);
  1191. $table->setLocale('spa');
  1192. $result = $table->find()->first();
  1193. $this->assertNull($result->description);
  1194. }
  1195. /**
  1196. * Test save with clean translate fields
  1197. *
  1198. * @return void
  1199. */
  1200. public function testSaveWithCleanFields()
  1201. {
  1202. $table = $this->getTableLocator()->get('Articles');
  1203. $table->addBehavior('Translate', ['fields' => ['title']]);
  1204. $table->setEntityClass(__NAMESPACE__ . '\Article');
  1205. I18n::setLocale('fra');
  1206. $article = $table->get(1);
  1207. $article->set('body', 'New Body');
  1208. $table->save($article);
  1209. $result = $table->get(1);
  1210. $this->assertEquals('New Body', $result->body);
  1211. $this->assertSame($article->title, $result->title);
  1212. }
  1213. /**
  1214. * Test save new entity with _translations field
  1215. *
  1216. * @return void
  1217. */
  1218. public function testSaveNewRecordWithTranslatesField()
  1219. {
  1220. $table = $this->getTableLocator()->get('Articles');
  1221. $table->addBehavior('Translate', [
  1222. 'fields' => ['title'],
  1223. 'validator' => (new \Cake\Validation\Validator)->add('title', 'notBlank', ['rule' => 'notBlank'])
  1224. ]);
  1225. $table->setEntityClass(__NAMESPACE__ . '\Article');
  1226. $data = [
  1227. 'author_id' => 1,
  1228. 'published' => 'N',
  1229. '_translations' => [
  1230. 'en' => [
  1231. 'title' => 'Title EN',
  1232. 'body' => 'Body EN'
  1233. ],
  1234. 'es' => [
  1235. 'title' => 'Title ES'
  1236. ]
  1237. ]
  1238. ];
  1239. $article = $table->patchEntity($table->newEntity(), $data);
  1240. $result = $table->save($article);
  1241. $this->assertNotFalse($result);
  1242. $expected = [
  1243. [
  1244. 'en' => [
  1245. 'title' => 'Title EN',
  1246. 'locale' => 'en'
  1247. ],
  1248. 'es' => [
  1249. 'title' => 'Title ES',
  1250. 'locale' => 'es'
  1251. ]
  1252. ]
  1253. ];
  1254. $result = $table->find('translations')->where(['id' => $result->id]);
  1255. $this->assertEquals($expected, $this->_extractTranslations($result)->toArray());
  1256. }
  1257. /**
  1258. * Tests adding new translation to a record where the only field is the translated one and it's not the default locale
  1259. *
  1260. * @return void
  1261. */
  1262. public function testSaveNewRecordWithOnlyTranslationsNotDefaultLocale()
  1263. {
  1264. $table = $this->getTableLocator()->get('Groups');
  1265. $table->addBehavior('Translate', [
  1266. 'fields' => ['title'],
  1267. 'validator' => (new \Cake\Validation\Validator)->add('title', 'notBlank', ['rule' => 'notBlank'])
  1268. ]);
  1269. $data = [
  1270. '_translations' => [
  1271. 'es' => [
  1272. 'title' => 'Title ES'
  1273. ]
  1274. ]
  1275. ];
  1276. $group = $table->newEntity($data);
  1277. $result = $table->save($group);
  1278. $this->assertNotFalse($result, 'Record should save.');
  1279. $expected = [
  1280. [
  1281. 'es' => [
  1282. 'title' => 'Title ES',
  1283. 'locale' => 'es'
  1284. ]
  1285. ]
  1286. ];
  1287. $result = $table->find('translations')->where(['id' => $result->id]);
  1288. $this->assertEquals($expected, $this->_extractTranslations($result)->toArray());
  1289. }
  1290. /**
  1291. * Test that existing records can be updated when only translations
  1292. * are modified/dirty.
  1293. *
  1294. * @return void
  1295. */
  1296. public function testSaveExistingRecordOnlyTranslations()
  1297. {
  1298. $table = $this->getTableLocator()->get('Articles');
  1299. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  1300. $table->setEntityClass(__NAMESPACE__ . '\Article');
  1301. $data = [
  1302. '_translations' => [
  1303. 'es' => [
  1304. 'title' => 'Spanish Translation',
  1305. ],
  1306. ]
  1307. ];
  1308. $article = $table->find()->first();
  1309. $article = $table->patchEntity($article, $data);
  1310. $this->assertNotFalse($table->save($article));
  1311. $results = $this->_extractTranslations(
  1312. $table->find('translations')->where(['id' => 1])
  1313. )->first();
  1314. $this->assertArrayHasKey('es', $results, 'New translation added');
  1315. $this->assertArrayHasKey('eng', $results, 'Old translations present');
  1316. $this->assertEquals('Spanish Translation', $results['es']['title']);
  1317. }
  1318. /**
  1319. * Test update entity with _translations field.
  1320. *
  1321. * @return void
  1322. */
  1323. public function testSaveExistingRecordWithTranslatesField()
  1324. {
  1325. $table = $this->getTableLocator()->get('Articles');
  1326. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  1327. $table->setEntityClass(__NAMESPACE__ . '\Article');
  1328. $data = [
  1329. 'author_id' => 1,
  1330. 'published' => 'Y',
  1331. '_translations' => [
  1332. 'eng' => [
  1333. 'title' => 'First Article1',
  1334. 'body' => 'First Article content has been updated'
  1335. ],
  1336. 'spa' => [
  1337. 'title' => 'Mi nuevo titulo',
  1338. 'body' => 'Contenido Actualizado'
  1339. ]
  1340. ]
  1341. ];
  1342. $article = $table->find()->first();
  1343. $article = $table->patchEntity($article, $data);
  1344. $this->assertNotFalse($table->save($article));
  1345. $results = $this->_extractTranslations(
  1346. $table->find('translations')->where(['id' => 1])
  1347. )->first();
  1348. $this->assertEquals('Mi nuevo titulo', $results['spa']['title']);
  1349. $this->assertEquals('Contenido Actualizado', $results['spa']['body']);
  1350. $this->assertEquals('First Article1', $results['eng']['title']);
  1351. $this->assertEquals('Description #1', $results['eng']['description']);
  1352. }
  1353. /**
  1354. * Tests that default locale saves ok.
  1355. *
  1356. * @return void
  1357. */
  1358. public function testSaveDefaultLocale()
  1359. {
  1360. $table = $this->getTableLocator()->get('Articles');
  1361. $table->hasMany('Comments');
  1362. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  1363. $article = $table->get(1);
  1364. $data = [
  1365. 'title' => 'New title',
  1366. 'body' => 'New body',
  1367. ];
  1368. $article = $table->patchEntity($article, $data);
  1369. $table->save($article);
  1370. $this->assertNull($article->get('_i18n'));
  1371. $article = $table->get(1);
  1372. $this->assertEquals('New title', $article->get('title'));
  1373. $this->assertEquals('New body', $article->get('body'));
  1374. }
  1375. /**
  1376. * Tests that translations are added to the whitelist of associations to be
  1377. * saved
  1378. *
  1379. * @return void
  1380. */
  1381. public function testSaveTranslationDefaultLocale()
  1382. {
  1383. $table = $this->getTableLocator()->get('Articles');
  1384. $table->hasMany('Comments');
  1385. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  1386. $article = $table->get(1);
  1387. $data = [
  1388. 'title' => 'New title',
  1389. 'body' => 'New body',
  1390. '_translations' => [
  1391. 'es' => [
  1392. 'title' => 'ES title',
  1393. 'body' => 'ES body'
  1394. ]
  1395. ]
  1396. ];
  1397. $article = $table->patchEntity($article, $data);
  1398. $table->save($article);
  1399. $this->assertNull($article->get('_i18n'));
  1400. $article = $table->find('translations')->where(['id' => 1])->first();
  1401. $this->assertEquals('New title', $article->get('title'));
  1402. $this->assertEquals('New body', $article->get('body'));
  1403. $this->assertEquals('ES title', $article->_translations['es']->title);
  1404. $this->assertEquals('ES body', $article->_translations['es']->body);
  1405. }
  1406. /**
  1407. * Test that no properties are enabled when the translations
  1408. * option is off.
  1409. *
  1410. * @return void
  1411. */
  1412. public function testBuildMarshalMapTranslationsOff()
  1413. {
  1414. $table = $this->getTableLocator()->get('Articles');
  1415. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  1416. $marshaller = $table->marshaller();
  1417. $translate = $table->behaviors()->get('Translate');
  1418. $result = $translate->buildMarshalMap($marshaller, [], ['translations' => false]);
  1419. $this->assertSame([], $result);
  1420. }
  1421. /**
  1422. * Test building a marshal map with translations on.
  1423. *
  1424. * @return void
  1425. */
  1426. public function testBuildMarshalMapTranslationsOn()
  1427. {
  1428. $table = $this->getTableLocator()->get('Articles');
  1429. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  1430. $marshaller = $table->marshaller();
  1431. $translate = $table->behaviors()->get('Translate');
  1432. $result = $translate->buildMarshalMap($marshaller, [], ['translations' => true]);
  1433. $this->assertArrayHasKey('_translations', $result);
  1434. $this->assertInstanceOf('Closure', $result['_translations']);
  1435. $result = $translate->buildMarshalMap($marshaller, [], []);
  1436. $this->assertArrayHasKey('_translations', $result);
  1437. $this->assertInstanceOf('Closure', $result['_translations']);
  1438. }
  1439. /**
  1440. * Test marshalling non-array data
  1441. *
  1442. * @return void
  1443. */
  1444. public function testBuildMarshalMapNonArrayData()
  1445. {
  1446. $table = $this->getTableLocator()->get('Articles');
  1447. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  1448. $translate = $table->behaviors()->get('Translate');
  1449. $map = $translate->buildMarshalMap($table->marshaller(), [], []);
  1450. $entity = $table->newEntity();
  1451. $result = $map['_translations']('garbage', $entity);
  1452. $this->assertNull($result, 'Non-array should not error out.');
  1453. $this->assertEmpty($entity->getErrors());
  1454. $this->assertEmpty($entity->get('_translations'));
  1455. }
  1456. /**
  1457. * Test buildMarshalMap() builds new entities.
  1458. *
  1459. * @return void
  1460. */
  1461. public function testBuildMarshalMapBuildEntities()
  1462. {
  1463. $table = $this->getTableLocator()->get('Articles');
  1464. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  1465. $translate = $table->behaviors()->get('Translate');
  1466. $map = $translate->buildMarshalMap($table->marshaller(), [], []);
  1467. $entity = $table->newEntity();
  1468. $data = [
  1469. 'en' => [
  1470. 'title' => 'English Title',
  1471. 'body' => 'English Content'
  1472. ],
  1473. 'es' => [
  1474. 'title' => 'Titulo Español',
  1475. 'body' => 'Contenido Español'
  1476. ]
  1477. ];
  1478. $result = $map['_translations']($data, $entity);
  1479. $this->assertEmpty($entity->getErrors(), 'No validation errors.');
  1480. $this->assertCount(2, $result);
  1481. $this->assertArrayHasKey('en', $result);
  1482. $this->assertArrayHasKey('es', $result);
  1483. $this->assertEquals('English Title', $result['en']->title);
  1484. $this->assertEquals('Titulo Español', $result['es']->title);
  1485. }
  1486. /**
  1487. * Test that validation errors are added to the original entity.
  1488. *
  1489. * @return void
  1490. */
  1491. public function testBuildMarshalMapBuildEntitiesValidationErrors()
  1492. {
  1493. $table = $this->getTableLocator()->get('Articles');
  1494. $table->addBehavior('Translate', [
  1495. 'fields' => ['title', 'body'],
  1496. 'validator' => 'custom'
  1497. ]);
  1498. $validator = (new Validator)->add('title', 'notBlank', ['rule' => 'notBlank']);
  1499. $table->setValidator('custom', $validator);
  1500. $translate = $table->behaviors()->get('Translate');
  1501. $entity = $table->newEntity();
  1502. $map = $translate->buildMarshalMap($table->marshaller(), [], []);
  1503. $data = [
  1504. 'en' => [
  1505. 'title' => 'English Title',
  1506. 'body' => 'English Content'
  1507. ],
  1508. 'es' => [
  1509. 'title' => '',
  1510. 'body' => 'Contenido Español'
  1511. ]
  1512. ];
  1513. $result = $map['_translations']($data, $entity);
  1514. $this->assertNotEmpty($entity->getErrors(), 'Needs validation errors.');
  1515. $expected = [
  1516. 'title' => [
  1517. '_empty' => 'This field cannot be left empty'
  1518. ]
  1519. ];
  1520. $this->assertEquals($expected, $entity->getError('es'));
  1521. $this->assertEquals('English Title', $result['en']->title);
  1522. $this->assertEquals('', $result['es']->title);
  1523. }
  1524. /**
  1525. * Test that marshalling updates existing translation entities.
  1526. *
  1527. * @return void
  1528. */
  1529. public function testBuildMarshalMapUpdateExistingEntities()
  1530. {
  1531. $table = $this->getTableLocator()->get('Articles');
  1532. $table->addBehavior('Translate', [
  1533. 'fields' => ['title', 'body'],
  1534. ]);
  1535. $translate = $table->behaviors()->get('Translate');
  1536. $entity = $table->newEntity();
  1537. $es = $table->newEntity(['title' => 'Old title', 'body' => 'Old body']);
  1538. $en = $table->newEntity(['title' => 'Old title', 'body' => 'Old body']);
  1539. $entity->set('_translations', [
  1540. 'es' => $es,
  1541. 'en' => $en,
  1542. ]);
  1543. $map = $translate->buildMarshalMap($table->marshaller(), [], []);
  1544. $data = [
  1545. 'en' => [
  1546. 'title' => 'English Title',
  1547. ],
  1548. 'es' => [
  1549. 'title' => 'Spanish Title',
  1550. ]
  1551. ];
  1552. $result = $map['_translations']($data, $entity);
  1553. $this->assertEmpty($entity->getErrors(), 'No validation errors.');
  1554. $this->assertSame($en, $result['en']);
  1555. $this->assertSame($es, $result['es']);
  1556. $this->assertSame($en, $entity->get('_translations')['en']);
  1557. $this->assertSame($es, $entity->get('_translations')['es']);
  1558. $this->assertEquals('English Title', $result['en']->title);
  1559. $this->assertEquals('Spanish Title', $result['es']->title);
  1560. $this->assertEquals('Old body', $result['en']->body);
  1561. $this->assertEquals('Old body', $result['es']->body);
  1562. }
  1563. /**
  1564. * Test that updating translation records works with validations.
  1565. *
  1566. * @return void
  1567. */
  1568. public function testBuildMarshalMapUpdateEntitiesValidationErrors()
  1569. {
  1570. $table = $this->getTableLocator()->get('Articles');
  1571. $table->addBehavior('Translate', [
  1572. 'fields' => ['title', 'body'],
  1573. 'validator' => 'custom'
  1574. ]);
  1575. $validator = (new Validator)->add('title', 'notBlank', ['rule' => 'notBlank']);
  1576. $table->setValidator('custom', $validator);
  1577. $translate = $table->behaviors()->get('Translate');
  1578. $entity = $table->newEntity();
  1579. $es = $table->newEntity(['title' => 'Old title', 'body' => 'Old body']);
  1580. $en = $table->newEntity(['title' => 'Old title', 'body' => 'Old body']);
  1581. $entity->set('_translations', [
  1582. 'es' => $es,
  1583. 'en' => $en,
  1584. ]);
  1585. $map = $translate->buildMarshalMap($table->marshaller(), [], []);
  1586. $data = [
  1587. 'en' => [
  1588. 'title' => 'English Title',
  1589. 'body' => 'English Content'
  1590. ],
  1591. 'es' => [
  1592. 'title' => '',
  1593. 'body' => 'Contenido Español'
  1594. ]
  1595. ];
  1596. $result = $map['_translations']($data, $entity);
  1597. $this->assertNotEmpty($entity->getErrors(), 'Needs validation errors.');
  1598. $expected = [
  1599. 'title' => [
  1600. '_empty' => 'This field cannot be left empty'
  1601. ]
  1602. ];
  1603. $this->assertEquals($expected, $entity->getError('es'));
  1604. }
  1605. /**
  1606. * Test that the behavior uses associations' locator.
  1607. *
  1608. * @return void
  1609. */
  1610. public function testDefaultTableLocator()
  1611. {
  1612. $locator = new TableLocator();
  1613. $table = $locator->get('Articles');
  1614. $table->addBehavior('Translate', [
  1615. 'fields' => ['title', 'body'],
  1616. 'validator' => 'custom'
  1617. ]);
  1618. $behaviorLocator = $table->behaviors()->get('Translate')->getTableLocator();
  1619. $this->assertSame($locator, $behaviorLocator);
  1620. $this->assertSame($table->associations()->getTableLocator(), $behaviorLocator);
  1621. $this->assertNotSame($this->getTableLocator(), $behaviorLocator);
  1622. }
  1623. /**
  1624. * Test that the behavior uses a custom locator.
  1625. *
  1626. * @return void
  1627. */
  1628. public function testCustomTableLocator()
  1629. {
  1630. $locator = new TableLocator();
  1631. $table = $this->getTableLocator()->get('Articles');
  1632. $table->addBehavior('Translate', [
  1633. 'fields' => ['title', 'body'],
  1634. 'validator' => 'custom',
  1635. 'tableLocator' => $locator,
  1636. ]);
  1637. $behaviorLocator = $table->behaviors()->get('Translate')->getTableLocator();
  1638. $this->assertSame($locator, $behaviorLocator);
  1639. $this->assertNotSame($table->associations()->getTableLocator(), $behaviorLocator);
  1640. $this->assertNotSame($this->getTableLocator(), $behaviorLocator);
  1641. }
  1642. /**
  1643. * Tests that using matching doesn't cause an association property to be created.
  1644. *
  1645. * @return void
  1646. */
  1647. public function testMatchingDoesNotCreateAssociationProperty()
  1648. {
  1649. $table = $this->getTableLocator()->get('Articles');
  1650. $table->hasMany('Comments');
  1651. $table->Comments->addBehavior('Translate');
  1652. $table->Comments->setLocale('abc');
  1653. $this->assertNotEquals($table->Comments->getLocale(), I18n::getLocale());
  1654. $result = $table
  1655. ->find()
  1656. ->matching('Comments')
  1657. ->first();
  1658. $this->assertArrayNotHasKey('comments', $result->toArray());
  1659. }
  1660. /**
  1661. * Tests that using deep matching doesn't cause an association property to be created.
  1662. *
  1663. * @return void
  1664. */
  1665. public function testDeepMatchingDoesNotCreateAssociationProperty()
  1666. {
  1667. $table = $this->getTableLocator()->get('Articles');
  1668. $table->hasMany('Comments');
  1669. $table->Comments->belongsTo('Authors')->setForeignKey('user_id');
  1670. $table->Comments->addBehavior('Translate');
  1671. $table->Comments->setLocale('abc');
  1672. $table->Comments->Authors->addBehavior('Translate');
  1673. $table->Comments->Authors->setLocale('xyz');
  1674. $this->assertNotEquals($table->Comments->getLocale(), I18n::getLocale());
  1675. $this->assertNotEquals($table->Comments->Authors->getLocale(), I18n::getLocale());
  1676. $result = $table
  1677. ->find()
  1678. ->contain('Comments')
  1679. ->matching('Comments.Authors')
  1680. ->first();
  1681. $this->assertArrayNotHasKey('author', $result->comments);
  1682. }
  1683. /**
  1684. * Tests that using contained matching doesn't cause an association property to be created.
  1685. *
  1686. * @return void
  1687. */
  1688. public function testContainedMatchingDoesNotCreateAssociationProperty()
  1689. {
  1690. $table = $this->getTableLocator()->get('Authors');
  1691. $table->hasMany('Comments')->setForeignKey('user_id');
  1692. $table->Comments->belongsTo('Articles');
  1693. $table->Comments->Articles->addBehavior('Translate');
  1694. $table->Comments->Articles->setLocale('xyz');
  1695. $this->assertNotEquals($table->Comments->Articles->getLocale(), I18n::getLocale());
  1696. $result = $table
  1697. ->find()
  1698. ->contain([
  1699. 'Comments' => function ($query) {
  1700. return $query->matching('Articles');
  1701. }
  1702. ])
  1703. ->first();
  1704. $this->assertArrayNotHasKey('article', $result->comments[0]->toArray());
  1705. }
  1706. /**
  1707. * Tests that the _locale property is set on the entity in the _matchingData property.
  1708. *
  1709. * @return void
  1710. */
  1711. public function testLocalePropertyIsSetInMatchingData()
  1712. {
  1713. $table = $this->getTableLocator()->get('Articles');
  1714. $table->hasMany('Comments');
  1715. $table->Comments->addBehavior('Translate');
  1716. $table->Comments->setLocale('abc');
  1717. $this->assertNotEquals($table->Comments->getLocale(), I18n::getLocale());
  1718. $result = $table
  1719. ->find()
  1720. ->contain('Comments')
  1721. ->matching('Comments')
  1722. ->first();
  1723. $this->assertArrayNotHasKey('_locale', $result->comments);
  1724. $this->assertEquals('abc', $result->_matchingData['Comments']->_locale);
  1725. }
  1726. /**
  1727. * Tests that the _locale property is set on the entity in the _matchingData property
  1728. * when using deep matching.
  1729. *
  1730. * @return void
  1731. */
  1732. public function testLocalePropertyIsSetInMatchingDataWhenUsingDeepMatching()
  1733. {
  1734. $table = $this->getTableLocator()->get('Articles');
  1735. $table->hasMany('Comments');
  1736. $table->Comments->belongsTo('Authors')->setForeignKey('user_id');
  1737. $table->Comments->addBehavior('Translate');
  1738. $table->Comments->setLocale('abc');
  1739. $table->Comments->Authors->addBehavior('Translate');
  1740. $table->Comments->Authors->setLocale('xyz');
  1741. $this->assertNotEquals($table->Comments->getLocale(), I18n::getLocale());
  1742. $this->assertNotEquals($table->Comments->Authors->getLocale(), I18n::getLocale());
  1743. $result = $table
  1744. ->find()
  1745. ->contain('Comments.Authors')
  1746. ->matching('Comments.Authors')
  1747. ->first();
  1748. $this->assertArrayNotHasKey('_locale', $result->comments);
  1749. $this->assertEquals('abc', $result->_matchingData['Comments']->_locale);
  1750. $this->assertEquals('xyz', $result->_matchingData['Authors']->_locale);
  1751. }
  1752. /**
  1753. * Tests that the _locale property is set on the entity in the _matchingData property
  1754. * when using contained matching.
  1755. *
  1756. * @return void
  1757. */
  1758. public function testLocalePropertyIsSetInMatchingDataWhenUsingContainedMatching()
  1759. {
  1760. $table = $this->getTableLocator()->get('Authors');
  1761. $table->hasMany('Articles');
  1762. $table->Articles->belongsToMany('Tags');
  1763. $table->Articles->addBehavior('Translate');
  1764. $table->Articles->setLocale('abc');
  1765. $table->Articles->Tags->addBehavior('Translate');
  1766. $table->Articles->Tags->setLocale('xyz');
  1767. $this->assertNotEquals($table->Articles->getLocale(), I18n::getLocale());
  1768. $this->assertNotEquals($table->Articles->Tags->getLocale(), I18n::getLocale());
  1769. $result = $table
  1770. ->find()
  1771. ->contain([
  1772. 'Articles' => function ($query) {
  1773. return $query->matching('Tags');
  1774. },
  1775. 'Articles.Tags'
  1776. ])
  1777. ->first();
  1778. $this->assertArrayNotHasKey('_locale', $result->articles);
  1779. $this->assertArrayNotHasKey('_locale', $result->articles[0]->tags);
  1780. $this->assertEquals('abc', $result->articles[0]->_locale);
  1781. $this->assertEquals('xyz', $result->articles[0]->_matchingData['Tags']->_locale);
  1782. }
  1783. }