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

Vue.js嵌套数组无法保存至Laravel后端问题求助

问题:Vue提交FormData到Laravel时无法保存关联的特性与技术数据

我开发了一个用于创建项目作品集的Vue组件,通过FormData对象向Laravel后端提交数据。每个项目关联多个项目特性与技术,同时包含一张图片。目前项目信息及图片路径可正常保存到projects表,但无法遍历特性和技术的子对象并将其保存到对应的数据表。


我的Vue组件代码

模板部分

<template>
  <div>
    <div class="row">
      <div class="col-md-10">
        <div class="form-group">
          <label>Project Title:</label>
          <input type="text" name="project_title" v-validate="'required|min:3|max:255'" class="form-control" v-model="project.project_title" />
          <div class="help-block alert alert-danger" v-show="errors.has('project_title')">
            {{errors.first('project_title')}}
          </div>
        </div>
      </div>
    </div>
    <div class="row">
      <div class="col-md-10">
        <div class="form-group">
          <label>Tag line:</label>
          <input type="text" name="tag_line" v-validate="'max:255'" class="form-control" v-model="project.tag_line" />
          <div class="help-block alert alert-danger" v-show="errors.has('tag_line')">
            {{errors.first('tag_line')}}
          </div>
        </div>
      </div>
    </div>
    <div class="row">
      <div class="col-md-10">
        <div class="form-group">
          <label>Description:</label>
          <textarea v-model="project.project_desc" name="project_desc" class="form-control" v-validate="'required|min:30'"></textarea>
          <div class="help-block alert alert-danger" v-show="errors.has('project_desc')">
            {{errors.first('project_desc')}}
          </div>
        </div>
      </div>
    </div>
    <div class="row">
      <div class="col-md-10">
        <div class="form-group">
          <label>Image:</label>
          <span class="help-block"> Only images of format JPG,JPEG or PNG allowed. </span>
          <input type="file" name ="project_thumb_nail" id="project_thumbnail" class="form-control" v-validate="'required|image|mimes:image/jpeg,image/jpg,image/png|size:10000'" @change="getProjectImage" />
          <div class="help-block alert alert-danger" v-show="errors.has('project_thumbnail')">
            {{errors.first('project_thumbnail')}}
          </div>
        </div>
      </div>
    </div>
    <div class="row">
      <div class="col-md-10">
        <div class="form-group">
          <label>URL:</label>
          <input type="url" name="url" v-validate="'url'" class="form-control" v-model="project.url" />
          <div class="help-block alert alert-danger" v-show="errors.has('url')">
            {{errors.first('url')}}
          </div>
        </div>
      </div>
    </div>
    <hr>
    <h4>Features <small>Add Key Features in this project</small></h4>
    <ol>
      <li v-for="f in features">{{f.feature}}</li>
    </ol>
    <hr>
    <div class="row">
      <div class="col-md-10">
        <div class="form-group" v-for="(feature, index) in features" :key="index">
          <input v-model="feature.feature" :name="`features[${index}][feature]`" v-validate="'required|max:255'">
          <div class="help-block alert alert-danger" v-show="errors.has(`features[${index}][feature]`)">
            {{errors.first(`features[${index}][feature]`)}}
          </div>
          <br>
          <hr>
        </div>
        <button @click="AddFeature" class="btn btn-default"> Add Feature </button>
      </div>
    </div>
    <br />
    <hr>
    <h4>Technologies</h4>
    <hr>
    <h4><small>Select Technologies used in this project</small></h4>
    <div class="row">
      <div class="col-md-10">
        <div v-for="(tech, index) in technologies" :key="tech.id">
          <input type="checkbox" :value="tech.id" :id="`tech-${tech.id}`" v-model="selectedTechs" />
          <label :for="`tech-${tech.id}`">{{tech.technology}}</label>
        </div>
      </div>
    </div>
    <br>
    <hr>
    <button class="btn btn-success" @click="addProject">Save</button>
  </div>
</template>

脚本部分

<script>
export default {
  name:'CreateProject',
  data(){
    return{
      project:{
        project_title: '',
        tag_line: '',
        project_desc: '',
        url: ''
      },
      features:[],
      technologies:[],
      selectedTechs:[],
      project_picture:null
    }
  },
  beforeMount: function() {
    this.fetchTechnologies();
  },
  methods: {
    fetchTechnologies() {
      return this.axios.get('technologies').then((res)=>{
        this.technologies=res.data;
      })
    },
    getProjectImage(event) {
      let files = event.target.files;
      if (files.length) this.project_picture = files[0];
    },
    AddFeature() {
      this.features.push({ feature: '' });
    },
    addProject(){
      const fd = new FormData();
      // 添加项目基础字段
      fd.append('project_title', this.project.project_title);
      fd.append('tag_line', this.project.tag_line);
      fd.append('project_desc', this.project.project_desc);
      fd.append('url', this.project.url);
      // 添加图片
      if(this.project_picture){
        fd.append('project_thumbnail', this.project_picture, this.project_picture.name);
      }
      // 手动添加特性数组到FormData
      this.features.forEach((feature, index) => {
        if(feature.feature.trim()){
          fd.append(`features[${index}][feature]`, feature.feature);
        }
      });
      // 添加选中的技术ID数组
      this.selectedTechs.forEach(techId => {
        fd.append('technologies[]', techId);
      });

      // 发送请求到后端
      this.axios.post('/projects', fd, {
        headers: {
          'Content-Type': 'multipart/form-data'
        }
      }).then(res => {
        console.log('Project saved successfully!');
        // 这里可以添加表单重置或页面跳转逻辑
      }).catch(err => {
        console.error('Error saving project:', err);
      });
    }
  }
}
</script>

问题分析与修正点

  1. FormData的数组处理限制:
    FormData不会自动序列化数组或嵌套对象,必须手动遍历特性和选中的技术,将它们逐个添加到FormData中,后端才能正确解析。

  2. AddFeature方法的逻辑错误:
    你之前的代码把整个features数组嵌套推入project.features,导致数据结构混乱,直接push单个空特性对象即可。

  3. 技术选择的交互优化:
    项目通常需要多选技术,所以把radio按钮改成checkbox;同时简化数据结构,用selectedTechs数组直接存储选中的技术ID,无需额外的selectedTechnologies数组。

  4. 表单验证的动态name适配:
    特性输入框的name要动态生成(如features[0][feature]),确保Laravel能正确识别数组结构,同时验证规则也要对应这个动态name。


Laravel后端处理示例(控制器代码)

假设你已建立Project、Feature、Technology模型,且Project与Feature为一对多关系,Project与Technology为多对多关系:

<?php

namespace App\Http\Controllers;

use App\Models\Project;
use Illuminate\Http\Request;

class ProjectController extends Controller
{
    public function store(Request $request)
    {
        // 验证请求数据
        $validated = $request->validate([
            'project_title' => 'required|min:3|max:255',
            'project_desc' => 'required|min:30',
            'project_thumbnail' => 'required|image|mimes:jpeg,jpg,png|max:10000',
            'features.*.feature' => 'required|max:255',
            'technologies' => 'array|exists:technologies,id'
        ]);

        // 处理图片上传
        $imagePath = $request->file('project_thumbnail')->store('project_images', 'public');

        // 创建项目
        $project = Project::create([
            'project_title' => $validated['project_title'],
            'tag_line' => $validated['tag_line'] ?? '',
            'project_desc' => $validated['project_desc'],
            'url' => $validated['url'] ?? '',
            'project_thumb_nail' => $imagePath
        ]);

        // 保存项目特性
        foreach($validated['features'] as $featureData){
            $project->features()->create($featureData);
        }

        // 关联项目与技术(多对多)
        if(isset($validated['technologies'])){
            $project->technologies()->attach($validated['technologies']);
        }

        return response()->json(['message' => 'Project created successfully'], 201);
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:53:57