You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue+Laravel中如何正确展示存在关联关系的两个模型数据

问题修复方案

核心错误梳理

你当前代码有4个直接导致效果不符合预期的问题:

  • Book模型关联定义错误:原本写的是关联Project模型的project()方法,和你要调用的with('author')预加载完全不匹配,关联根本不会生效
  • 前端取值路径错误:Eloquent预加载的关联数据会挂载在和关联方法同名的属性上,你写的item.author_id.name逻辑完全错误——author_id是存储作者ID的数字型字段,不是对象,无法读取name、mobile属性
  • 数据维度错误:你直接遍历所有单本书籍数据,自然每本书都会渲染一个独立的作者工具栏,要实现同作者书籍归到同一卡片的效果,必须先按作者维度对数据分组
  • 初始代码逻辑错误:原控制器写了两次return,第一次返回作者列表后,后续查询书籍的代码属于永远不会执行的死代码

分步修复

1. 修正Laravel后端代码

首先修正Book模型的关联定义,替换掉原来错误的project方法:

// app/Models/Book.php
public function author()
{
    // 外键字段为author_id,符合Laravel命名约定,第二个参数可省略
    return $this->belongsTo(Author::class);
}

然后修正控制器代码,去掉死代码,查询时预加载作者关联,同时直接按作者分组返回数据,减少前端处理逻辑:

// 控制器方法
public function index()
{
    $books = Book::with('author')->get();
    // 按作者ID分组,聚合同作者的所有书籍
    $groupedData = $books->groupBy(fn($book) => $book->author->id)
        ->map(fn($items) => [
            'author' => $items->first()->author,
            'book_list' => $items
        ])->values();

    return response()->json([
        'grouped_books' => $groupedData
    ], 200);
}

2. 修正Vuetify前端代码

调整遍历逻辑,外层遍历作者分组,每个分组渲染一个作者标题栏,内层遍历当前作者下的所有书籍渲染标签:

<v-row dense>
  <v-col 
    cols="4" md="4" sm="12" 
    v-for="group in grouped_books" 
    :key="group.author.id"
  >
    <v-toolbar color="#1565C0" dark dense>
      <v-toolbar-title>{{ group.author.name }}|{{ group.author.mobile }}</v-toolbar-title>
    </v-toolbar>
    <v-card class="mb-5">
      <v-card-text>
        <v-chip 
          class="ma-2" 
          outlined 
          label 
          color="#1565C0"
          v-for="book in group.book_list"
          :key="book.id"
        >
          {{ book.title }}
        </v-chip>
      </v-card-text>
    </v-card>
  </v-col>    
</v-row>

如果不想在后端做分组,也可以前端拿到带author关联的书籍列表后,用reduce方法按author.id做分组,逻辑和后端处理完全一致。


内容的提问来源于stack exchange,提问作者Edson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 22:57:09