Astrotomic/laravel-translatable Support
Hey guys,
Apologies for this, but after a few hours I can't manage to route bind models that use this package translatable trait.
My goal here is to make the urls dynamic such as:
/en/article/slug-in-english /pt/artigo/slug-in-portuguese
Using this package alongside => https://github.com/Astrotomic/laravel-translatable
In routes.php I have used:
Route::get(Lang::uri('article').'/{article:slug}', 'ArticleController@show')->name('article.show');
Then, in my language switcher dropdown:
\Route::localizedUrl($lang)
The problem so far, is that the dropdown only displays the slugs in english. It changes the 2 char locale ("en" => "pt") but not the slug itself.
Article Model:
<?php
/*
* Copyright (c) 2021 Ceuton.
*/
namespace App\Models;
use App\Http\Controllers\Controller;
use Astrotomic\Translatable\Contracts\Translatable as TranslatableContract;
use Astrotomic\Translatable\Translatable;
use Cviebrock\EloquentSluggable\Sluggable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\MorphToMany;
use Str;
use Throwable;
class Article extends Model implements TranslatableContract
{
use Translatable;
const STATUS_UNPUBLISHED = 0;
const STATUS_PUBLISHED = 1;
const STATUS_DRAFT = 2;
const STATUS_SCHEDULED = 3;
protected $fillable = [
'featured_img',
'scheduled_at',
'status',
'created_at',
'updated_at'
];
public array $translatedAttributes = [
'slug',
'title',
'body'
];
public function categories(): BelongsToMany
{
return $this->belongsToMany(Category::class);
}
public function tags(): BelongsToMany
{
return $this->belongsToMany(Tag::class);
}
public function keywords(): BelongsToMany
{
return $this->belongsToMany(Keyword::class);
}
public function medias(): MorphToMany
{
return $this->morphToMany(Media::class, 'mediable');
}
public function user(): BelongsTo
{
return $this->belongsTo(User::class, 'user_id');
}
public function getRouteKeyName(): string
{
return 'slug';
}
public function getRouteKey($locale = null)
{
try {
$slugs = [];
foreach (config('constants.locales') as $key => $name)
if($this->hasTranslation($key))
$slugs[$key] = $this->translate($key)->slug;
if($locale)
return $slugs[$locale];
return $slugs[app()->getLocale()];
} catch (Throwable $exception) {
return $this->slug;
}
}
public function getRoute(): string
{
try {
return route('article.show', [$this->getRouteKey()], true, app()->getLocale());
} catch (Throwable $exception) {
return "#";
}
}
public function getTitle($size = null, $locale = null)
{
try {
if(!$locale)
$locale = app()->getLocale();
if ($size)
return Str::limit($this->{'title:' . $locale}, $size);
return $this->{'title:' . $locale};
} catch (Throwable $exception) {
return "---";
}
}
public function getExcerpt($size = null): string
{
try {
if ($size)
return Str::limit(strip_tags($this->{'body:' . app()->getLocale()}), $size);
return strip_tags($this->{'body:' . app()->getLocale()});
} catch (Throwable $exception) {
return "---";
}
}
public function getBody($size = null): string
{
try {
if ($size)
return Str::limit($this->{'body:' . app()->getLocale()}, $size);
return $this->{'body:' . app()->getLocale()};
} catch (Throwable $exception) {
return "---";
}
}
public function getAdvertisedBody(): string
{
try {
return (new Controller())->injectAdv($this->getBody());
} catch (Throwable $exception) {
return "---";
}
}
public function getFeaturedImg(): string
{
try {
return asset_cdn('storage/uploads/' . $this->medias()->firstOrFail()->name);
} catch (Throwable $exception) {
return asset_cdn('assets/img/featured.jpg');
}
}
public function getFeaturedImgCredits()
{
try {
return $this->medias()->firstOrFail()->credits;
} catch (Throwable $exception) {
return null;
}
}
public function getMainCategory($attribute = "slug")
{
try {
return $this->categories()->firstOrFail()->$attribute;
} catch (Throwable $exception) {
return "other";
}
}
public function getKeywords()
{
try {
$keywords = collect([]);
$keywords->merge($this->categories()->pluck('name'));
$keywords->merge($this->tags()->pluck('name'));
return $keywords->toArray();
} catch (Throwable $exception) {
return collect([]);
}
}
public function getStatusElements()
{
switch ($this->status) {
case Article::STATUS_UNPUBLISHED:
return '<span class="badge badge-danger">Lixo</span>';
case Article::STATUS_PUBLISHED:
return '<span class="badge badge-success">Publicado</span>';
case Article::STATUS_SCHEDULED:
return '<span class="badge badge-primary">Agendado</span>';
case Article::STATUS_DRAFT:
return '<span class="badge badge-warning">Rascunho</span>';
}
}
public function resolveRouteBinding($value, $field = null)
{
return $this->translations()->where($field ?? $this->getRouteKeyName().'->'.app()->getLocale(), $value)->firstOrFail();
}
}
ArticleTranslation:
<?php
/*
* Copyright (c) 2021 Ceuton.
*/
namespace App\Models;
use Cviebrock\EloquentSluggable\Sluggable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class ArticleTranslation extends Model
{
use Sluggable;
public $timestamps = false;
protected $fillable = [
'locale',
'slug',
'title',
'body',
];
public function article(): BelongsTo
{
return $this->belongsTo(Article::class, 'article_id', 'id');
}
public function sluggable(): array
{
return [
'slug' => [
'source' => 'title'
]
];
}
}
At first glance, your code looks fine...
When you call...
\Route::localizedUrl($lang)
...your end result is...
/en/article/slug-in-english
/pt/artigo/slug-in-english
Right? Or is artigo not translated either?
I will try to reproduce this in a test.
Hey @ivanvermeyen,
As far as I can see, the problem was with the method localizedUrl.
I googled the problem and found this method that fixed the problem for me in RouteServiceProvider:
Route::macro('currentLocalizedUrl', function ($locale = null, $parameters = null, $absolute = true) {
$locale = $locale ?? app()->getLocale();
$parameters = $parameters ?: Route::current()->parameters();
$currentLocale = app()->getLocale();
app()->setLocale($locale);
foreach ($parameters as $attribute => $value) {
if ($value instanceof Model) {
$parameters[$attribute] = $value->getRouteKey();
}
}
app()->setLocale($currentLocale);
return route(Route::current()->getName(), $parameters, $absolute, $locale);
});
Let me know what you think :D
That is basically what the included macro should do, but it was expanded to handle unnamed routes and 404 pages, which don't have a Route::current().
Are you running the latest version of this package?
I am! That's why is wierd...
I got the issue myself now, will start debugging it :)
Cool @ivanvermeyen! I'll be waiting :D
Turns out that I have a different problem.
Because I use a route like posts/{id}/{slug?}, the id is using model binding but the slug is not, so it's just a string.
I'll have to fix that...
But with normal routes like posts/{slug} it should just work, as long as you type hint it in the controller.
The slugs are not cached in some way by the other package?
Hi,
Sorry, seems I lost sight of this issue... Is this issue still relevant? Closing for now, but feel free to reopen!