選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

168 行
6.1KB

  1. <?php
  2. /**
  3. * @link http://www.yiiframework.com/
  4. * @copyright Copyright (c) 2008 Yii Software LLC
  5. * @license http://www.yiiframework.com/license/
  6. */
  7. namespace yii\i18n;
  8. use Yii;
  9. use yii\base\InvalidConfigException;
  10. use yii\di\Instance;
  11. use yii\helpers\ArrayHelper;
  12. use yii\caching\Cache;
  13. use yii\db\Connection;
  14. use yii\db\Query;
  15. /**
  16. * DbMessageSource extends [[MessageSource]] and represents a message source that stores translated
  17. * messages in database.
  18. *
  19. * The database must contain the following two tables:
  20. *
  21. * ~~~
  22. * CREATE TABLE source_message (
  23. * id INTEGER PRIMARY KEY AUTO_INCREMENT,
  24. * category VARCHAR(32),
  25. * message TEXT
  26. * );
  27. *
  28. * CREATE TABLE message (
  29. * id INTEGER,
  30. * language VARCHAR(16),
  31. * translation TEXT,
  32. * PRIMARY KEY (id, language),
  33. * CONSTRAINT fk_message_source_message FOREIGN KEY (id)
  34. * REFERENCES source_message (id) ON DELETE CASCADE ON UPDATE RESTRICT
  35. * );
  36. * ~~~
  37. *
  38. * The `source_message` table stores the messages to be translated, and the `message` table stores
  39. * the translated messages. The name of these two tables can be customized by setting [[sourceMessageTable]]
  40. * and [[messageTable]], respectively.
  41. *
  42. * @author resurtm <resurtm@gmail.com>
  43. * @since 2.0
  44. */
  45. class DbMessageSource extends MessageSource
  46. {
  47. /**
  48. * Prefix which would be used when generating cache key.
  49. */
  50. const CACHE_KEY_PREFIX = 'DbMessageSource';
  51. /**
  52. * @var Connection|array|string the DB connection object or the application component ID of the DB connection.
  53. * After the DbMessageSource object is created, if you want to change this property, you should only assign
  54. * it with a DB connection object.
  55. * Starting from version 2.0.2, this can also be a configuration array for creating the object.
  56. */
  57. public $db = 'db';
  58. /**
  59. * @var Cache|array|string the cache object or the application component ID of the cache object.
  60. * The messages data will be cached using this cache object. Note, this property has meaning only
  61. * in case [[cachingDuration]] set to non-zero value.
  62. * After the DbMessageSource object is created, if you want to change this property, you should only assign
  63. * it with a cache object.
  64. * Starting from version 2.0.2, this can also be a configuration array for creating the object.
  65. */
  66. public $cache = 'cache';
  67. /**
  68. * @var string the name of the source message table.
  69. */
  70. public $sourceMessageTable = '{{%source_message}}';
  71. /**
  72. * @var string the name of the translated message table.
  73. */
  74. public $messageTable = '{{%message}}';
  75. /**
  76. * @var integer the time in seconds that the messages can remain valid in cache.
  77. * Use 0 to indicate that the cached data will never expire.
  78. * @see enableCaching
  79. */
  80. public $cachingDuration = 0;
  81. /**
  82. * @var boolean whether to enable caching translated messages
  83. */
  84. public $enableCaching = false;
  85. /**
  86. * Initializes the DbMessageSource component.
  87. * This method will initialize the [[db]] property to make sure it refers to a valid DB connection.
  88. * Configured [[cache]] component would also be initialized.
  89. * @throws InvalidConfigException if [[db]] is invalid or [[cache]] is invalid.
  90. */
  91. public function init()
  92. {
  93. parent::init();
  94. $this->db = Instance::ensure($this->db, Connection::className());
  95. if ($this->enableCaching) {
  96. $this->cache = Instance::ensure($this->cache, Cache::className());
  97. }
  98. }
  99. /**
  100. * Loads the message translation for the specified language and category.
  101. * If translation for specific locale code such as `en-US` isn't found it
  102. * tries more generic `en`.
  103. *
  104. * @param string $category the message category
  105. * @param string $language the target language
  106. * @return array the loaded messages. The keys are original messages, and the values
  107. * are translated messages.
  108. */
  109. protected function loadMessages($category, $language)
  110. {
  111. if ($this->enableCaching) {
  112. $key = [
  113. __CLASS__,
  114. $category,
  115. $language,
  116. ];
  117. $messages = $this->cache->get($key);
  118. if ($messages === false) {
  119. $messages = $this->loadMessagesFromDb($category, $language);
  120. $this->cache->set($key, $messages, $this->cachingDuration);
  121. }
  122. return $messages;
  123. } else {
  124. return $this->loadMessagesFromDb($category, $language);
  125. }
  126. }
  127. /**
  128. * Loads the messages from database.
  129. * You may override this method to customize the message storage in the database.
  130. * @param string $category the message category.
  131. * @param string $language the target language.
  132. * @return array the messages loaded from database.
  133. */
  134. protected function loadMessagesFromDb($category, $language)
  135. {
  136. $mainQuery = new Query();
  137. $mainQuery->select(['t1.message message', 't2.translation translation'])
  138. ->from(["$this->sourceMessageTable t1", "$this->messageTable t2"])
  139. ->where('t1.id = t2.id AND t1.category = :category AND t2.language = :language')
  140. ->params([':category' => $category, ':language' => $language]);
  141. $fallbackLanguage = substr($language, 0, 2);
  142. if ($fallbackLanguage != $language) {
  143. $fallbackQuery = new Query();
  144. $fallbackQuery->select(['t1.message message', 't2.translation translation'])
  145. ->from(["$this->sourceMessageTable t1", "$this->messageTable t2"])
  146. ->where('t1.id = t2.id AND t1.category = :category AND t2.language = :fallbackLanguage')
  147. ->andWhere("t2.id NOT IN (SELECT id FROM $this->messageTable WHERE language = :language)")
  148. ->params([':category' => $category, ':language' => $language, ':fallbackLanguage' => $fallbackLanguage]);
  149. $mainQuery->union($fallbackQuery, true);
  150. }
  151. $messages = $mainQuery->createCommand($this->db)->queryAll();
  152. return ArrayHelper::map($messages, 'message', 'translation');
  153. }
  154. }