class StringDatabaseStorage in Localization update 7.2
Defines the locale string class.
This is the base class for SourceString and TranslationString.
Hierarchy
- class \StringDatabaseStorage implements StringStorageInterface
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);
}
}