Add a new translatable field to an existing transl

2019-05-31 08:28发布

问题:

I'm using CakePHP's translatable behavior. I have a few existing fields working fine, but I'm having trouble adding a new translatable field to my model.

CakePHP uses an INNER JOIN to fetch all translatable fields from the database.

Now, if I add an extra translatable field to my model, all the translation records for that field won't exist in the database. And because of the inner join, whenever it tries to fetch ANY existing records from the database, it will return blank - because the INNER JOIN on the new field fails, and so the entire query returns nothing.

Surely people must have come accross this situation before. Is there an easy solution?

One solution would be to edit/override the core and make all the INNER JOIN's into LEFT OUTER JOIN's. Is there anything wrong with that?

Another solution would be to run an update on the translations table to create all the extra records for the new field, every time you add a new translatable field - but I hate that solution.

Is there a better solution? How have others dealt with this problem?

Thanks in advance.

回答1:

OK, here's a way of making sure the records exist after each time you add a new translatable field. If you've got a better answer, add it, and I'll mark yours as correct.

PS - this is tested for my purposes. I'm using multiple translation tables (http://book.cakephp.org/2.0/en/core-libraries/behaviors/translate.html#multiple-translation-tables). I think it should work for most situations, but if not, it should at least be a good starting point.

In your model (the model that actsAs Translatable), add the following method. What it does is takes an array of locales, and then for every record in the table, and for every translatable field, and for every locale (ie, 3 loops), it checks that a translation record exists. If a translation doesn't exist, it adds a blank one, so at least the INNER JOIN won't fail.

It returns an array of all the records it added, so you can then go through and check them or change their content or whatever.

Here's the model method:

function ensureTranslationIntegrity($localesToCheck){
    $allRows = $this->find('all', array('fields' => array('id')));
    $fieldsToCheck = array();

    $translatableFields = $this->actsAs['Translate'];
    foreach($translatableFields as $key => $value){
        // actsAs Translatabe can take field names only, or Key => Value pairs - see http://book.cakephp.org/2.0/en/core-libraries/behaviors/translate.html#retrieve-all-translation-records-for-a-field
        if(is_numeric($key)){
            $field = $value;
        } else {
            $field = $key;
        }
        array_push($fieldsToCheck, $field);
    }

    $translateModel = $this->translateModel();
    $addedRows = array(); // This will contain all the rows we have to add

    foreach ($allRows as $row){
        foreach($fieldsToCheck as $field){
            foreach($localesToCheck as $locale){
                $conditions = array(
                    'model' => $this->name,
                    'foreign_key' => $row[$this->name]['id'],
                    'field' => $field,
                    'locale' => $locale
                );
                $translation = $translateModel->find('first',array('conditions' => $conditions));

                if(!$translation){
                    $data = $conditions; // The data we want to insert will mostly just match the conditions of the failed find
                    $data['content'] = ''; // add it as empty

                    $translateModel->create();
                    $translateModel->save($data);

                    array_push($addedRows, $data);
                }
            } // END foreach($localesToCheck as $locale){
        } // END foreach($fieldsToCheck as $field){
    } // END foreach ($allRows as $row){

    return $addedRows;
}

And in your controller, you'd call it something like this:

public function ensure_translation_integrity(){
    $locales = array('en_au','en_gb','en_nz','pt_br','xh_za');
    $addedRows = $this->YourModel->ensureTranslationIntegrity($locales);
    debug($addedRows);
}

Hope that helps someone, but like I said, I'd love to see a better solution if someone has one.