Laravel 6 API RESTful with many to many relationship filtered.

问题描述 投票:0回答: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个模型,第一个是Category的模型

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');
    }
}

在我的逻辑中(我使用的是服务模式),我在创建Category时使用了这种方法,每次我传递一个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种特定的语言,这是一个典型的使用案例,当用户在导航过程中只使用1种语言,所以,如果我只想要1种语言(和1个名称在类别的枢轴关系),我需要什么样的操作?

我已经为任何集合组织了特定的过滤器和排序服务,但这种需求似乎让我抓狂!所以这是我最终的getCategories。

所以这是我最后的getCategories方法,它可以建立过滤器,排序和包括dinamically。

-

成像,所以要有一个这样的查询字符串。

http:/localhost:8000apiv1categories?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'; 

你将检索一个类别列表,只有在英语语言... ...

谢谢你的建议和帮助

php laravel many-to-many laravel-6 laravel-resource
1个回答
0
投票

我不认为这是最优雅的方式,但是,我已经解决了一个小的工作方法。

首先,我不在查询字符串中传递语言代码,而是在Header paramas中使用X-localization params,就像下面解释的那样。多语言的laravel教程.

其次,当检索所有资源时,我总是在service方法中返回query->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();

}

最后,我在Category API Resource中截取请求头,如果设置了X-localization param,我在map中过滤语言数组,就像这样。

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


0
投票

你可以用雄辩的方式来加入。

$language_code=$request->input('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();
© www.soinside.com 2019 - 2024. All rights reserved.