Laravel 6 API RESTful 过滤了多对多关系
Laravel 6 API RESTful with many to many relationship filtered
我正在使用 laravel 6 开发多语言 API,我的数据库中出现了这种情况:
Categories
id
other not relevants fields
Languages:
id
name
code
Category_Language
id
language_id
category_id
name --> this is the name of the category in the specific language.
现在我有 2 个模型,第一个用于类别
class Category extends Model
{
public function languages()
{
return $this->belongsToMany('App\Models\v1\Language')->withTimestamps()->withPivot('name');
}
}
和第二个模型
class Language extends Model
{
protected $fillable = ['code', 'name', 'image_id', 'enabled'];
public function categories() {
return $this->belongsToMany('App\Models\v1\Category')->withTimestamps()->withPivot('name');
}
}
在我的逻辑中(我正在使用服务模式)我在创建类别时使用了这种方法,每次我像这样传递一个 JSON 对象时:
{
"names": [
{
"languageId": 1,
"name": "Hello"
},
{
"languageId": 2,
"name": "Hola"
}
]
}
首先,我创建了一个类别(验证语言的 ID 是否真的存储在数据库中),然后使用多对多 laravel 的能力,我将语言和名称附加到类别模型中,例如这个:
foreach($request->names as $name) {
$category->languages()->attach($name['languageId'], ['name' => $name['name']]);
}
现在这似乎工作得很好,并且在不使用过滤语言的情况下检索所有类别是很好的 API 像这样的资源:
public function toArray($request)
{
$category = [];
$category['id'] = $this->id;
$category['languages'] = [];
$category['languages'] = $this->languages->map(function ($language) {
return [
'languageId' => $language->id,
'languageCode' => $language->code,
'languageName' => $language->name,
'categoryName' => $language->pivot->name,
];
});
return $category;
}
这是 3 种语言的 3 个类别的输出(伪造数据):
array:3 [
"data" => array:3 [
0 => array:4 [
"id" => 1
"languages" => array:3 [
0 => array:4 [
"languageId" => 1
"languageCode" => "pa"
"languageName" => "Kacey Trantow"
"categoryName" => "quasi"
]
1 => array:4 [
"languageId" => 2
"languageCode" => "ne"
"languageName" => "Mr. Alexandre Heathcote"
"categoryName" => "perferendis"
]
2 => array:4 [
"languageId" => 3
"languageCode" => "kj"
"languageName" => "Mr. Misael Robel"
"categoryName" => "repudiandae"
]
]
"imageUrl" => null
"enabled" => true
]
1 => array:4 [
"id" => 2
"languages" => array:3 [
0 => array:4 [
"languageId" => 1
"languageCode" => "pa"
"languageName" => "Kacey Trantow"
"categoryName" => "non"
]
1 => array:4 [
"languageId" => 2
"languageCode" => "ne"
"languageName" => "Mr. Alexandre Heathcote"
"categoryName" => "vitae"
]
2 => array:4 [
"languageId" => 3
"languageCode" => "kj"
"languageName" => "Mr. Misael Robel"
"categoryName" => "suscipit"
]
]
"imageUrl" => null
"enabled" => true
]
2 => array:4 [
"id" => 3
"languages" => array:3 [
0 => array:4 [
"languageId" => 1
"languageCode" => "pa"
"languageName" => "Kacey Trantow"
"categoryName" => "molestiae"
]
1 => array:4 [
"languageId" => 2
"languageCode" => "ne"
"languageName" => "Mr. Alexandre Heathcote"
"categoryName" => "esse"
]
2 => array:4 [
"languageId" => 3
"languageCode" => "kj"
"languageName" => "Mr. Misael Robel"
"categoryName" => "beatae"
]
]
"imageUrl" => null
"enabled" => true
]
]
现在,问题是当我想过滤只传递一种特定语言的类别时,这是一个典型的用例,当用户在导航过程中只使用一种语言时,如果我只想要一种语言(和数据透视表中的 1 个名称)类的关系)我需要什么样的操作?
我已经用特定的过滤器组织了服务,并为任何集合排序,但这种需求似乎让我发疯!
所以这是我构建过滤器、排序和动态包含的最终 getCategories 方法:
-
成像所以有这样的查询字符串:
http://localhost:8000/api/v1/categories?orderBy=id:asc&include=language&language.code=EN
我想要这样的回复:
array:3 [
"data" => array:3 [
0 => array:4 [
"id" => 1
"languages" => array:3 [
0 => array:4 [
"languageId" => 1
"languageCode" => "EN"
"languageName" => "English"
"categoryName" => "quasi"
]
]
]
1 => array:4 [
"id" => 2
"languages" => array:3 [
0 => array:4 [
"languageId" => 1
"languageCode" => "EN"
"languageName" => "English"
"categoryName" => "non"
]
]
]
2 => array:4 [
"id" => 3
"languages" => array:3 [
0 => array:4 [
"languageId" => 1
"languageCode" => "EN"
"languageName" => "English"
"categoryName" => "molestiae"
]
]
]
]
我需要这样的东西,但一般来说,因为我们有其他实体对多语言系统使用相同的逻辑。
澄清我想要的是像这样的连接查询:
SELECT *
FROM categories ca INNER JOIN category_language cl ON ca.id =cl.category_id
INNER JOIN languages lan ON lan.id = cl.language_id
WHERE lan.code = 'EN';
您将检索仅以英语显示的类别列表...
感谢您的建议和帮助!
您可以像这样加入 eloquent:
$language_code=$请求->输入('language_code');
$values=Category::query()->join('category_language','category_language.category_id','=','categories.id')
->join('languages','languages.id','=','category_language.language_id')
->where('languages.code','=',$language_code)
->select('categories.*','languages.code')
->orderBy('categories.id')->get();
我认为这不是最优雅的方法,但我已经通过一些变通办法解决了。
首先,我没有在查询字符串中传递语言代码,而是在 Header 参数中使用 X-localization 参数,如 tutorial for laravel with multilanguages.[=14= 中所述]
其次,我在检索所有资源时总是return在服务方法中查询->paginate():
public function getCategories(Request $request)
{
$sort = $this->buildSort($request->sort ?? '', 'id', 'asc');
$where = $this->buildWhere($request->where ?? '');
$includes = $this->buildWith($request->include ?? '');
$query = Category::orderBy($sort[0], $sort[1]);
if (!empty($include)) {
$query = $query->with($includes);
}
if (!empty($where)) {
$query = $query->where($where);
}
return $query->paginate();
}
最后,我将请求 header 拦截到类别 API 资源中,如果设置了 X-localization 参数,我将在地图中过滤语言数组,如下所示:
public function toArray($request)
{
$languageCode = $request->header('X-localization') ?? null;
$category = [];
$category['id'] = $this->id;
$category['languages'] = [];
$languages = $this->languages;
if ($languageCode) {
$languages = $languages->where('code', '=', $languageCode);
}
$languages = $languages->map(function ($language) use ($languageCode, $category) {
return [
'languageId' => $language->id,
'languageCode' => $language->code,
'languageName' => $language->name,
'categoryName' => $language->pivot->name,
];
});
$category['languages'] = $languages;
return $category;
}
结果正是我想要的,而没有在我的项目中使用纯 SQL(假数据):
array:3 [
"data" => array:3 [
0 => array:4 [
"id" => 1
"languages" => array:1 [
0 => array:4 [
"languageId" => 1
"languageCode" => "ho"
"languageName" => "Dott. Orfeo Sartori"
"categoryName" => "rem"
]
]
"imageUrl" => null
"enabled" => true
]
1 => array:4 [
"id" => 2
"languages" => array:1 [
0 => array:4 [
"languageId" => 1
"languageCode" => "ho"
"languageName" => "Dott. Orfeo Sartori"
"categoryName" => "accusamus"
]
]
"imageUrl" => null
"enabled" => true
]
2 => array:4 [
"id" => 3
"languages" => array:1 [
0 => array:4 [
"languageId" => 1
"languageCode" => "ho"
"languageName" => "Dott. Orfeo Sartori"
"categoryName" => "totam"
]
]
"imageUrl" => null
"enabled" => true
]
]
"links" => array:4 [
"first" => "http://localhost/api/v1/categories?page=1"
"last" => "http://localhost/api/v1/categories?page=1"
"prev" => null
"next" => null
]
"meta" => array:7 [
"current_page" => 1
"from" => 1
"last_page" => 1
"path" => "http://localhost/api/v1/categories"
"per_page" => 15
"to" => 3
"total" => 3
]
]
进程已完成,退出代码为 1
我正在使用 laravel 6 开发多语言 API,我的数据库中出现了这种情况:
Categories
id
other not relevants fields
Languages:
id
name
code
Category_Language
id
language_id
category_id
name --> this is the name of the category in the specific language.
现在我有 2 个模型,第一个用于类别
class Category extends Model
{
public function languages()
{
return $this->belongsToMany('App\Models\v1\Language')->withTimestamps()->withPivot('name');
}
}
和第二个模型
class Language extends Model
{
protected $fillable = ['code', 'name', 'image_id', 'enabled'];
public function categories() {
return $this->belongsToMany('App\Models\v1\Category')->withTimestamps()->withPivot('name');
}
}
在我的逻辑中(我正在使用服务模式)我在创建类别时使用了这种方法,每次我像这样传递一个 JSON 对象时:
{
"names": [
{
"languageId": 1,
"name": "Hello"
},
{
"languageId": 2,
"name": "Hola"
}
]
}
首先,我创建了一个类别(验证语言的 ID 是否真的存储在数据库中),然后使用多对多 laravel 的能力,我将语言和名称附加到类别模型中,例如这个:
foreach($request->names as $name) {
$category->languages()->attach($name['languageId'], ['name' => $name['name']]);
}
现在这似乎工作得很好,并且在不使用过滤语言的情况下检索所有类别是很好的 API 像这样的资源:
public function toArray($request)
{
$category = [];
$category['id'] = $this->id;
$category['languages'] = [];
$category['languages'] = $this->languages->map(function ($language) {
return [
'languageId' => $language->id,
'languageCode' => $language->code,
'languageName' => $language->name,
'categoryName' => $language->pivot->name,
];
});
return $category;
}
这是 3 种语言的 3 个类别的输出(伪造数据):
array:3 [
"data" => array:3 [
0 => array:4 [
"id" => 1
"languages" => array:3 [
0 => array:4 [
"languageId" => 1
"languageCode" => "pa"
"languageName" => "Kacey Trantow"
"categoryName" => "quasi"
]
1 => array:4 [
"languageId" => 2
"languageCode" => "ne"
"languageName" => "Mr. Alexandre Heathcote"
"categoryName" => "perferendis"
]
2 => array:4 [
"languageId" => 3
"languageCode" => "kj"
"languageName" => "Mr. Misael Robel"
"categoryName" => "repudiandae"
]
]
"imageUrl" => null
"enabled" => true
]
1 => array:4 [
"id" => 2
"languages" => array:3 [
0 => array:4 [
"languageId" => 1
"languageCode" => "pa"
"languageName" => "Kacey Trantow"
"categoryName" => "non"
]
1 => array:4 [
"languageId" => 2
"languageCode" => "ne"
"languageName" => "Mr. Alexandre Heathcote"
"categoryName" => "vitae"
]
2 => array:4 [
"languageId" => 3
"languageCode" => "kj"
"languageName" => "Mr. Misael Robel"
"categoryName" => "suscipit"
]
]
"imageUrl" => null
"enabled" => true
]
2 => array:4 [
"id" => 3
"languages" => array:3 [
0 => array:4 [
"languageId" => 1
"languageCode" => "pa"
"languageName" => "Kacey Trantow"
"categoryName" => "molestiae"
]
1 => array:4 [
"languageId" => 2
"languageCode" => "ne"
"languageName" => "Mr. Alexandre Heathcote"
"categoryName" => "esse"
]
2 => array:4 [
"languageId" => 3
"languageCode" => "kj"
"languageName" => "Mr. Misael Robel"
"categoryName" => "beatae"
]
]
"imageUrl" => null
"enabled" => true
]
]
现在,问题是当我想过滤只传递一种特定语言的类别时,这是一个典型的用例,当用户在导航过程中只使用一种语言时,如果我只想要一种语言(和数据透视表中的 1 个名称)类的关系)我需要什么样的操作?
我已经用特定的过滤器组织了服务,并为任何集合排序,但这种需求似乎让我发疯!
所以这是我构建过滤器、排序和动态包含的最终 getCategories 方法:
-
成像所以有这样的查询字符串:
http://localhost:8000/api/v1/categories?orderBy=id:asc&include=language&language.code=EN
我想要这样的回复:
array:3 [
"data" => array:3 [
0 => array:4 [
"id" => 1
"languages" => array:3 [
0 => array:4 [
"languageId" => 1
"languageCode" => "EN"
"languageName" => "English"
"categoryName" => "quasi"
]
]
]
1 => array:4 [
"id" => 2
"languages" => array:3 [
0 => array:4 [
"languageId" => 1
"languageCode" => "EN"
"languageName" => "English"
"categoryName" => "non"
]
]
]
2 => array:4 [
"id" => 3
"languages" => array:3 [
0 => array:4 [
"languageId" => 1
"languageCode" => "EN"
"languageName" => "English"
"categoryName" => "molestiae"
]
]
]
]
我需要这样的东西,但一般来说,因为我们有其他实体对多语言系统使用相同的逻辑。
澄清我想要的是像这样的连接查询:
SELECT *
FROM categories ca INNER JOIN category_language cl ON ca.id =cl.category_id
INNER JOIN languages lan ON lan.id = cl.language_id
WHERE lan.code = 'EN';
您将检索仅以英语显示的类别列表...
感谢您的建议和帮助!
您可以像这样加入 eloquent:
$language_code=$请求->输入('language_code');
$values=Category::query()->join('category_language','category_language.category_id','=','categories.id')
->join('languages','languages.id','=','category_language.language_id')
->where('languages.code','=',$language_code)
->select('categories.*','languages.code')
->orderBy('categories.id')->get();
我认为这不是最优雅的方法,但我已经通过一些变通办法解决了。
首先,我没有在查询字符串中传递语言代码,而是在 Header 参数中使用 X-localization 参数,如 tutorial for laravel with multilanguages.[=14= 中所述]
其次,我在检索所有资源时总是return在服务方法中查询->paginate():
public function getCategories(Request $request)
{
$sort = $this->buildSort($request->sort ?? '', 'id', 'asc');
$where = $this->buildWhere($request->where ?? '');
$includes = $this->buildWith($request->include ?? '');
$query = Category::orderBy($sort[0], $sort[1]);
if (!empty($include)) {
$query = $query->with($includes);
}
if (!empty($where)) {
$query = $query->where($where);
}
return $query->paginate();
}
最后,我将请求 header 拦截到类别 API 资源中,如果设置了 X-localization 参数,我将在地图中过滤语言数组,如下所示:
public function toArray($request)
{
$languageCode = $request->header('X-localization') ?? null;
$category = [];
$category['id'] = $this->id;
$category['languages'] = [];
$languages = $this->languages;
if ($languageCode) {
$languages = $languages->where('code', '=', $languageCode);
}
$languages = $languages->map(function ($language) use ($languageCode, $category) {
return [
'languageId' => $language->id,
'languageCode' => $language->code,
'languageName' => $language->name,
'categoryName' => $language->pivot->name,
];
});
$category['languages'] = $languages;
return $category;
}
结果正是我想要的,而没有在我的项目中使用纯 SQL(假数据):
array:3 [
"data" => array:3 [
0 => array:4 [
"id" => 1
"languages" => array:1 [
0 => array:4 [
"languageId" => 1
"languageCode" => "ho"
"languageName" => "Dott. Orfeo Sartori"
"categoryName" => "rem"
]
]
"imageUrl" => null
"enabled" => true
]
1 => array:4 [
"id" => 2
"languages" => array:1 [
0 => array:4 [
"languageId" => 1
"languageCode" => "ho"
"languageName" => "Dott. Orfeo Sartori"
"categoryName" => "accusamus"
]
]
"imageUrl" => null
"enabled" => true
]
2 => array:4 [
"id" => 3
"languages" => array:1 [
0 => array:4 [
"languageId" => 1
"languageCode" => "ho"
"languageName" => "Dott. Orfeo Sartori"
"categoryName" => "totam"
]
]
"imageUrl" => null
"enabled" => true
]
]
"links" => array:4 [
"first" => "http://localhost/api/v1/categories?page=1"
"last" => "http://localhost/api/v1/categories?page=1"
"prev" => null
"next" => null
]
"meta" => array:7 [
"current_page" => 1
"from" => 1
"last_page" => 1
"path" => "http://localhost/api/v1/categories"
"per_page" => 15
"to" => 3
"total" => 3
]
]
进程已完成,退出代码为 1