CakePHP действует как Translate и $Model::find()

Я прикрепил поведение Translate к одной из своих моделей, и у меня есть некоторые недостатки в этом:

1) Если я не сохраняю данные во всех полях, переданных в качестве параметров при присоединении поведения к модели, метод $Model::find() не получает вставленные строки.

public $actsAs = array(
    'Translate' => array(
        'title' => 'title_Translation',
        'description' => 'description_Translation',
        'description_long' => 'description_long_Translation'
    )
);

Пример: если я передам методу $Model::save() только значение для 'title', данные будут сохранены даже в таблице i18n, но $Model::find() ничего не получит. Я должен передать данные для всех полей.

Могу ли я заставить его получить эти записи?

2) Как я могу получить все записи в административной части приложения (независимо от языка, на котором запись сохранена), чтобы перечислить их, чтобы пользователь мог их изменить (редактировать данные, сохранить данные на нескольких языках)? Прямо сейчас я могу получить только те записи, которые соответствуют текущему языку (прочитаны из Configure или заданы явно).

Спасибо!


person Michael    schedule 27.09.2012    source источник
comment
Та же проблема здесь. Сгенерированный запрос выполняет внутреннее соединение с таблицей переводов для каждого переведенного поля, поэтому, если хотя бы одно из них отсутствует в таблице переводов, даже исходная запись не извлекается. Если вам нужно добавить новое переведенное поле в существующую модель, все готово.   -  person eleonzx    schedule 16.11.2012
comment
У меня была та же проблема, и я написал некоторый код, чтобы исправить это... в основном, после того, как вы добавите новое переводимое поле, вы выполняете этот код, чтобы пройти и добавить новые записи перевода в БД для всех существующих строк. См. здесь: stackoverflow.com/questions/13243785/   -  person joshua.paling    schedule 16.11.2012


Ответы (4)


Хорошо, я могу немного опоздать, но в любом случае...

1) Cake использует INNER JOIN при выборке строки и связанных с ней переводов, поэтому в основном нет простого способа обойти это. Вы должны убедиться, что сохраняете каждое переводимое поле каждый раз, даже если вы просто сохраняете его как пустое. Единственной альтернативой было бы взломать ядро, чтобы заставить его использовать левое соединение, а не внутреннее соединение, но не делайте этого.

2) В поваренной книге объясняется, как получить все записи здесь: http://book.cakephp.org/2.0/en/core-libraries/behaviors/translate.html#retrieve-all-translation-records-for-a-field

Теперь, вероятно, большую часть времени вы хотите получить только один перевод, поэтому вы не хотите изменять определение вашего массива $actsAs['Translate'] в вашей модели. Итак, что я сделал, так это настроил метод в AppModel.php, который модифицирует массив $actsAs['Translate'] на лету:

/*  
 * See http://book.cakephp.org/2.0/en/core-libraries/behaviors/translate.html#using-the-bindtranslation-method
 * This is for making it so we fetch all translations, as opposed to just that of the current locale.
 * Used for eg. editing (multiple translations) via the admin interface.
 */
public function bindAllTranslations(){
    $translatableFields = $this->actsAs['Translate'];

    $keyValueFields = array();
    foreach($translatableFields as $field){
        $keyValueFields[$field] = $field.'Translation';
    }

    $this->bindTranslation($keyValueFields,false);  // false means it will be changed for all future DB transactions in this page request - and won't be reset after the next transaction.
}

Итак, если это метод администратора (или любая другая ситуация, в которой вам нужны все переводы), вы вызываете этот код перед поиском:

$this->MyModel->bindAllTranslations();
$this->MyModel->find('all');

Надеюсь, это поможет!

person joshua.paling    schedule 16.11.2012

Я как бы решил это, я скопировал TranslateBehavior в app/Model/Behavior (просто чтобы избежать проблем при будущих обновлениях и сохранить исходный на всякий случай), затем я изменил метод _addJoin(...) поведения, просто изменил тип соединения от INNER к LEFT в строке 255 (я использую торт 2.2.3).

Теперь, если запись существует, она всегда извлекается, даже если переведенные поля отсутствуют.

Минусов не вижу, кроме необходимости проверять, не пусто ли поле перевода.

person eleonzx    schedule 16.11.2012

Не совсем уверен, поможет ли это в вашем случае, но вы также можете использовать массив для установки локали перед вызовом find()

$this->YourModel->locale = array("ENG", "GER", "JAP");

Таким образом, вы всегда будете получать все записи, даже если они не имеют всех возможных переводов.

person Zbigniew Ledwoń    schedule 04.05.2015

Большое спасибо, eleonzx, у меня эта проблема уже десять лет, и благодаря вашему простому ответу я теперь могу двигаться вперед! Так что еще раз спасибо.

И, возможно, этот код может помочь многим людям:

в моем методе AppController перед фильтром я вызываю _setLanguage

private function _setLanguage() {
    if($this->Session->read('Config.language')){
        $locale = $this->Session->read('Config.language');
        $this->{$this->modelClass}->setLocale($locale);
    }else{
        $this->{$this->modelClass}->Behaviors->disable('Translate');
    }
}

С условием else я отключаю Translate Behavior на лету, чтобы получить исходное содержимое, если в сеансе не задана локаль (я использую базовые ссылки для переключения между языками).

person Chris Carton    schedule 11.01.2017