123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518 |
- <?php
- /**
- * @file
- * Definition of StringDatabaseStorage.
- */
- /**
- * Defines the locale string class.
- *
- * This is the base class for SourceString and TranslationString.
- */
- class StringDatabaseStorage implements StringStorageInterface {
- /**
- * Additional database connection options to use in queries.
- *
- * @var array
- */
- protected $options = array();
- /**
- * Constructs a new StringStorage controller.
- *
- * @param array $options
- * (optional) Any additional database connection options to use in queries.
- */
- public function __construct(array $options = array()) {
- $this->options = $options;
- }
- /**
- * Implements StringStorageInterface::getStrings().
- */
- public function getStrings(array $conditions = array(), array $options = array()) {
- return $this->dbStringLoad($conditions, $options, 'SourceString');
- }
- /**
- * Implements StringStorageInterface::getTranslations().
- */
- public function getTranslations(array $conditions = array(), array $options = array()) {
- return $this->dbStringLoad($conditions, array('translation' => TRUE) + $options, 'TranslationString');
- }
- /**
- * Implements StringStorageInterface::findString().
- */
- public function findString(array $conditions) {
- $values = $this->dbStringSelect($conditions)
- ->execute()
- ->fetchAssoc();
- if (!empty($values)) {
- $string = new SourceString($values);
- $string->setStorage($this);
- return $string;
- }
- }
- /**
- * Implements StringStorageInterface::findTranslation().
- */
- public function findTranslation(array $conditions) {
- $values = $this->dbStringSelect($conditions, array('translation' => TRUE))
- ->execute()
- ->fetchAssoc();
- if (!empty($values)) {
- $string = new TranslationString($values);
- $this->checkVersion($string, VERSION);
- $string->setStorage($this);
- return $string;
- }
- }
- /**
- * Implements StringStorageInterface::countStrings().
- */
- public function countStrings() {
- return $this->dbExecute("SELECT COUNT(*) FROM {locales_source}")->fetchField();
- }
- /**
- * Implements StringStorageInterface::countTranslations().
- */
- public function countTranslations() {
- return $this->dbExecute("SELECT t.language, COUNT(*) AS translated FROM {locales_source} s INNER JOIN {locales_target} t ON s.lid = t.lid GROUP BY t.language")->fetchAllKeyed();
- }
- /**
- * Implements StringStorageInterface::save().
- */
- public function save($string) {
- if ($string->isNew()) {
- $result = $this->dbStringInsert($string);
- if ($string->isSource() && $result) {
- // Only for source strings, we set the locale identifier.
- $string->setId($result);
- }
- $string->setStorage($this);
- }
- else {
- $this->dbStringUpdate($string);
- }
- return $this;
- }
- /**
- * Checks whether the string version matches a given version, fix it if not.
- *
- * @param StringInterface $string
- * The string object.
- * @param string $version
- * Drupal version to check against.
- */
- protected function checkVersion($string, $version) {
- if ($string->getId() && $string->getVersion() != $version) {
- $string->setVersion($version);
- db_update('locales_source', $this->options)
- ->condition('lid', $string->getId())
- ->fields(array('version' => $version))
- ->execute();
- }
- }
- /**
- * Implements StringStorageInterface::delete().
- */
- public function delete($string) {
- if ($keys = $this->dbStringKeys($string)) {
- $this->dbDelete('locales_target', $keys)->execute();
- if ($string->isSource()) {
- $this->dbDelete('locales_source', $keys)->execute();
- $this->dbDelete('locales_location', $keys)->execute();
- $string->setId(NULL);
- }
- }
- else {
- throw new StringStorageException(format_string('The string cannot be deleted because it lacks some key fields: @string', array(
- '@string' => $string->getString()
- )));
- }
- return $this;
- }
- /**
- * Implements StringStorageInterface::deleteLanguage().
- */
- public function deleteStrings($conditions) {
- $lids = $this->dbStringSelect($conditions, array('fields' => array('lid')))->execute()->fetchCol();
- if ($lids) {
- $this->dbDelete('locales_target', array('lid' => $lids))->execute();
- $this->dbDelete('locales_source', array('lid' => $lids))->execute();
- $this->dbDelete('locales_location', array('sid' => $lids))->execute();
- }
- }
- /**
- * Implements StringStorageInterface::deleteLanguage().
- */
- public function deleteTranslations($conditions) {
- $this->dbDelete('locales_target', $conditions)->execute();
- }
- /**
- * Implements StringStorageInterface::createString().
- */
- public function createString($values = array()) {
- return new SourceString($values + array('storage' => $this));
- }
- /**
- * Implements StringStorageInterface::createTranslation().
- */
- public function createTranslation($values = array()) {
- return new TranslationString($values + array(
- 'storage' => $this,
- 'is_new' => TRUE
- ));
- }
- /**
- * Gets table alias for field.
- *
- * @param string $field
- * Field name to find the table alias for.
- *
- * @return string
- * Either 's', 't' or 'l' depending on whether the field belongs to source,
- * target or location table table.
- */
- protected function dbFieldTable($field) {
- if (in_array($field, array('language', 'translation', 'customized'))) {
- return 't';
- }
- elseif (in_array($field, array('type', 'name'))) {
- return 'l';
- }
- else {
- return 's';
- }
- }
- /**
- * Gets table name for storing string object.
- *
- * @param StringInterface $string
- * The string object.
- *
- * @return string
- * The table name.
- */
- protected function dbStringTable($string) {
- if ($string->isSource()) {
- return 'locales_source';
- }
- elseif ($string->isTranslation()) {
- return 'locales_target';
- }
- }
- /**
- * Gets keys values that are in a database table.
- *
- * @param StringInterface $string
- * The string object.
- *
- * @return array
- * Array with key fields if the string has all keys, or empty array if not.
- */
- protected function dbStringKeys($string) {
- if ($string->isSource()) {
- $keys = array('lid');
- }
- elseif ($string->isTranslation()) {
- $keys = array('lid', 'language');
- }
- if (!empty($keys) && ($values = $string->getValues($keys)) && count($keys) == count($values)) {
- return $values;
- }
- else {
- return array();
- }
- }
- /**
- * Loads multiple string objects.
- *
- * @param array $conditions
- * Any of the conditions used by dbStringSelect().
- * @param array $options
- * Any of the options used by dbStringSelect().
- * @param string $class
- * Class name to use for fetching returned objects.
- *
- * @return array
- * Array of objects of the class requested.
- */
- protected function dbStringLoad(array $conditions, array $options, $class) {
- $strings = array();
- $result = $this->dbStringSelect($conditions, $options)->execute();
- foreach ($result as $item) {
- $string = new $class($item);
- $string->setStorage($this);
- $strings[] = $string;
- }
- return $strings;
- }
- /**
- * Builds a SELECT query with multiple conditions and fields.
- *
- * The query uses both 'locales_source' and 'locales_target' tables.
- * Note that by default, as we are selecting both translated and untranslated
- * strings target field's conditions will be modified to match NULL rows too.
- *
- * @param array $conditions
- * An associative array with field => value conditions that may include
- * NULL values. If a language condition is included it will be used for
- * joining the 'locales_target' table.
- * @param array $options
- * An associative array of additional options. It may contain any of the
- * options used by StringStorageInterface::getStrings() and these additional
- * ones:
- * - 'translation', Whether to include translation fields too. Defaults to
- * FALSE.
- * @return SelectQuery
- * Query object with all the tables, fields and conditions.
- */
- protected function dbStringSelect(array $conditions, array $options = array()) {
- // Change field 'customized' into 'l10n_status'. This enables the Drupal 8
- // backported code to work with the Drupal 7 style database tables.
- if (isset($conditions['customized'])) {
- $conditions['l10n_status'] = $conditions['customized'];
- unset($conditions['customized']);
- }
- if (isset($options['customized'])) {
- $options['l10n_status'] = $options['customized'];
- unset($options['customized']);
- }
- // Start building the query with source table and check whether we need to
- // join the target table too.
- $query = db_select('locales_source', 's', $this->options)
- ->fields('s');
- // Figure out how to join and translate some options into conditions.
- if (isset($conditions['translated'])) {
- // This is a meta-condition we need to translate into simple ones.
- if ($conditions['translated']) {
- // Select only translated strings.
- $join = 'innerJoin';
- }
- else {
- // Select only untranslated strings.
- $join = 'leftJoin';
- $conditions['translation'] = NULL;
- }
- unset($conditions['translated']);
- }
- else {
- $join = !empty($options['translation']) ? 'leftJoin' : FALSE;
- }
- if ($join) {
- if (isset($conditions['language'])) {
- // If we've got a language condition, we use it for the join.
- $query->$join('locales_target', 't', "t.lid = s.lid AND t.language = :langcode", array(
- ':langcode' => $conditions['language']
- ));
- unset($conditions['language']);
- }
- else {
- // Since we don't have a language, join with locale id only.
- $query->$join('locales_target', 't', "t.lid = s.lid");
- }
- if (!empty($options['translation'])) {
- // We cannot just add all fields because 'lid' may get null values.
- $query->addField('t', 'language');
- $query->addField('t', 'translation');
- $query->addField('t', 'l10n_status', 'customized');
- }
- }
- // If we have conditions for location's type or name, then we need the
- // location table, for which we add a subquery.
- if (isset($conditions['type']) || isset($conditions['name'])) {
- $subquery = db_select('locales_location', 'l', $this->options)
- ->fields('l', array('sid'));
- foreach (array('type', 'name') as $field) {
- if (isset($conditions[$field])) {
- $subquery->condition('l.' . $field, $conditions[$field]);
- unset($conditions[$field]);
- }
- }
- $query->condition('s.lid', $subquery, 'IN');
- }
- // Add conditions for both tables.
- foreach ($conditions as $field => $value) {
- $table_alias = $this->dbFieldTable($field);
- $field_alias = $table_alias . '.' . $field;
- if (is_null($value)) {
- $query->isNull($field_alias);
- }
- elseif ($table_alias == 't' && $join === 'leftJoin') {
- // Conditions for target fields when doing an outer join only make
- // sense if we add also OR field IS NULL.
- $query->condition(db_or()
- ->condition($field_alias, $value)
- ->isNull($field_alias)
- );
- }
- else {
- $query->condition($field_alias, $value);
- }
- }
- // Process other options, string filter, query limit, etc...
- if (!empty($options['filters'])) {
- if (count($options['filters']) > 1) {
- $filter = db_or();
- $query->condition($filter);
- }
- else {
- // If we have a single filter, just add it to the query.
- $filter = $query;
- }
- foreach ($options['filters'] as $field => $string) {
- $filter->condition($this->dbFieldTable($field) . '.' . $field, '%' . db_like($string) . '%', 'LIKE');
- }
- }
- if (!empty($options['pager limit'])) {
- $query = $query->extend('PagerDefault')->limit($options['pager limit']);
- }
- return $query;
- }
- /**
- * Createds a database record for a string object.
- *
- * @param StringInterface $string
- * The string object.
- *
- * @return bool|int
- * If the operation failed, returns FALSE.
- * If it succeeded returns the last insert ID of the query, if one exists.
- *
- * @throws StringStorageException
- * If the string is not suitable for this storage, an exception ithrown.
- */
- protected function dbStringInsert($string) {
- if ($string->isSource()) {
- $string->setValues(array('context' => '', 'version' => 'none'), FALSE);
- $fields = $string->getValues(array('source', 'context', 'version'));
- }
- elseif ($string->isTranslation()) {
- $string->setValues(array('customized' => 0), FALSE);
- $fields = $string->getValues(array('lid', 'language', 'translation', 'customized'));
- }
- if (!empty($fields)) {
- // Change field 'customized' into 'l10n_status'. This enables the Drupal 8
- // backported code to work with the Drupal 7 style database tables.
- if (isset($fields['customized'])) {
- $fields['l10n_status'] = $fields['customized'];
- unset($fields['customized']);
- }
- return db_insert($this->dbStringTable($string), $this->options)
- ->fields($fields)
- ->execute();
- }
- else {
- throw new StringStorageException(format_string('The string cannot be saved: @string', array(
- '@string' => $string->getString()
- )));
- }
- }
- /**
- * Updates string object in the database.
- *
- * @param StringInterface $string
- * The string object.
- *
- * @return bool|int
- * If the record update failed, returns FALSE. If it succeeded, returns
- * SAVED_NEW or SAVED_UPDATED.
- *
- * @throws StringStorageException
- * If the string is not suitable for this storage, an exception is thrown.
- */
- protected function dbStringUpdate($string) {
- if ($string->isSource()) {
- $values = $string->getValues(array('source', 'context', 'version'));
- }
- elseif ($string->isTranslation()) {
- $values = $string->getValues(array('translation', 'customized'));
- }
- if (!empty($values) && $keys = $this->dbStringKeys($string)) {
- // Change field 'customized' into 'l10n_status'. This enables the Drupal 8
- // backported code to work with the Drupal 7 style database tables.
- if (isset($keys['customized'])) {
- $keys['l10n_status'] = $keys['customized'];
- unset($keys['customized']);
- }
- if (isset($values['customized'])) {
- $values['l10n_status'] = $values['customized'];
- unset($values['customized']);
- }
- return db_merge($this->dbStringTable($string), $this->options)
- ->key($keys)
- ->fields($values)
- ->execute();
- }
- else {
- throw new StringStorageException(format_string('The string cannot be updated: @string', array(
- '@string' => $string->getString()
- )));
- }
- }
- /**
- * Creates delete query.
- *
- * @param string $table
- * The table name.
- * @param array $keys
- * Array with object keys indexed by field name.
- *
- * @return DeleteQuery
- * Returns a new DeleteQuery object for the active database.
- */
- protected function dbDelete($table, $keys) {
- $query = db_delete($table, $this->options);
- // Change field 'customized' into 'l10n_status'. This enables the Drupal 8
- // backported code to work with the Drupal 7 style database tables.
- if (isset($keys['customized'])) {
- $keys['l10n_status'] = $keys['customized'];
- unset($keys['customized']);
- }
- foreach ($keys as $field => $value) {
- $query->condition($field, $value);
- }
- return $query;
- }
- /**
- * Executes an arbitrary SELECT query string.
- */
- protected function dbExecute($query, array $args = array()) {
- return db_query($query, $args, $this->options);
- }
- }
|