构建 Laravel 翻译包 - 数据库驱动

发布时间 作者

Building a Laravel Translation Package – The Database Driver image

在系列的上一篇文章中,我们讨论了如何处理缺失的翻译,这让我们离将该包的功能完善非常近。为了完成本系列的构建阶段,我们将讨论如何添加数据库驱动。

签署合同

正如您可能还记得本系列的早期内容,我们使用接口定义了文件驱动程序需要实现的所有方法才能正常运行。这样做的主要原因是,始终在包路线图中能够支持多个驱动程序。定义接口意味着我们有一个合同,所有新的驱动程序都可以依赖该合同来提供所需的功能。

迁移

为了启动数据库驱动程序,我们需要一些数据库表来存储我们的翻译。这不需要过于复杂 - 一个用于存储语言的表和另一个用于存储翻译的表就足够了。

// languages table
public function up()
{
Schema::create(config('translation.database.languages_table'), function (Blueprint $table) {
$table->increments('id');
$table->string('name')->nullable();
$table->string('language');
$table->timestamps();
});
}
 
// translations table
public function up()
{
Schema::create(config('translation.database.translations_table'), function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('language_id');
$table->foreign('language_id')->references('id')->on(config('translation.database.languages_table'));
$table->string('group')->nullable();
$table->text('key');
$table->text('value')->nullable();
$table->timestamps();
});
}

我们使用包配置来决定迁移中表的名称。这允许包的用户命名他们自己的表,并避免任何潜在的冲突。

还值得注意的是翻译表上的 language_id 外键。这告诉我们每个翻译属于哪种语言。

还有一个名为 group 的可空字段,它将用于跟踪它是单个翻译还是组翻译。

最后,还有用于 keyvalue 的字段,顾名思义,它们存储每个翻译的键和值。

为了使迁移在用户运行 php artisan migrate 时自动运行,我们需要使用 loadMigrationsFrom 方法将它们加载到我们的服务提供程序中,并将新创建的迁移的路径传递进去。

public function boot()
{
$this->loadMigrationsFrom(__DIR__.'/../database/migrations');
}

模型

我们的模型也可以保持相对简单。鉴于我们允许用户定义他们自己的表名,每个模型都需要明确定义其关联的表。我们可以在模型的构造函数中通过将配置中设置的值赋值来做到这一点。

public function __construct(array $attributes = [])
{
parent::__construct($attributes);
$this->table = config('translation.database.languages_table');
}

最后,每个表都需要定义它与另一个表的关联关系

...
 
class Langauge extends Model
{
...
 
public function translations()
{
return $this->hasMany(Translation::class);
}
}
...
 
class Translation extends Model
{
...
 
public function language()
{
return $this->belongsTo(Language::class);
}
}

连接驱动程序

在定义了迁移和模型之后,我们现在可以开始构建数据库驱动程序实现。为此,我们将创建一个名为 Database 的新类,并实现驱动程序接口。我们还将扩展 Translation 抽象类,该类包含所有驱动程序实现共有的某些方法。

class Database extends Translation implements DriverInterface
{
...
}

现在,只需要构建所需的方法。为此,我们将大量依赖 Eloquent。我在下面记录了一些比较有趣的方法,但是如果您有兴趣查看完整的实现,可以在GitHub 上查看。

public function allLanguages()
{
return Language::all()->mapWithKeys(function ($language) {
return [$language->language => $language->name ?: $language->language];
});
}
 
// $this->allLanguages();
 
// [
// ‘en’ => ‘en’,
// ‘fr’ => ‘fr’,
// ‘es’ => ‘es’,
// ];

在这里,我们使用 Eloquent 从数据库中获取所有语言,并遍历每个语言,以与文件驱动程序相同的格式返回语言。

public function getGroupTranslationsFor($language)
{
$translations = $this->getLanguage($language)
->translations()
->whereNotNull('group')
->where('group', 'not like', '%single')
->get()
->groupBy('group');
 
return $translations->map(function ($translations) {
return $translations->mapWithKeys(function ($translation) {
return [$translation->key => $translation->value];
});
});
}
 
// $this->getGroupTranslationsFor('en');
 
// [
// ‘auth’ => [
// ‘failed’ => ‘These credentials do not match our records’,
// ],
// ]

在这里,我们返回给定语言的所有组翻译。我们通过使用 Eloquent 获取该语言的所有翻译,其中 group 字段不包含 single 来做到这一点。然后,我们遍历 Eloquent 集合,以正确的格式返回结果。

public function addGroupTranslation($language, $key, $value = '')
{
list($group, $key) = explode('.', $key);
 
Language::where('language', $language)
->first()
->translations()
->updateOrCreate([
'group' => $group,
'key' => $key,
], [
'group' => $group,
'key' => $key,
'value' => $value,
]);
}

在这里,我们按句点拆分键以获取组和翻译的键。然后,我们使用 Eloquent 的 updateOrCreate 方法来确定翻译是否存在。如果存在,我们更新它以防值已更改。如果不存在,我们为该语言创建一个新的翻译。

使用驱动程序

为了告诉 Laravel 实例化哪个驱动程序,我们将创建一个名为 TranslationManager 的新类,该类使用包配置来确定要返回哪个驱动程序。

public function resolve()
{
$driver = $this->config['driver'];
$driverResolver = studly_case($driver);
$method = "resolve{$driverResolver}Driver";
 
return $this->{$method}();
}
 
protected function resolveFileDriver()
{
return new File(...);
}
 
protected function resolveDatabaseDriver()
{
return new Database(...);
}

在这里,我们获取配置文件中定义的驱动程序值,将其转换为负责理解如何实例化驱动程序并返回它的方法。这样一来,将来添加新的驱动程序将成为一项更轻松的任务。例如,如果我们要在配置中添加一个名为 cloud 的新驱动程序,我们将向 TranslationManager 添加一个名为 resolveCloudDriver 的方法,该方法返回该驱动程序的新实例。

现在,在我们的服务提供程序的 register 方法中,我们可以将翻译驱动程序作为单例绑定到容器中。这意味着任何时候我们从容器中获取它,都会返回相同的实例。

use JoeDixon\Translation\Drivers\Translation;
 
...
 
$this->app->singleton(Translation::class, function ($app) {
return (new TranslationManager($app, $app['config']['translation'], $app->make(Scanner::class)))->resolve();
});

您可能会注意到我们实际绑定的类是抽象 Translation 类。这是可能的,因为我们所有的驱动程序都扩展了此类。它很有用,因为它意味着无论实例化哪个驱动程序,都可以通过相同的方式从容器中获取它。

有了这个驱动程序,我们的包几乎可以发布到世界上了。在本系列的下一篇文章中,我们将讨论文档以及帮助用户开始使用该包所需的知识。

与往常一样,如果您有任何问题,请随时通过Twitter 联系。

Joe Dixon photo

ubisend 的创始人兼首席技术官。两个小英雄的父亲,丈夫,开发人员,偶尔的环球旅行者。

归档于
Cube

Laravel 新闻通讯

加入 40,000 多名其他开发人员,不错过任何新的技巧、教程等。

Laravel Forge logo

Laravel Forge

轻松创建和管理您的服务器,并在几秒钟内部署您的 Laravel 应用程序。

Laravel Forge
Tinkerwell logo

Tinkerwell

Laravel 开发人员必备的代码运行器。使用 AI、自动完成和对本地和生产环境的即时反馈进行调试。

Tinkerwell
No Compromises logo

绝不妥协

Joel 和 Aaron,来自“绝不妥协”播客的两位经验丰富的开发人员,现已可供雇用,为您的 Laravel 项目提供服务。 ⬧ 固定费用 7500 美元/月。 ⬧ 无需漫长的销售流程。 ⬧ 无需签订合同。 ⬧ 100% 退款保证。

绝不妥协
Kirschbaum logo

Kirschbaum

提供创新和稳定性,确保您的 Web 应用程序成功。

Kirschbaum
Shift logo

Shift

使用旧版 Laravel 版本?即时、自动化的 Laravel 升级和代码现代化,让您的应用程序保持新鲜。

Shift
Bacancy logo

Bacancy

让经验丰富的 Laravel 开发人员(拥有 4-6 年经验)以每月 2500 美元的价格为您的项目提供助力。获得 160 小时的专职专业知识和 15 天免费试用。立即预约通话!

Bacancy
Lucky Media logo

Lucky Media

现在就选择 Lucky - Laravel 开发的理想选择,拥有十多年的经验!

Lucky Media
Lunar: Laravel E-Commerce logo

Lunar:Laravel 电子商务

Laravel 的电子商务。一个开源包,为 Laravel 带来了现代无头电子商务功能的力量。

Lunar:Laravel 电子商务
LaraJobs logo

LaraJobs

官方 Laravel 招聘网站

LaraJobs
SaaSykit: Laravel SaaS Starter Kit logo

SaaSykit:Laravel SaaS 启动工具包

SaaSykit 是一款 Laravel SaaS 启动工具包,包含运行现代 SaaS 所需的所有功能。支付、精美结账、管理面板、用户仪表板、身份验证、即用型组件、统计信息、博客、文档等。

SaaSykit:Laravel SaaS 启动工具包
Rector logo

Rector

您无缝升级 Laravel 的合作伙伴,降低成本,加速创新,助力企业成功

Rector
MongoDB logo

MongoDB

通过强大的 MongoDB 和 Laravel 集成增强您的 PHP 应用程序,使开发人员能够轻松高效地构建应用程序。在使用熟悉的 Eloquent API 的同时,支持事务、搜索、分析和移动用例。了解 MongoDB 的灵活、现代数据库如何改变您的 Laravel 应用程序。

MongoDB
Maska is a Simple Zero-dependency Input Mask Library image

Maska 是一个简单的零依赖输入掩码库

阅读文章
Add Swagger UI to Your Laravel Application image

将 Swagger UI 添加到您的 Laravel 应用程序

阅读文章
Assert the Exact JSON Structure of a Response in Laravel 11.19 image

在 Laravel 11.19 中断言响应的精确 JSON 结构

阅读文章
Build SSH Apps with PHP and Laravel Prompts image

使用 PHP 和 Laravel Prompts 构建 SSH 应用程序

阅读文章
Building fast, fuzzy site search with Laravel and Typesense image

使用 Laravel 和 Typesense 构建快速、模糊的网站搜索

阅读文章
Add Comments to your Laravel Application with the Commenter Package image

使用 Commenter 包在您的 Laravel 应用程序中添加评论

阅读文章