如何在PHP-Laravel的一个模型中有多个相同的多态关系?
How to have multiple same polymorphic relations in one model in PHP-Laravel?
我正在处理一个挑战,我想创建一个多态模型本地化。这将包括长文本列,例如 default、en_us、de_de,...
现在想象另一个模型 Product。产品通常具有文本属性,如名称、描述等……这部分来了,我可以在其中使用 Localizations 的多态关系。 localizations table 应该具有这种类型的结构:
id
localizable_id
localizable_type
default
en_us
de_de
1
25
App\Model\Product
Phone
null
Telefon
2
25
App\Model\Product
The best phone on the market without an audio jack
null
Das beste Telefon auf dem Markt ohne Audioanschluss
3
15
App\Model\Article
Top 10 products
null
Top 10 Produkte
4
15
App\Model\Job
Salesman
null
Verkäufer
如果我希望 Product 具有可本地化的名称和描述,那么根据 Laravel 文档,您应该会在我的代码中看到类似这样的内容:
class Product extends Model
{
public function name() // Expecting model with default attribute 'Phone'
{
return $this->morphOne(Localization::class,'localizable');
}
public function description() // Expecting model with default attribute 'The best phone on the market without an audio jack'
{
return $this->morphOne(Localization::class,'localizable');
}
}
无论多么明显,它都不会正常工作,因为我不能指望两个相同的方法可以 return 不同的值。
另一方面,如果我想遵循 Laravel 的约定,本地化 模型应该如下所示:
class Localizable extends Model
{
public function localizable()
{
return $this->morphTo(__FUNCTION__,'localizable_type','localizable_id');
}
}
正如你在上面的例子中看到的,多态关系与我需要的相反。问题是我想要一个包含所有字符串的 table,这些字符串可能会被翻译 (localizables) 并在许多其他模型中使用它们,而不仅仅是 Product 、喜欢商店、工作、文章、...
我需要以某种方式实现区分,不仅是 localizable_types 之间的区别,还有 column/attribute 应该相关的区别。实现此目标的最佳方法是什么?
我终于找到了满足我期望的解决方法。尽管这不是最漂亮的(或Laravel)方式。我从 GitHub here.
上发现的类似斗争中得到启发
此解决方案的重点是覆盖 Product 的 getMorphClass()
方法,该方法用于确定 *_type
列值。我的产品型号:
class Product extends Model
{
protected $morphClass = null; // Create an attribute that will not be saved into the DB
public function getMorphClass() // Method for determinating the type value
{
return $this->morphClass? : self::class;
}
public function name()
{
$this->morphClass = self::class . '.name'; // App\Models\Product.name
return $this->morphOne(Localization::class, 'localizable');
}
public function description()
{
$this->morphClass = self::class . '.description'; // App\Models\Product.description
return $this->morphOne(Localization::class, 'description');
}
}
上面的修改会影响,Laravel 如何将 Localization 保存到数据库。现在,需要从关系的另一端完成一些工作 - custom polymorphic types。在 app/Providers/AppServiceProvider.php
文件中更新您的 AppServiceProvider
class。您必须将 morphMap 添加到 boot()
方法:
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
// ...any of your previous modifications...
Relation::morphMap([
Product::class . '.name' => Product::class,
Product::class . '.description' => Product::class,
]);
}
但请注意: 一旦您决定在您的模型中使用多个相同类型的变形,您必须在每个动态方法中更改受保护的属性 $morphClass
调用 morphOne()
或 morphMany()
。否则,您可能会将描述保存到名称中,反之亦然。
此外,当您将在另一端使用 morphTo()
方法时,您将只会获得 Product - 如果您需要知道,什么属性产品 模型相关,我建议您创建一些方法来提取上下文。
请随时评论此方法中的任何潜在威胁。
我正在处理一个挑战,我想创建一个多态模型本地化。这将包括长文本列,例如 default、en_us、de_de,...
现在想象另一个模型 Product。产品通常具有文本属性,如名称、描述等……这部分来了,我可以在其中使用 Localizations 的多态关系。 localizations table 应该具有这种类型的结构:
id | localizable_id | localizable_type | default | en_us | de_de |
---|---|---|---|---|---|
1 | 25 | App\Model\Product | Phone | null | Telefon |
2 | 25 | App\Model\Product | The best phone on the market without an audio jack | null | Das beste Telefon auf dem Markt ohne Audioanschluss |
3 | 15 | App\Model\Article | Top 10 products | null | Top 10 Produkte |
4 | 15 | App\Model\Job | Salesman | null | Verkäufer |
如果我希望 Product 具有可本地化的名称和描述,那么根据 Laravel 文档,您应该会在我的代码中看到类似这样的内容:
class Product extends Model
{
public function name() // Expecting model with default attribute 'Phone'
{
return $this->morphOne(Localization::class,'localizable');
}
public function description() // Expecting model with default attribute 'The best phone on the market without an audio jack'
{
return $this->morphOne(Localization::class,'localizable');
}
}
无论多么明显,它都不会正常工作,因为我不能指望两个相同的方法可以 return 不同的值。
另一方面,如果我想遵循 Laravel 的约定,本地化 模型应该如下所示:
class Localizable extends Model
{
public function localizable()
{
return $this->morphTo(__FUNCTION__,'localizable_type','localizable_id');
}
}
正如你在上面的例子中看到的,多态关系与我需要的相反。问题是我想要一个包含所有字符串的 table,这些字符串可能会被翻译 (localizables) 并在许多其他模型中使用它们,而不仅仅是 Product 、喜欢商店、工作、文章、...
我需要以某种方式实现区分,不仅是 localizable_types 之间的区别,还有 column/attribute 应该相关的区别。实现此目标的最佳方法是什么?
我终于找到了满足我期望的解决方法。尽管这不是最漂亮的(或Laravel)方式。我从 GitHub here.
上发现的类似斗争中得到启发此解决方案的重点是覆盖 Product 的 getMorphClass()
方法,该方法用于确定 *_type
列值。我的产品型号:
class Product extends Model
{
protected $morphClass = null; // Create an attribute that will not be saved into the DB
public function getMorphClass() // Method for determinating the type value
{
return $this->morphClass? : self::class;
}
public function name()
{
$this->morphClass = self::class . '.name'; // App\Models\Product.name
return $this->morphOne(Localization::class, 'localizable');
}
public function description()
{
$this->morphClass = self::class . '.description'; // App\Models\Product.description
return $this->morphOne(Localization::class, 'description');
}
}
上面的修改会影响,Laravel 如何将 Localization 保存到数据库。现在,需要从关系的另一端完成一些工作 - custom polymorphic types。在 app/Providers/AppServiceProvider.php
文件中更新您的 AppServiceProvider
class。您必须将 morphMap 添加到 boot()
方法:
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
// ...any of your previous modifications...
Relation::morphMap([
Product::class . '.name' => Product::class,
Product::class . '.description' => Product::class,
]);
}
但请注意: 一旦您决定在您的模型中使用多个相同类型的变形,您必须在每个动态方法中更改受保护的属性 $morphClass
调用 morphOne()
或 morphMany()
。否则,您可能会将描述保存到名称中,反之亦然。
此外,当您将在另一端使用 morphTo()
方法时,您将只会获得 Product - 如果您需要知道,什么属性产品 模型相关,我建议您创建一些方法来提取上下文。
请随时评论此方法中的任何潜在威胁。