You are here

class StringDatabaseStorage in Localization update 7.2

Defines the locale string class.

This is the base class for SourceString and TranslationString.

Hierarchy

Expanded class hierarchy of StringDatabaseStorage

File

includes/locale/StringDatabaseStorage.php, line 13
Definition of StringDatabaseStorage.

View source
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(\StringInterface $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(StringInterface $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(\StringInterface $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(array $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(array $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',
      'l10n_status',
    ))) {
      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(StringInterface $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(StringInterface $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']);
    }

    // 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', 'plid');
        $query
          ->addField('t', 'plural');
        $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;
  }

  /**
   * Creates 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(StringInterface $string) {
    if ($string
      ->isSource()) {
      $string
        ->setValues(array(
        'context' => '',
        'version' => 'none',
      ), FALSE);
      $fields = $string
        ->getValues(array(
        'source',
        'context',
        'version',
        'textgroup',
      ));

      // @todo Add support for D7 fields 'location' and 'textgroup'.
    }
    elseif ($string
      ->isTranslation()) {
      $string
        ->setValues(array(
        'customized' => 0,
      ), FALSE);
      $fields = $string
        ->getValues(array(
        'lid',
        'language',
        'translation',
        'plid',
        'plural',
        '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(StringInterface $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, array $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);
  }

}

Members

Namesort descending Modifiers Type Description Overrides
StringDatabaseStorage::$options protected property Additional database connection options to use in queries.
StringDatabaseStorage::checkVersion protected function Checks whether the string version matches a given version, fix it if not.
StringDatabaseStorage::countStrings public function Implements StringStorageInterface::countStrings(). Overrides StringStorageInterface::countStrings
StringDatabaseStorage::countTranslations public function Implements StringStorageInterface::countTranslations(). Overrides StringStorageInterface::countTranslations
StringDatabaseStorage::createString public function Implements StringStorageInterface::createString(). Overrides StringStorageInterface::createString
StringDatabaseStorage::createTranslation public function Implements StringStorageInterface::createTranslation(). Overrides StringStorageInterface::createTranslation
StringDatabaseStorage::dbDelete protected function Creates delete query.
StringDatabaseStorage::dbExecute protected function Executes an arbitrary SELECT query string.
StringDatabaseStorage::dbFieldTable protected function Gets table alias for field.
StringDatabaseStorage::dbStringInsert protected function Creates a database record for a string object.
StringDatabaseStorage::dbStringKeys protected function Gets keys values that are in a database table.
StringDatabaseStorage::dbStringLoad protected function Loads multiple string objects.
StringDatabaseStorage::dbStringSelect protected function Builds a SELECT query with multiple conditions and fields.
StringDatabaseStorage::dbStringTable protected function Gets table name for storing string object.
StringDatabaseStorage::dbStringUpdate protected function Updates string object in the database.
StringDatabaseStorage::delete public function Implements StringStorageInterface::delete(). Overrides StringStorageInterface::delete
StringDatabaseStorage::deleteStrings public function Implements StringStorageInterface::deleteLanguage(). Overrides StringStorageInterface::deleteStrings
StringDatabaseStorage::deleteTranslations public function Implements StringStorageInterface::deleteLanguage(). Overrides StringStorageInterface::deleteTranslations
StringDatabaseStorage::findString public function Implements StringStorageInterface::findString(). Overrides StringStorageInterface::findString
StringDatabaseStorage::findTranslation public function Implements StringStorageInterface::findTranslation(). Overrides StringStorageInterface::findTranslation
StringDatabaseStorage::getStrings public function Implements StringStorageInterface::getStrings(). Overrides StringStorageInterface::getStrings
StringDatabaseStorage::getTranslations public function Implements StringStorageInterface::getTranslations(). Overrides StringStorageInterface::getTranslations
StringDatabaseStorage::save public function Implements StringStorageInterface::save(). Overrides StringStorageInterface::save
StringDatabaseStorage::__construct public function Constructs a new StringStorage controller.