php 如何从Laravel 9中的API资源访问数据透视表

ev7lccsx  于 2023-02-11  发布在  PHP
关注(0)|答案(2)|浏览(170)
    • bounty将在5天后过期**。回答此问题可获得+50声望奖励。St. Jan希望引起更多人对此问题的关注:希望能知道laravel的方式,并找出这是不是我的记忆泄漏到哪里。

我正在使用资源构建一个API。我有一个产品(巧克力蛋糕),它可以链接到一个属性(过敏),该属性具有Properties_property(麸质),每个产品需要以不同的顺序显示。

product    <---[many-to-many] --->   properties
     ^                                      ^
     |                                      | 
[many-to-many]-->  properties_property  --[belongs-to] 
(pivot table)
  position

表格如下所示:

products:
id
name

product_property (first pivot table)
product_id
property_id

properties:
id
name 

properties_properties
id
name

product_properties_property (this is the pivot table witht the value) 
product_id
properties_property_id
position
    • https://localhost/product**的理想JSON输出为:
{
    "product": [{
        "product_id": 1,
        "name": "Choco Cake",
        "properties": [{
            "property_id": 1,
            "name": "Allergies",
            "properties_properties": [{
                "properties_property_id": 1,
                "name": "Gluten",
                "position": 1
            }]
        }]
    }]
}

属性属性模型中有一个属于多个的关系,如下所示:

public function products () {
  return $this->belongsToMany(Product::class)->withPivot('position');
}

我把routes/api.php中的所有产品都放在那里

Route::get('/product', function () {
    return new ProductCollection(Product::all());
});

我有以下资源:* 产品资源、属性资源和属性属性资源 *。这些资源彼此链接,如下所示:

return [
 'product_id' => $this->product_id,
 'name' => $this->name,
 'properties' => ProductsPropertyResource::collection($this->properties)
];

在Properties_property的资源中,我想访问数据透视表的位置字段。我该怎么做呢?理想情况下,我的 * App\Http\Resource\PropertysPropertyResours.php * 应该看起来像这样:

return [
  'properties_property_id' => $this->id,
  'name' => $this->name,
  'position' => $this->products->pivot->position
];

但这会返回"属性[pivot]不存在于此集合中"
我可以在我的PropertysPropertyResource.php中写一个sql来获得它,如下所示:

return [
...
'position' => $this->products->where('id', $this->product_id)->first()->pivot->position
],

这会产生很多额外的负载!问题(我认为)是我想从父资源(ProperiesProperty)而不是像您通常所做的那样从子资源(Product)访问资源中透视表上的数据。难道没有更像Laravel的方法来实现这一点吗?
要求提供的信息:透视表中目前大约有230个连接,所以这应该不是什么大问题。
更新:
我发现了这个问题,我尝试了解决方案:

'position' => $this->whenPivotLoaded ('product_properties_property', function () {
  return $this->pivot->position;
}),

但是这里的position键甚至没有出现在/product端点的Json中。我开始怀疑您是否需要使用放入控制器或routes/api.php文件中的SQL来填充这些值。

rmbxnbpk

rmbxnbpk1#

这是我的答案,我希望我能给你的问题带来一些启发。我已经发布了一个GitHub repository,其中包含了我在这里编写的所有代码的示例。我在www.example.com文件中添加了更多关于复制我的场景的信息。README.md file.

问题:

如何从"属于多个"关系加载透视数据?

我的回答和建议:

下面是我对数据库及其关系的解释。您可以查看所有到存储库的迁移。我在这里的目的不是定义您的结构,而是帮助您了解Laravel是如何工作的。我希望在此之后,您可以根据需要更改、调整或更新您的项目值。

我希望向您提供有关如何与透视表交互以及如何通过转换透视表属性来操纵它们以隐藏和转换数据的信息。Laravel使用关系透视表作为插件。如果您需要此信息,则需要在模型中 * 附加 * 所需透视数据的详细信息。
首先,您可以在任何模型上定义关系,但我选择在Product模型上显示透视数据。下面是模型定义(所有点表示隐藏数据,以简化类定义,但您可以查看整个模型here):

<?php
...
class Product extends Model
{
...

    protected $with = ['properties']; // 6

    public function properties(): BelongsToMany // 1
    {
        return $this->belongsToMany(Property::class) // 2
            ->as('attributes') // 3
            ->using(ProductProperty::class) // 4
            ->withPivot('position'); // 5
    }
}

让我们来解释一下这里发生了什么:

  1. properties方法表示产品与其属性之间的关系。
    1.由于Product是父对象,因此可以使用hasMany关系表示此关系,但不允许关联透视表。要连接 * Pivot * 表,必须将此关系定义为BelongsToMany关系。这将解锁示例中指定的所有其他方法。
  2. as方法允许您重命名关系。默认情况下,Laravel通过pivot关键字调用任何透视加载的数据。在本例中,我们使用attributes值重命名它。
  3. using方法允许您使用定义的透视模型来转换和修改从透视表检索的数据的行为。
  4. withPivot方法定义透视关系中所有额外的必需字段。默认情况下,Laravel从相关的基本模型加载主键列。在本例中,我从透视表添加position列。
  5. $with保护变量定义了调用Product模型时应该加载哪些关系,在本例中,我在加载任何产品时添加了Product的所有属性。
    接下来,让我们解释一下ProductProperty模型上发生了什么(您可以查看整个模型here):
<?php
...
class ProductProperty extends Pivot // 1
{
    protected $casts = [ // 2
        'position' => 'integer',
    ];

    protected $hidden = [ // 3
        'product_id',
        'property_id',
    ];
}

让我们像以前一样解释:
1.正如你所看到的,ProductProperty类扩展了Pivot类。这一点非常重要。这就是Laravel如何将其标识为透视关系而不是基础模型的。(这就是为什么你可以在Product模型中使用using方法)。

  1. $cast protected属性允许您定义列类型以利用数据转换。有关详细信息,请参阅:Eloquent: Mutators & Casting
  2. $hidden protected属性允许您定义在看到包含此数据透视表模型定义的关系时不希望显示的列的列表。
    现在你知道如何定义透视关系了。但是,我该如何使用它呢?让我们检查一下ProductController的定义(你可以查看整个控制器here):
<?php
...
class ProductController extends Controller
{
    public function index(Request $request)
    {
        $products = Product::all(); // 1
        return new ProductCollection($products); // 2
    }
}

// Returned data:
/**
{
   "data":[
      {
         "id":1,
         "name":"Costa Rica",
         "properties":[
            {
               "id":1,
               "name":"DeepSkyBlue",
               "attributes":{ // By default Laravel use 'pivot' here, but we replace this with the 'attributes' name.
                  "position":8
               }
            },
            {
               "id":2,
               "name":"Azure",
               "attributes":{
                  "position":8
               }
            },
            {
               "id":3,
               "name":"LavenderBlush",
               "attributes":{
                  "position":8
               }
            }
         ]
      },
    ... // Sample of 1 Product with 3 Properties
   ]
}
 */

这里发生了什么:
1.我从数据库调用所有现有的产品,因为我们在Product模型上定义了$with属性,所以所有Product都返回了它们的属性。
1.我将返回 Package 在API资源中的产品,以便将来需要时转换Endpoint结构。雄辩:API资源
好了,就是这样!这里我尽可能简单地介绍了一些基本知识。我希望这些信息能有所帮助。
在这个例子中,我使用Laravel Blueprint来加速项目搭建。你可以在这里阅读更多关于它的信息:Laravel Blueprint

rqqzpn5f

rqqzpn5f2#

您无法访问 * PropertysPropertyResource * 中的透视表 product_properties_property,因为尚不知道加载了哪个关系。
在这种情况下,最好的解决方案是在模型中将您的透视表重命名为类似“attributes”的名称(感谢@Ricardo Vargas提出这个想法):

public function properties_properties()
{
   return $this->belongsToMany(Product::class) 
       ->as('attributes') 
       ->withPivot('position'); 
}

然后创建一个返回所查找属性的 * PropertyPropertyAttributeResource *:

class PropertiesPropertyAttributeResource extends JsonResource
{
    public function toArray($request)
    {
      return [
          'position' => $this->attributes->position,
        ];
    }
}

并将其加载到 * 属性属性资源 * 中:

class ProductsPropertiesPropertyResource extends JsonResource
{
    public function toArray($request)
    {
      return [
        'properties_property_id' => $this->id,
        'name' => $this->name,
        'attributes' => PropertiesPropertyAttributeResource::collection($this->products)
      ];
    }
}

这样,数据透视表的值将在JSON中显示为:

{
    "product": [{
        "product_id": 1,
        "name": "Choco Cake",
        "properties": [{
            "property_id": 1,
            "name": "Allergies",
            "properties_properties": [{
                "properties_property_id": 1,
                "name": "Gluten",
                "attributes" [{
                    "position": 1
                }]
            }]
        }]
    }]
}

注意:在我询问源记录和最终记录之间的2个多对多关系的情况下,您需要创建一个Eloquent对象,该对象包含使用API资源生成输出的正确信息。_PropertysProperty没有默认方法来知道哪个产品发起了请求。

相关问题