Crud: Translatable fields

Created on 23 Jul 2016  路  59Comments  路  Source: Laravel-Backpack/CRUD

What about translatable fields?
In my own project I use https://github.com/spatie/laravel-translatable and create new field with tabs for each language.

urgent

Most helpful comment

BOOM!

I'm happy to tell you we've _finally_ got a fully-working solution on the multilanguage branch :-)

Screenshots

screen shot 2016-12-23 at 15 13 28

screen shot 2016-12-23 at 14 50 59

Test drive

If anyone wants to give it a test-drive, here's how to do that. Please make sure you test drive in a separate branch. Some things might change before final release.

  1. Take a look at the pull request to see what's changed in the code.
  2. Use the "multilanguage" branch in you composer.json. It's similar to how you'd try the 3.2 branch.
  3. Install spatie/laravel-translatable
  4. Instead of using spatie's HasTranslations trait on your model, use Backpack's HasTranslations and define what fields are translatable on your model. For example:
namespace App\Models;

use Backpack\CRUD\CrudTrait;
use Illuminate\Database\Eloquent\Model;
use Backpack\CRUD\ModelTraits\SpatieTranslatable\HasTranslations;

class Product extends Model
{
    use CrudTrait;
    use HasTranslations;

     /*
    |--------------------------------------------------------------------------
    | GLOBAL VARIABLES
    |--------------------------------------------------------------------------
    */

    protected $table = 'products';
    protected $primaryKey = 'id';
    protected $fillable = ['name', 'category_id', 'options', 'price', 'tags'];
    protected $translatable = ['name', 'options'];
  1. Make the database columns you want to be translatable "text" or "json". You can use migrations or do it directly in the db, whatever floats your boat.
  2. The CRUD config file now has a list of all locales in the world. Uncomment the ones you want.
  3. You'll notice that:
  4. the "Edit" button now has a dropdown with your languages
  5. the edit form will have a dropdown with your languages
  6. the create form will NOT have a dropdown with your languages (entries are created in the base language 99% of the time, then translated)
  7. the translatable fields have a flag next to them

I've tried it with all reasonable fields, columns, features and use cases - it _looks to me_ like it works as intended. But it's a big important feature, so please tell me if you find anything I didn't.

Thanks, cheers!

Notes & Questions

A. Currently it only supports the way spatie/laravel-translatable does translations, but it's done in such a manner that adding dimsav/laravel-translatable will be a non-breaking change. I call them adapters. Switching between one way and the other should be as easy as changing a trait in your model.

B. You'll probably get errors if you already had entries in that table, because the translatable columns don't have JSONs in them. So it's best to start with a fresh table.

C. When you go to edit a French translation, you'll notice ?locale=fr in the address bar. There's also a hidden input inside the edit form, with the name "locale", that uses that value. Do you think it's too common and it might conflict with anyone's app? Why would you call an attribute or db column "locale" if it's not the locale?

All 59 comments

Yup, that's exactly the plan :-) And that's exactly the package I was planning on using. Also, I plan on the Edit button having a dropdown with all the languages so you can go directly to it.

Haven't had time to do the multilanguage functionality yet, but I will soon, 'cause it's an important feature.

Oh, sorry. In fact, I haven't made up my mind yet wether to use:

The important difference being if it's better to store the translations in a separate table, or in a JSON field in the same table.

What's your opinion about this?

I liked the idea with JSON fields, MySQL already have this feature. We need less time for configuration (no need to create additional models, migrations) and the package from dimsav there are bugs that are already for a long time are not fixed.

So i finalize my own translatable fields and it looks like:
screenshot from 2016-07-23 17-13-28

I like it! Let's make some improvements on it for the general implementation, from a UX standpoint.

I've had this problem in the past with clients translating websites and their approach to translation is usually different. In your interface the best way to translate is:

  • take every db column and translate it to all languages

But their approach was closer to:

  • take every database row and translate it to a specific language

This might totally work for your project (I don't know anything about it), but I think for the general implementation we should do something close to this:
0c105908-50f9-11e6-840c-90130538794a

Which (I believe) has a few benefits:

  • UI complexity reduction (translating will be less frightening);
  • shorter forms (tabs are only show once);
  • preserves a standard web form functionality (write something, hit TAB and move to the next input, etc).
  • validation errors for a specific language string will be easier to place on page, close to that particular input;
  • makes entities faster to translate [just like a production line, where the mental price of switching languages is bigger than the mental price of switching strings (work one language, then switch to another language)];

I am, however, struggling with:

TECHNICAL:
1) whether to place translations in JSON or a separate table (as discussed above);

Will follow-up with a comprehensive comparison for us to make a decision. There are a lot of factors to be taken into consideration here, including:

  • db query time when retrieving rows;
  • db query time when searching for a string;
  • ease of programming when using a package or the other;
  • ease of switching from a single-language app to a multi-language app;

UI:
2) whether to place the big tabs at the top of the form, or use bootstrap pills on the left side of the form, in order to:

  • account for having a lot of languages, more that can fit in one row of tabs;
  • allow us to create multi-tabbed entities, while still allowing translation [an article could have, say Basic (Title, Slug, Content, Date), Metas (Meta title, Meta description, Meta keywords), Suggestions (Suggested Articles, Suggested Products)];

Need to see how that would look, though, might be pretty ugly and/or confusing (we already have the left-side menu).

UX:
3) whether the big tabs/pills should function as:

  • separate pages (with separate URLs), which will make editing, programming and customisation easier;
  • (or)
  • one page with multiple tabs/pills, which will allow a user to add an entry already completely translated;

Do users actually insert a row already translated, or do they translate it after the fact? My gut tells me they don't, but we need to do some user research to find out.

In my previous admin panel builder (Dick), I did implement translatable fields, but I made the mistake of implementing the first solution that came to mind. Then wanted to change a lot of stuff, but I couldn't, because people were already using the package the way I built it. I don't want to make that same mistake again. I'm hopefully not going to fall into analysis paralysis, but this is a complex functionality that I want to get right from the start. That's the entire reason we don't have it by now :-)

What are your thoughts on the 3 issues above?

Technical issue
Saving the language in a separate table will give an advantage in speed, because there is no need each time to parse json (except mysql 5.7 and above), but as a consequence, a long initial setup.

UI
In my previous implementation of multi-language site I just did as you have in the screenshot, but there were not as such the possibility to dynamically add fields.
Based on the foregoing, we conclude that the best thing will be to implement a separate table for languages.

Or add the locale as a column for the table that becomes translatable and using a global query scope for filtering?

@axyr that's exactly what the dimsav/laravel-translatable package does.

@tabacitu I begin to rewrite the multilingual field tomorrow, but it will no longer field would be correct to call it a multilingual CRUD, see what happens. For me, this is the first experience of participation in the open sourse community. Inspiring

Technical

IMO I prefer the separate table approach and beside your points I think that the majority of hosting companies have not switched to MySQL v 5.7 at least in my country.

UI

Maybe tabs with a bit of add-on solution like this for example or this

UX

Separate pages I guess... would not it be easier when validating the request or response back with error messages (except If we use some ajax requests).

Note sure the progression of this item, or if this has been started/finished or whats not, but we had a think about this in the past and we ended up choosing to do it was something like:

$row->id = 1;
$row->name = 'over';
$row->description = 'its over';
$row->status = 1;
$row->translations = {}; //json store of translations

then the translations column would be a json store of the customised fields so you'd have

{
  "de" : {
     "name": "uber"
  }
}

then something like which updates the actual values

if( $session->locale !== 'en' ){
    $row = array_merge($row, $row->translations->{$session->locale})
}

and you end up something like

$row->id = 1;
$row->name = 'uber';
$row->description = 'its over';
$row->status = 1;
$row->translations = {}; //json store of translations

We chose this route as the previous translations table we had got incredibly large and became a nightmare to debug and trace back data as you never knew if if the data was in the translations table or the actual models database row

@OwenMelbz ,

That actually makes more sense to me than:

OR

Do you know any package that does that for Laravel, so we just create the interface for it, or would we have to code the translation storage/retrieval logic too?

@tabacitu I've never seen it done in laravel, however I know https://wpml.org/ for wordpress does it with serialised data.

So potentially this might be something needing to be built from scratch, so in the long run maybe worth using a pre-existing library than supporting another package/section.

One of the biggest issues I can see being tricky in laravel would be things like

Trains::where(['url' => Request::segment(1)]->get(); as the segment would be in German, but the Model will run the search on the url field, not the json field.

A problem I see with this design is that what happens when you have a field that is not translatable and is the same for both languages. For instance a color or time field. All of the translation wordpress plugins I've seen have a tab per field for this reason.

Hmm... I think you're right, @VictorSigma . There definitely should be non-translatable columns. That's easy to do in code, with a property on the model (dimsav/translatable has $translatableAttributes and spatie/translatable has $translatable).

But how would this be reflected in the interface? I see three options:
1) We show the untranslatable fields in all translations, so you can edit them from anywhere; we also show some sort of notice next to each untranslatable field, to let the admin know that value will be updated across all translations;
2) We show the untranslatable fields only in the primary form, and maybe show them disabled in all others, with a notice "modify this from the primary form".
3) We show the untranslatable fields separately;

I think the most user-friendly option is the first one, since it doesn't require the user to go somewhere else, modify it, then come back. And it doesn't require him to complete 2 tabs in order to submit one entity (translatable and untranslatable fields).

What do you think?

@OwenMelbz you're going to love the MySQL JSON columns ;-)

However, we should keep PostgreSQL, SQL Server, SQLite, MongoDB support in mind...

Honestly I don't really like any of those options. And much prefer the tab per field. When translator translates a piece of text or if the person is already multi-lingual, they will need to quickly jump back and forth between the language fields (especially if their is a long piece of text). In the "tab per language scenario" the translator will need to scroll down read a sentence, scroll back up, switch tabs, scroll back down, write the translation. Repeated for every single sentence/field. Constantly scrolling back up and down. I know it doesn't look as clean but it is the way most sites I've seen do it.

@VictorSigma , I think you're right, that would be way too much jumping around. That's why I proposed that the default value when you're on a translation page to be the text in the "primary" language (see image again). So the translator wouldn't have to jump at all: if you have English (primary), French and German, when on the French tab you'd already have the English texts typed in. You'd only need to write your French text in the field and then delete the English one. That would make translating to a new language almost twice as fast, I believe. I think it's the fastest for the translator, because:

  • there would be zero switching between tabs;
  • they can actually reuse some text from the primary language (sometimes the translations is similar to the original);
  • in fields where the content has a complex format (WYSIWYG, textareas) it's _way_ easier to keep the format from the primary language by editing, rather than recreating; and those are the fields that are the most time-intensive for translators;

Please tell me if having the primary language pre-written makes this a better option than tabs in your opinion.

I do see a possible problem with this interface though: if the admin clicks "save" after translating just a few fields, the translations would be saved (French text), though the text is actually in English (following the same example). But I guess we can solve that with a big NOTICE. Right?

Cheers and thanks!

I do see a possible problem with this interface though: if the admin clicks "save" after translating just a few fields, the translations would be saved (French text), though the text is actually in English

I don't see this as a problem since most systems I saw will fallback to the English if no translations exists. Although you might want to make this an option. I see it more of a storage issue. Say I have a news site with articles. Maybe only a 1/6 of them will have a translations out of the thousands. But every article will have two copies of the exact same text stored in the DB. This might be mitigated with some code checking for duplicates. But then the View might want to check if there is a translation available and then it becomes unclear. Some languages use a mixture of english and another language such as hindi. In that case the english and hindi translation might be the same thing.

I do like the like the copying of formatted text between languages and see how it mitigates a lot of the problems. I could see this being done when the user switches tabs. I still like the need to flip back is important for each field. Sometime translations are not easily one to one. For instance I've had clients completely reorder a piece of text essentially destroying the original. Plus their is the use case of a bilingual person writing something then immediately writing the translation (this is done a lot for short fields). How bout something like
this
? Seems like it has a lot less clutter and takes up the same amount of space whether the user is using translations or not.

Hi! Is there any update on this issue? Thanks

I HOPE to have this finished by the end of the month. It's the next feature to be launched, and it's an important one. Hope I can make the time to do it. I'll keep you guys updated.

Cheers!

Great news, I will stay tunned. Thank You! :)

+1 for separate table to allow compatibility with existing systems. Including Asgardcms and other projects we have with dimsav/laravel-translatable

UI/UX

This is how a plugin of OctoberCMS is doing it, with a dropdown per-field basis
screen shot 2016-12-12 at 11 30 29

I believe this solution is the most elegant and doesn't add tabs that can get confusing/clunky quickly

Some improvements I would add from the above solution

  • Relocate the dropdown from inside the input to inline with the label (so that it works with select, WYSIWYG and virtually any kind of field)
  • Add a select input at the top that allows to switch all fields to the selected locale with 1 click

something like this
screen shot 2016-12-12 at 11 46 01

And as a nice to have I would also add a visual feedback about the state of the translation for each locale

The indicator (kind of ugly in the mockup :) could be both at the locale and field level
It would assist the editor when uploading/reviewing translations

screen shot 2016-12-12 at 11 48 40

Wow, that erm, I do not like that at all haha! Imagine being a dutch editor, then having to click on every dropdown field to reveal the language you want, or conflicts with other field types that have inline interactions. Personally I think that wont work for Backpack unless I'm overlooking something.

My personal preference has always just been a simple query string and dropdown which lets you pick the language you're editing. Then the UI would be as simple as a dropdown you pick which version you're currently editing

untitled

@OwenMelbz that could work too :)

IMO to the solution you proposed we still need a to add an indicator of which fields are global and which are translatable (inline with the label?)

Ah I see yeah, I just assumed then that all fields are translatable :P but sure a little visual indicator by the labels would definitely be helpful

Base/Crud have had the philosophy of not forcing any certain type of experience on the end user. I do like the feature and think it's neat, but adding it sets a precedent thatBase/Crud are now both okay with being opinionated.

I personally like leaving stuff like this optional as a separate package.

Hmm... I'd have to agree with @OwenMelbz on this one. The dropdown he's proposed is a ridiculously simple solution for a complex problem. And I love it when that happens :-)

@higiacomo , I think the "_each field has a toggle_" approach would make it more difficult for users to write custom field types. And since not everybody will be using multi-language, this would be bad UX for the developer - for each custom field they'd have to create a functionality that they don't actually use. But you do bring up a good point - how would we include/highlight the fields that are non-translatable? Do you have any ideas?

@zschuessler , you bring up a good point too. I'll take a look if we can release this as a separate package, that might be cleaner. But even if we don't, everything will live inside one trait and the "edit/create" view will only have one line inserted - that dropdown. So it shouldn't complicate the developer's UX or add any bloat to the package. But you're right - we will need to make some choices and this will make Backpack more opinionated. But the lack of multilanguage makes Backpack useless for some projects, so I think an opinionated option is better than no option. Plus, the interface is just based on views. In a not-too-distant Backpack\Base version I plan on launching "themes". If you like another way to do it, you can just use a different theme (it will just be a bunch of blade, css and js files). Or create your own theme, just for your projects or your company. Or open-source it so anybody else can use it. So that should solve it, right? :-)

Thanks for the feedback guys. Please keep it coming. I'll be working on this as soon as this week, so the sooner your opinions are expressed, the better.

Cheers!

Just wanted to chime in with an example on how to show which fields are translatable / non translatable. In Craft CMS it is done with a little tag style label next to the field label that shows the current language code of the field content. If the field is not translatable no language code is shown. Look at the video on this page for an example. Maybe something similar could be done for Laravel Backpack?

You can probably get away with something like

edit_school____footy_finance_admin_

However i'd highly recommend setting a config item like 'show_translatable_field_icon' => true/false

This would allow the icon to be hidden, you could additionally have 'translatable_field_icon' => 'fa-language' allowing users to toggle it.

Personally I'd have the icon disabled, I don't feel the user needs an extra icon next to them, I think its just UI bloat people are adding based on a concept of "the user needs to know what fields are translatable" where I feel in reality, this probably isn't the case, I don't really see many users getting confused in a real life situation, I think the interface would be obvious enough as it is and it may be over-engineering it + extra markup/code for people who dont want it (unless you're injecting it via js)

TL;DR Keep it simple, don't assume the user will need things because you've done/seen it in the past.

@reekris , thanks, Craft is really nailing some things, it's very good to know how they do things.
@OwenMelbz , I agree, we should give developers the option of not having these. And yes, it's a bummer to insert code in all fields for this. I'll try to avoid that.

Personally, I think it makes more sense to highlight to the user which fields are NOT translatable. The current language is highlighted in the box-header, and the user has probably already clicked to edit that particular language. So it's no new information for him that most fields will be to edit that translation. What I think we _should_ make obvious at this point is the fields that are cross-translation:

  • there will be less of those
  • changing one option there will change it for all other translations (need to tell the user that)

What do you guys think about something like this?
edit-translation

I think that would make more sense, less is more :)

However I would think we'd need to consider the positioning of the icons as if people are using prefix/suffix on their fields and if people are marking fields as required like my screenshot then it could get a bit busy (that's why I aligned mine out of the way)

Unless you've got any better ideas?

Deal! It sounds like we've got a front-end :-) Anybody else that want to weight in with alternatives, please do.

On the back-end I'll first try to make an interface for both packages, so that it would work with both dimsav/laravel-translatable and spatie/laravel-translatable, depending on what trait your model is using. Should be perfectly possible, will come back to you guys if it's not.

Cheers! Over and out! :-)

I really like your CMS and cannot wait for the translatable fields!
A short question though: Will it be possible to translate slugs? And if so, will there also be an option to call those slugs directly (in the language the user is currently viewing the website)? (like https://github.com/doitonlinemedia/TranslatableRoutes)

BOOM!

I'm happy to tell you we've _finally_ got a fully-working solution on the multilanguage branch :-)

Screenshots

screen shot 2016-12-23 at 15 13 28

screen shot 2016-12-23 at 14 50 59

Test drive

If anyone wants to give it a test-drive, here's how to do that. Please make sure you test drive in a separate branch. Some things might change before final release.

  1. Take a look at the pull request to see what's changed in the code.
  2. Use the "multilanguage" branch in you composer.json. It's similar to how you'd try the 3.2 branch.
  3. Install spatie/laravel-translatable
  4. Instead of using spatie's HasTranslations trait on your model, use Backpack's HasTranslations and define what fields are translatable on your model. For example:
namespace App\Models;

use Backpack\CRUD\CrudTrait;
use Illuminate\Database\Eloquent\Model;
use Backpack\CRUD\ModelTraits\SpatieTranslatable\HasTranslations;

class Product extends Model
{
    use CrudTrait;
    use HasTranslations;

     /*
    |--------------------------------------------------------------------------
    | GLOBAL VARIABLES
    |--------------------------------------------------------------------------
    */

    protected $table = 'products';
    protected $primaryKey = 'id';
    protected $fillable = ['name', 'category_id', 'options', 'price', 'tags'];
    protected $translatable = ['name', 'options'];
  1. Make the database columns you want to be translatable "text" or "json". You can use migrations or do it directly in the db, whatever floats your boat.
  2. The CRUD config file now has a list of all locales in the world. Uncomment the ones you want.
  3. You'll notice that:
  4. the "Edit" button now has a dropdown with your languages
  5. the edit form will have a dropdown with your languages
  6. the create form will NOT have a dropdown with your languages (entries are created in the base language 99% of the time, then translated)
  7. the translatable fields have a flag next to them

I've tried it with all reasonable fields, columns, features and use cases - it _looks to me_ like it works as intended. But it's a big important feature, so please tell me if you find anything I didn't.

Thanks, cheers!

Notes & Questions

A. Currently it only supports the way spatie/laravel-translatable does translations, but it's done in such a manner that adding dimsav/laravel-translatable will be a non-breaking change. I call them adapters. Switching between one way and the other should be as easy as changing a trait in your model.

B. You'll probably get errors if you already had entries in that table, because the translatable columns don't have JSONs in them. So it's best to start with a fresh table.

C. When you go to edit a French translation, you'll notice ?locale=fr in the address bar. There's also a hidden input inside the edit form, with the name "locale", that uses that value. Do you think it's too common and it might conflict with anyone's app? Why would you call an attribute or db column "locale" if it's not the locale?

Fantastic :D

However... Personally I'm not keen on that little racing flag haha! Personally I'd use the language icon from FontAwesome http://fontawesome.io/icon/language/

I've also noticed many other CMSs/frameworks use that icon, but its your software :D

This feature have some PHP7 only sintaxis. I like it but i think it should be noted somewhere.

One question. Can Fake Fields be translated?

@imagina can you give an example of the php7 specific syntax?

For example the Null coalescing operator: ??

$locale = $attributes['locale'] ?? \App::getLocale();

Ah right cool, did you say this is used within backpack then? Or the translatable package by spatie? (As we can't do much about that) ?

It seems Pagemanager and some other Backpack packages don't work with the "multilanguage" branch, are there any safe "tweaks"?

Heads up:

BREAKING CHANGES on this branch!

  1. I've renamed Backpack\CRUD\ModelTraits\Translatable\SpatieTranslatableAdaptor to Backpack\CRUD\ModelTraits\SpatieTranslatable\HasTranslations, so anyone using SpatieTranslatableAdaptor in their models, please change it. Complete example below. I've made this change in order to preserve consistency: all classes that are overwritten by Backpack now have the same name as the class it overwrites. This will make them easier to use, easier to understand and easier to search.

  2. The latest push will make cviebrock/eloquent-sluggable work with spatie/laravel-translatable. It's pretty "plug-and-play", you just need to use the included traits instead of the ones provided by eloquent-sluggable. Please note: you need MySQL 5.7 to use cviebrock/eloquent-sluggable and spatie/laraveltranslatable (or PosgreSQL with JSON column compatibility). Otherwise features like findBySlug() and findBySlugOrFail() won't work. I found no way around it, the queries were way too slow without this mysql version. And it was a hack anyway. It's a pain to upgrade, I know, I've gone through it myself, but in the end it's worth it. Laravel keeps pushing us to adopt the latest version (PHP 7+, MySQL 5.7+), so let's just do it. @AurelDragut , this will make it easier to integrate with PageManager when Laravel 5.4 rolls around (in a few days).

--

Here's a complete translatable model definition, with the renamed SpatieTranslatableAdaptor, and that uses eloquent-sluggable:

<?php

namespace Backpack\NewsCRUD\app\Models;

use Illuminate\Database\Eloquent\Model;
use Backpack\CRUD\CrudTrait;
use Backpack\CRUD\ModelTraits\SpatieTranslatable\Sluggable;
use Backpack\CRUD\ModelTraits\SpatieTranslatable\SluggableScopeHelpers;
use Backpack\CRUD\ModelTraits\SpatieTranslatable\HasTranslations;

class Category extends Model
{
    use CrudTrait;
    use Sluggable, SluggableScopeHelpers;
    use HasTranslations;

    /*
    |--------------------------------------------------------------------------
    | GLOBAL VARIABLES
    |--------------------------------------------------------------------------
    */

    protected $table = 'categories';
    protected $primaryKey = 'id';
    // public $timestamps = false;
    // protected $guarded = ['id'];
    protected $fillable = ['name', 'parent_id'];
    // protected $hidden = [];
    // protected $dates = [];
    protected $translatable = ['name', 'slug'];

    /**
     * Return the sluggable configuration array for this model.
     *
     * @return array
     */
    public function sluggable()
    {
        return [
            'slug' => [
                'source' => 'slug_or_name',
            ],
        ];
    }
}

Any feedback on the new translatable & eloquent-sluggable feature is welcome, of course. Especially as it will be included in the next CRUD version in a few days, when Laravel 5.4 launches.

Cheers!

PS. Also updated the new syntax in the comment above.

What's the status ?

hello @tabacitu
i would like to test this branch, could you please update multilang branch to fetch last 3.2 commits?
thanks in advance,
pxpm

@pxpm 3.2 is already using the multilanguage branch. So if you download 3.2 you will have translateable fields

sorry for not paying attention to that. and thanks for answering @petyots .
Sorry for bothering you with this, could you please enlight me how can i refactor this code to work with L5.4

$config_items = app('config')->all();
    $this->app['config'] = $this->app->share(function($app) use ($config_items)
    {
        return $app->make('App\ConfigWritter\Repository', $config_items);
    });

share is not supported by laravel anymore... thanks

I am not quite sure but you can try this, it's not tested btw

$config_items = app('config')->all();
    $this->app['config'] = $this->app->singleton(function($app) use ($config_items)
    {
        return $app->make('App\ConfigWritter\Repository', $config_items);
    });

Or

$config_items = app('config')->all();
    $this->app['config']=$this->app->singleton('App\ConfigWritter\Repository', function ($app) use($config_items) {
    return new App\ConfigWritter\Repository($config_items);
});

Hi @tabacitu
I am using the latest version 3.2 of backpack CRUD, with mysql 5.7 and multinglang models, it works perfectly with the example above and when I set the column type to text, but if I use json for the column type then I get the following error when calling $this->crud->setFromDb(); from the controller. Have you experienced this error before or is it just not possible to use the setFromDb() when using JSON columns?

DBALException in AbstractPlatform.php line 423:
Unknown database type json requested, Doctrine\DBAL\Platforms\MySQL57Platform may not support it.

in AbstractPlatform.php line 423
at AbstractPlatform->getDoctrineTypeMapping('json') in MySqlSchemaManager.php line 126
at MySqlSchemaManager->_getPortableTableColumnDefinition(array('field' => 'name', 'type' => 'json', 'null' => 'NO', 'key' => '', 'default' => null, 'extra' => '', 'comment' => '', 'characterset' => null, 'collation' => null, 'name' => '')) in AbstractSchemaManager.php line 820
at AbstractSchemaManager->_getPortableTableColumnList('brands', 'site-manager-backpack', array(array('Field' => 'id', 'Type' => 'int(10) unsigned', 'Null' => 'NO', 'Key' => 'PRI', 'Default' => null, 'Extra' => 'auto_increment', 'Comment' => '', 'CharacterSet' => null, 'Collation' => null), array('Field' => 'name', 'Type' => 'json', 'Null' => 'NO', 'Key' => '', 'Default' => null, 'Extra' => '', 'Comment' => '', 'CharacterSet' => null, 'Collation' => null), array('Field' => 'brand_code', 'Type' => 'varchar(191)', 'Null' => 'NO', 'Key' => 'UNI', 'Default' => null, 'Extra' => '', 'Comment' => '', 'CharacterSet' => 'utf8mb4', 'Collation' => 'utf8mb4_unicode_ci'), array('Field' => 'login_domain', 'Type' => 'json', 'Null' => 'NO', 'Key' => '', 'Default' => null, 'Extra' => '', 'Comment' => '', 'CharacterSet' => null, 'Collation' => null), array('Field' => 'shell_domain', 'Type' => 'json', 'Null' => 'NO', 'Key' => '', 'Default' => null, 'Extra' => '', 'Comment' => '', 'CharacterSet' => null, 'Collation' => null), array('Field' => 'mail_from_address', 'Type' => 'json', 'Null' => 'NO', 'Key' => '', 'Default' => null, 'Extra' => '', 'Comment' => '', 'CharacterSet' => null, 'Collation' => null), array('Field' => 'created_at', 'Type' => 'timestamp', 'Null' => 'YES', 'Key' => '', 'Default' => null, 'Extra' => '', 'Comment' => '', 'CharacterSet' => null, 'Collation' => null), array('Field' => 'updated_at', 'Type' => 'timestamp', 'Null' => 'YES', 'Key' => '', 'Default' => null, 'Extra' => '', 'Comment' => '', 'CharacterSet' => null, 'Collation' => null))) in AbstractSchemaManager.php line 175
at AbstractSchemaManager->listTableColumns('brands') in AbstractSchemaManager.php line 281
at AbstractSchemaManager->listTableDetails('brands') in Connection.php line 843
at Connection->getDoctrineColumn('brands', 'id') in Builder.php line 121
at Builder->getColumnType('brands', 'id') in AutoSet.php line 53
at CrudPanel->getDbColumnTypes() in AutoSet.php line 17
at CrudPanel->setFromDb() in BrandCrudController.php line 33
at BrandCrudController->setUp() in CrudController.php line 39
at CrudController->Backpack\CRUD\app\Http\Controllers\{closure}(object(Request), object(Closure)) in Pipeline.php line 131
at Pipeline->Illuminate\Pipeline\{closure}(object(Request)) in Pipeline.php line 53
at Pipeline->Illuminate\Routing\{closure}(object(Request)) in Admin.php line 29
at Admin->handle(object(Request), object(Closure)) in Pipeline.php line 148
at Pipeline->Illuminate\Pipeline\{closure}(object(Request)) in Pipeline.php line 53
at Pipeline->Illuminate\Routing\{closure}(object(Request)) in SubstituteBindings.php line 41
at SubstituteBindings->handle(object(Request), object(Closure)) in Pipeline.php line 148
at Pipeline->Illuminate\Pipeline\{closure}(object(Request)) in Pipeline.php line 53
at Pipeline->Illuminate\Routing\{closure}(object(Request)) in VerifyCsrfToken.php line 65
at VerifyCsrfToken->handle(object(Request), object(Closure)) in Pipeline.php line 148
at Pipeline->Illuminate\Pipeline\{closure}(object(Request)) in Pipeline.php line 53
at Pipeline->Illuminate\Routing\{closure}(object(Request)) in ShareErrorsFromSession.php line 49
at ShareErrorsFromSession->handle(object(Request), object(Closure)) in Pipeline.php line 148
at Pipeline->Illuminate\Pipeline\{closure}(object(Request)) in Pipeline.php line 53
at Pipeline->Illuminate\Routing\{closure}(object(Request)) in StartSession.php line 64
at StartSession->handle(object(Request), object(Closure)) in Pipeline.php line 148
at Pipeline->Illuminate\Pipeline\{closure}(object(Request)) in Pipeline.php line 53
at Pipeline->Illuminate\Routing\{closure}(object(Request)) in AddQueuedCookiesToResponse.php line 37
at AddQueuedCookiesToResponse->handle(object(Request), object(Closure)) in Pipeline.php line 148
at Pipeline->Illuminate\Pipeline\{closure}(object(Request)) in Pipeline.php line 53
at Pipeline->Illuminate\Routing\{closure}(object(Request)) in EncryptCookies.php line 59
at EncryptCookies->handle(object(Request), object(Closure)) in Pipeline.php line 148
at Pipeline->Illuminate\Pipeline\{closure}(object(Request)) in Pipeline.php line 53
at Pipeline->Illuminate\Routing\{closure}(object(Request)) in Pipeline.php line 102
at Pipeline->then(object(Closure)) in Router.php line 561
at Router->runRouteWithinStack(object(Route), object(Request)) in Router.php line 520
at Router->dispatchToRoute(object(Request)) in Router.php line 498
at Router->dispatch(object(Request)) in Kernel.php line 174
at Kernel->Illuminate\Foundation\Http\{closure}(object(Request)) in Pipeline.php line 30
at Pipeline->Illuminate\Routing\{closure}(object(Request)) in TransformsRequest.php line 30
at TransformsRequest->handle(object(Request), object(Closure)) in Pipeline.php line 148
at Pipeline->Illuminate\Pipeline\{closure}(object(Request)) in Pipeline.php line 53
at Pipeline->Illuminate\Routing\{closure}(object(Request)) in TransformsRequest.php line 30
at TransformsRequest->handle(object(Request), object(Closure)) in Pipeline.php line 148
at Pipeline->Illuminate\Pipeline\{closure}(object(Request)) in Pipeline.php line 53
at Pipeline->Illuminate\Routing\{closure}(object(Request)) in ValidatePostSize.php line 27
at ValidatePostSize->handle(object(Request), object(Closure)) in Pipeline.php line 148
at Pipeline->Illuminate\Pipeline\{closure}(object(Request)) in Pipeline.php line 53
at Pipeline->Illuminate\Routing\{closure}(object(Request)) in CheckForMaintenanceMode.php line 46
at CheckForMaintenanceMode->handle(object(Request), object(Closure)) in Pipeline.php line 148
at Pipeline->Illuminate\Pipeline\{closure}(object(Request)) in Pipeline.php line 53
at Pipeline->Illuminate\Routing\{closure}(object(Request)) in Pipeline.php line 102
at Pipeline->then(object(Closure)) in Kernel.php line 149
at Kernel->sendRequestThroughRouter(object(Request)) in Kernel.php line 116
at Kernel->handle(object(Request)) in index.php line 53
at require('/Users/alfhenderson/workspace/site-manager/public/index.php') in server.php line 133

@alfhen the error clearly explains the problem :)

It tells you it's from Doctrine.

Your best off asking over at Doctrine for how to support the json field type :)

@OwenMelbz, yeah indeed, I was just hoping you might have experienced this already, but I found a solution: By adding this to the boot method of the AppServiceProvider.

$platform = Schema::getConnection()->getDoctrineSchemaManager()->getDatabasePlatform();
$platform->registerDoctrineTypeMapping('json', 'text');

@alfhen hi there. i experienced the same problem you did.
If you look at Doctrine on Github you will find a solution.
Basically is creating a new adapted for supporting json.
There is a PR already submited but not merged yet.
Also you will have problems if working with xampp in the lastest versions because MariaDB (the default that come with it) does not provide fully support to json columns natively.
If you map that to text probably the spatie functions for translating json fields are not going to work as it uses native json functions.

@pxpm Thanks for that it is helpfull, info! 馃憤 馃槃

Hi everyone! I've managed to get this working smoothly, can you please tell me if there is an easy way to access the translations in the views? I've created a TranslationsHelper Trait, that fills an array with the translatable fields.

Here is my code:

namespace App\Models\Traits;

trait TranslationsHelper
{
    /**
     * Fills the translation attribute of the current model
     * based on the translatable attribute
     */
    public function fillTranslations() {
        $trans = array();
        foreach ($this['translatable'] as $value)
            $trans[$value] = $this->$value;
        $this['translations'] = $trans;
    }
}

By using this trait in my model, and calling $myModel->fillTranslations(); _(in the controller)_ I can access the translations like this: $myModel['translations']['title'].
Is this correct? Thanks!

What about dimsav/translatable support?

Hello,
i'm learning laravel. I'm using laravel translation table. But i can't join admin panel and frontend. How i learn and let me know tutorial.
Thanks

Hi @zinnia-visible

Welcome to our community. We use github only for issues/features and small info.

If you want some additional support from the community you should ask on stackoverflow or join our gitter chatroom in https://gitter.im/BackpackForLaravel/Lobby

Also our Documentation should help you in getting started.

Best,

Was this page helpful?
0 / 5 - 0 ratings

Related issues

genesiscz picture genesiscz  路  3Comments

sseggio picture sseggio  路  3Comments

M0H3N picture M0H3N  路  3Comments

bastos71 picture bastos71  路  3Comments

sokvebolkol picture sokvebolkol  路  3Comments