0

I am new to Laravel Vue, write some code to CURD function, everything working fine except server side validation. I get 422 with this error message : {"message":"The given data was invalid.","errors":{"iso":["The iso field is required."]}}

Request Payload : {countries: [,…]} countries: [,…] 0: {id: 2, iso: "UK", idd: "44", en_GB: "United Kingom", zh_HK: "UK", zh_CN: "UK", local: "UK", in_use: 1,…} 1: {id: 1, iso: "US", idd: "1", en_GB: "USA", zh_HK: "US", zh_CN: "US", local: "US", in_use: 1,…}

Enviropment : mariaDB 10 PHP 7.3.19, nodejs 12.16.3 composer 1.10.6 laravel 7.6.1 Homestead v10.8.1 virtualbox 6.1 Vue 2.6.11

related codes : countryManager.vue, CountrySwitch.vue, store/index.js, Amdin/CountryController.php, Country.php.

counryManger.vue

<template> 
    <v-container>
        <v-layout>
            <v-flex>
                <v-card class="mx-auto" >
                    <v-system-bar color="indigo darken-2" dark>
                    <v-spacer></v-spacer>
                
                    <v-icon>mdi-window-minimize</v-icon>
                
                    <v-icon>mdi-window-maximize</v-icon>
                
                    <v-icon>mdi-close</v-icon>
                    </v-system-bar>
                
                    <v-toolbar color="indigo" dark>
                        <v-app-bar-nav-icon></v-app-bar-nav-icon>
                    
                        <v-toolbar-title>Country table maintenance</v-toolbar-title>
                    
                        <v-spacer></v-spacer>
                    
                        <v-btn icon>
                            <v-icon>mdi-magnify</v-icon>
                        </v-btn>
                    </v-toolbar>
                    <v-card-text color="indigo darken-2" dark>
                        <div class="flex-table">
                        <div>ISO</div>
                            <div>English</div>
                            <div>Chinese Traditional</div>
                            <div>Chinese Simplified</div>
                            <div>Local Name</div>
                            <div>Idd Dialing Code</div>
                            <div>Remarks</div>
                            <div>
                                <v-icon @click="addCountry()">fa fa-plus-circle</v-icon>
                                <v-icon @click="trashedCountry()">fa fa-recycle</v-icon>
                            </div>
                        </div>
                        <div v-for="(country, index) in countries" :key="country.id" class="flex-table">
                            <div>
                                <div v-if="countryEditingId ==country.id">
                                    <v-text-field v-model="country.iso"></v-text-field>
                                </div>
                                <div v-else>
                                    {{country.iso}}
                                </div>
                            </div>
                            <div>
                                <div v-if="countryEditingId ==country.id">
                                    <v-text-field v-model="country.en_GB" ></v-text-field>
                                </div>
                                <div v-else>
                                    {{country.en_GB}}
                                </div>
                            </div>
                            <div>
                                <div v-if="countryEditingId ==country.id">
                                    <v-text-field v-model="country.zh_HK" ></v-text-field>
                                </div>
                                <div v-else>
                                    {{country.zh_HK}}
                                </div>
                            </div>
                            <div>
                                <div v-if="countryEditingId ==country.id">
                                    <v-text-field v-model="country.zh_CN" ></v-text-field>
                                </div>
                                <div v-else>
                                    {{country.zh_CN}}
                                </div>
                            </div>
                            <div>
                                <div v-if="countryEditingId ==country.id">
                                    <v-text-field v-model="country.local" ></v-text-field>
                                </div>
                                <div v-else>
                                    {{country.local}}
                                </div>
                            </div>
                            <div>
                                <div v-if="countryEditingId ==country.id">
                                    <v-text-field v-model="country.idd" ></v-text-field>
                                </div>
                                <div v-else>
                                    {{country.idd}}
                                </div>
                            </div>
                            <div>
                                <div v-if="countryEditingId ==country.id">
                                    <v-text-field v-model="country.remarks"></v-text-field>
                                </div>
                                <div v-else>
                                    {{country.remarks}}
                                </div>
                            </div>
                            
                            <div>
                                <div v-if="!country.deleted_at">
                                    <div v-if="countryEditingId ==country.id">
                                        <v-btn x-small @click="saveCountries(country)">Save</v-btn>
                                    </div>
                                    <div v-else>
                                        <v-btn x-small @click="setToEditing(country)">Edit</v-btn>
                                    </div>
                                </div>
                                <div  v-if="!country.deleted_at">
                                    <div v-if="countryEditingId ==country.id">
                                        <v-btn x-small @click="resetCountries()">Cancel</v-btn>
                                    </div>
                                    <div v-else>
                                        <v-btn x-small @click="removeCountries(index)">Remove</v-btn>
                                    </div>
                                </div>
                                <div v-else>
                                    <v-btn x-small @click="restoreCountries(index)">Restore</v-btn>
                                </div>
                                
                            </div>
                        </div>
                    </v-card-text>
                    <v-spacer></v-spacer>
                    <v-card-subtitle class="grey">
                        <div class="card-footer text-muted">{{ feedback }}</div>
                        <div v-if="validationErrors"class="card-footer text-muted">{{ validationErrors }}</div>
                    </v-card-subtitle>
                </v-card>
            </v-flex>
        </v-layout>
    </v-container>
</template>

<script>
    export default {
        data(){
            return {
                countryEditingId:''
            };
        },
        computed: {
            validationErrors(){
                return this.$store.state.validationErrors;
            },
            countries(){
                return this.$store.state.countries;
            },
            feedback(){
                return this.$store.state.feedback;
            }
        },
        methods: {
            resetCountries(){
                this.countryEditingId = "";
                this.$store.dispatch('resetCountries');
            },
            setToEditing(country){
                this.countryEditingId = country.id;
            },
            addCountry(){
                this.$store.commit('ADD_COUNTRIES',{
                    id:'',
                    iso:'',
                    en_GB:'',
                    zh_HK:'',
                    zh_CN:'',
                    idd:'',
                    remarks:''
                });
            },
            saveCountries(){
                this.countryEditingId='';
                this.$store.dispatch('saveCountries');
               
            },
            updateCountries($event, property, index){
                this.countryEditingId='';
                this.$store.commit('UPDATE_COUNTRIES',{
                    index,
                    property,
                    value:$event.target.value
                });
            },
        }
    }
</script>
<style lang="scss" scoped>
.flex-table {
    display: grid;
    grid-template-columns: repeat(auto-fill,12%);
    padding:5px;
    &:nth-of-type(2n) {
        background-color: #dedede;
    }
    .actions {
        * {
            padding-right: 10px;
        }
    }
}
</style>

countrySwitch.vue

<template>
<v-app>
  
    <!-- <router-link :to="{name: 'country'}">Country</router-link> -->
    <router-view></router-view>
  </v-app>
</template>

<script>
    import VueRouter from 'vue-router';   
    import CountryManager from './CountryManager.vue';
    import store from '../store';
    Vue.use(VueRouter);

    export default {
        store,
        props: ['countries'],
        created() {
            this.$store.commit('SET_COUNTRIES', _.cloneDeep(this.countries));
        },
        router: new VueRouter({
            mode: 'history',
            base: 'country',
            routes: [
                {
                    path: '/admin/country',
                    name: 'country',
                    component: CountryManager
                },
                {
                    path: '/',
                    redirect: {name: 'country'}
                },
            ]
        })
    }
</script>

store/index.js

import Vue from 'vue';
import Vuex from 'vuex';
import Axios from 'axios';

Vue.use(Vuex);

export default new Vuex.Store({
    state:{
        countries:[],
        feedback: '',
        validationErrors:''
    },
    mutations :{
        SET_COUNTRIES(state, countries){
            state.countries = countries;
        },
        ADD_COUNTRIES(state, countries){
            state.countries.push(countries);
        },
        REMOVE_COUNTRIES(state, index){
            state.countries.splice(index, 1);
        },
        UPDATE_COUNTRIES(state,{index, property, value}){
            state.countries[index][property] = value;
        },
        SET_FEEDBACK(state, feedback){
            state.feedback = feedback;
        },
        SET_ERRORS(state, validationErrors){
            state.validationErrors = validationErrors;
        },
    },
    actions:{
        saveCountries({commit, state}){
            axios.post('/api/countries/upsert',{
                countries: state.countries,
            })
            .then ((res)=>{
                if(res.status == 200){
                    commit('SET_FEEDBACK','Changes Saved');
                    setTimeout(()=> commit('SET_FEEDBACK',''), 5000);
                    commit('SET_COUNTRIES', res.data.countries);
                }
            })
            .catch((error)=>{
                commit('SET_FEEDBACK','Error: Unable update / insert records');
                commit('SET_ERRORS', error.response);
            });
        },
        resetCountries({commit}){
            axios.get('/api/countries/list')
            .then ((res)=>{
                if(res.status == 200){
                    commit('SET_FEEDBACK','Action Cancelled');
                    setTimeout(()=> commit('SET_FEEDBACK',''), 5000);
                    commit('SET_COUNTRIES', res.data.countries);
                };
            });
        },
        removeCountries({commit, state}, index){
            let id = state.countries[index].id;
            if (id > 0){
                axios.delete('/api/countries/'+id)
                    .then((res)=>commit('REMOVE_COUNTRIES',index));
            }
            commit ('REMOVE_COUNTRIES', index)
        },
        restoreCountries({commit, state}, index){
            let id = state.countries[index].id;
            // console.log(id);
            if (id > 0){
                return axios.put('/api/countries/',{id})
                // .then((res)=>console.log(res.status))
                .then ((res)=>{
                    if(res.status == 200){
                        // console.log(res);
                        commit('SET_FEEDBACK','Record Restored');
                        setTimeout(()=> commit('SET_FEEDBACK',''), 5000);
                        commit('SET_COUNTRIES', res.data.countries);
                    }
                });
            }
        },

    }

});

Amdin/CountryController.php

<?php

namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\country;
use Illuminate\Http\Request;

class CountryController extends Controller
{
    /**
     * Update and Insert records
     *
     * @return \Illuminate\Http\Response
     */
    public function upsert(Request $request, country $countries)
    {
        $this->authorize('restore','App\Country');
        $this->validate($request, ['iso' => 'required']);
        $countries = $request->post('countries');
        foreach ($countries as $cou){
            if ($cou['id']){              
                Country::where('id', $cou['id'])->update($cou);
            }else{
               
                Country::create($cou);
            }
        }
        $countries = Country::orderBy('en_GB')->get();
        return  ['countries'=> $countries];
    }
   
}

Country.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Country extends Model
{
    use SoftDeletes;
    protected $guarded = [];
    
}

2 Answers 2

1

this code will only work when you will validate one country per post:

 $this->validate($request, ['iso' => 'required']);

But you are storing n countries, so you will have to validate the array:

$this->validate($request, ['countries.*.iso' => 'required']);
Sign up to request clarification or add additional context in comments.

Comments

0

the problem is with your validation rules. When you write

$this->validate($request, ['iso' => 'required']);

it means that you request requires a field "iso" to be validated, and in your post

 axios.post('/api/countries/upsert',{
            countries: state.countries,
 })

you only have a "countries" prop and "iso" is considered empty so your required rules is always declined.

If you are trying to validate that every country in your countries array needs to have a required "iso" field you should check how to validate arrays here Laravel Arrays Validation

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.