Extra Table - nevadskiy/laravel-translatable GitHub Wiki

With this strategy, the translatable model table does not physically contain fields for translatable attributes. They are stored separately in a special table for translations. However, the model can work with them as it would with regular model attributes.

The following picture shows the database schema of this strategy. Columns that contain actual translations are highlighted in blue.

Database structure

Set up

Let's make, for example, a translatable Book model that has 2 translatable attributes: title and description.

The model class may look like this:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Nevadskiy\Translatable\Strategies\ExtraTable\HasTranslations;

class Book extends Model
{
    use HasTranslations; 

    protected $translatable = [
        'title', 
        'description',
    ];
}

And we need 2 tables: books and book_translations.

The simplest books table migration might look like this:

Schema::create('books', function (Blueprint $table) {
    $table->id();
    $table->timestamps();
});

The book_translations table should contain the title and description fields:

Schema::create('book_translations', function (Blueprint $table) {
    $table->id();
    $table->foreignId('book_id')->references('id')->on('books');
    $table->string('locale', 2);
    $table->string('title');
    $table->text('description');
    $table->timestamps();

    $table->unique(['book_id', 'locale']);
});

That's all. The model is now ready to work with translations.

Configuration

Custom foreign key

By default, the foreign key is determined automatically by the model name. For example, if the model is named Book, the strategy expects the foreign key to be book_id.

If you want to use another foreign key, you can do so by overriding the getEntityTranslationForeignKey method in the model as follows:

<?php

namespace App;

use App\BookTranslation;
use Illuminate\Database\Eloquent\Model;
use Nevadskiy\Translatable\Strategies\ExtraTable\HasTranslations;

class Book extends Model
{
    use HasTranslations;

    public function getEntityTranslationForeignKey(): string
    {
        return 'entity_id';
    }
}

Custom table name

The name of the table for translations is also determined from the model name. For the Book model, the strategy expects the name book_translations for the table for translations.

To use a custom table name, you must override the getEntityTranslationTable method in the model as follows:

<?php

namespace App;

use App\BookTranslation;
use Illuminate\Database\Eloquent\Model;
use Nevadskiy\Translatable\Strategies\ExtraTable\HasTranslations;

class Book extends Model
{
    use HasTranslations;

    protected function getEntityTranslationTable(): string
    {
        return 'book_entity_translations';
    }
}

Custom translation model

By default, you do not need to create a separate model to work with the translation table. The strategy uses one Translation model internally and dynamically sets the name of the table into it.

But if for some reason you need to create a custom translation model, then you can specify it by overriding the getTranslationModelClass method:

<?php

namespace App;

use App\BookTranslation;
use Illuminate\Database\Eloquent\Model;
use Nevadskiy\Translatable\Strategies\ExtraTable\HasTranslations;

class Book extends Model
{
    use HasTranslations;

    public function getTranslationModelClass() : string
    {
        return BookTranslation::class;
    }
}

Note that all casts, accessors, mutators and everything related to translatable attributes must only be declared once in the original entity model, not in the translation model.

Custom global translation model

You can also specify one global model for the extra table strategy globally in the App\Providers\AppServiceProvider class:

use Nevadskiy\Translatable\Strategies\ExtraTable\ExtraTableStrategy;
use App\Translation;

public function boot()
{
    ExtraTableStrategy::useModel(Translation::class);
}

For this model, you do not need to specify a concrete table name, because the strategy will dynamically substitute its table name for each translated model on the fly.