Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

spook1's avatar

How can I add file in api crud with vue frontend form

Bein a noob, learning laravel and Vue, I stumble upon the problem where I need to mix strings and files in a single request.

Following a tutorial I managed to create a text based CRUD, based on an api and VUe frontend. Users can create cards with a title and a description.

I want to make a game where they also add a picture to the card. Now I cannot figure out how to get the request, containing text and image-file to vue methods. I intend to save the image name to the database, using a fetch('cards/api') and save the file itself to the storage/images folder.

I cannot figure out how get hold of the image-file and save, while sending the filename to the api. I am lacking the full understanding of content types and handling of the requests...

I tried to implement some examples I found on the fora, but when I change the content type in the fetch-methods breaks the existing, working, application where I at least can add, edit, and delete the text records.. Cannot figure out how to make a request with both the text fields and the image file, like I was used in standard laravel applications.

Can any help me shed some light on this?

My vue component is:

template>
<div>
    <h2>Cards</h2>
    <form @submit.prevent="addCard" class="mb-3"> 
        <div class="form-group">
            <input type="text" class="form-control" placeholder="title" v-model="card.title">
        </div>

        <div class="form-group">
            <textarea  class="form-control" placeholder="description" v-model="card.description">
            </textarea>
        </div>

        <div>
		    <input type="file" v-on:change="onFileChange" id="file_picture_input">
        </div>

        <button type="submit" class="btn btn-primary btn-block" style="color:white">Save</button>
    </form>
    <div class="card card-body mb-2" v-for="card in cards" v-bind:key="card.id">   
        <h3> {{card.title}}   </h3>
        <p> {{card.description}}</p>
        <img class="img-circle" style="width:150px" v-bind:src="card.picture" alt="Card Image">
        <button @click="deleteCard(card.id)" class="btn btn-danger m-1" style="width:100px;color:white">  Delete  </button>
        <button @click="editCard(card)" class="btn btn-warning m-1" style="width:100px;color:black">  Edit  </button>

    </div>
</div>
</template>

<script>

export default {
    mounted(){
        console.log('component mounted')
    },
    data() {
        return{
            cards:[],
            card:{
                id:'',
                title:'',
                description:'',
                picture:'',
                file:''
            },
            card_id:'',
            edit:false
        }

    },
    created(){
        this.fetchCards();
    },
    methods: {
        fetchCards(){
            fetch('api/cards')
                .then(res => res.json())
                .then(res=>{
                   console.log(res);
                   this.cards = res;
                })
        },
        deleteCard(id){
            if(confirm('Are you sure?')){
                fetch(`api/cards/${id}`, {
                    method: 'delete'
                })
                .then(res => res.json)
                .then(data => {
                    alert('Card removed');
                    this.fetchCards();
                } )
                .catch(err => console.log(err));
            }

        },
        addCard(){
            if(this.edit === false){
                //add
                fetch(`api/cards`, {
                    method: 'post',
                    body:JSON.stringify(this.card),
                    headers:{
                        'content-type': 'application/json',
                    }
                })
                .then(res => res.json)
                .then(data => {
                    this.card.title='';
                    this.card.description='';
                    alert('Card added');
                    this.fetchCards();
                })
                .catch(err => console.log(err));
            } else{
                //update
                fetch(`api/cards/${this.card.card_id}`, {
                    method: 'put',
                    body:JSON.stringify(this.card),
                    headers:{
                        'content-type': 'application/json'
                    }
                })
                .then(res => res.json)
                .then(data => {
                    this.card.title='';
                    this.card.description='';
                    alert('Card updated');
                    this.fetchCards();
                })
                .catch(err => console.log(err));
            }
        },
        editCard(card){
            this.edit=true;
            this.card.id=card.id;
            this.card.card_id=card.id;
            this.card.title=card.title;
            this.card.description=card.description;
        },
         onFileChange(e){
                console.log(e.target.files[0]);
                this.file = e.target.files[0];
            }
    }
}
</script>

The Controller:


<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Card;
use Intervention\Image\Facades\Image;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Str;

class CardController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
       $cards =  Card::all()->toArray();
       
       return array_reverse($cards);
    
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {

        //validate data first
        $request->validate([
            'description'=> 'required'
        ]);
        
        $card = $request->all();
        
        if ($request->hasFile('file_picture_input')){

            $this->validate($request, ['file_picture_input' => 'max:2000'], [__('Maximum file size of 2MB allowed')]);
            
    		$image_name = strval(time().Str::random(5)).'.png'; 
            
                                        
            $request->file->move(storage_path('images'),$image_name);
           
                                        
            $path = storage_path('images'.$image_name);
            $card['picture'] = $path;
        }
        else {
            $card['picture'] = 'img/profile/default_avatar.png';
        }
        
        
        //create a post
        return Card::create($card);



    
       
    }

    /**
     * Display the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function show($id)
    {
        return Card::find($id);
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, $id)
    {
        $card =  Card::find($id); 
        $card->update($request->all());
        return $card;
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function destroy($id)
    {
        return Card::destroy($id);
    }
}


0 likes
9 replies
spook1's avatar

That is what I t hought. When I change it, existing code breaks... You know why?

apex1's avatar

What breaks? The vue code or laravel? Are you getting errors? any more information

spook1's avatar

No errors, but adding does not work anymore...

apex1's avatar

I don't know if this will fix it but .then(res => res.json) should be .then(res => res.json()), everywhere in your code.

1 like
spook1's avatar

this helped, now I can get the form-data in the request. Thanks!

spook1's avatar

Thanks for you interest. I studied all the links and tried many many different approaches. It seems that that I can do a successful request, still the file and data ( formData ) are not correctly processed by the backend.

Will place a new question to adress that..

Please or to participate in this conversation.