Home > Blockchain >  What causes the data validation error in this Laravel API?
What causes the data validation error in this Laravel API?

Time:01-20

I am working on a registration form with Laravel 8 and Vue 3. The back-end is an API.

In the users table migration file I have:

class CreateUsersTable extends Migration {

 public function up()
 {
  Schema::create('users', function (Blueprint $table) {
      $table->id();
      $table->string('first_name');
      $table->string('last_name');
      $table->string('email')->unique();
      $table->timestamp('email_verified_at')->nullable();
      $table->string('password');
      $table->unsignedInteger('country_id')->nullable();
      $table->foreign('country_id')->references('id')->on('countries');
      $table->rememberToken();
      $table->timestamps();
   });
 }
 // More code here
}

As can be seen above, the id in the countries table is a foreign key in the users table.

I have this piece of code in the AuthController to register a new user:

class AuthController extends Controller {
    
    public function countries()
    {
        return country::all('id', 'name', 'code');
    }
    
    public function register(Request $request) {

        $rules = [
            'first_name' => 'required|string,',
            'last_name' => 'required|string',
            'email' => 'required|email|unique:users,email',
            'password' => 'required|string|confirmed',
            'country_id' => 'required|exists:countries',
            'accept' => 'accepted',
        ];

        $customMessages = [
            'first_name.required' => 'First name is required.',
            'last_name.required' => 'Last name is required.',
            'email.required' => 'A valid email is required.',
            'email.email' => 'The email address you provided is not valid.',
            'password.required' => 'A password is required.',
            'password.confirmed' => 'The passwords do NOT match.',
            'country_id.required' => 'Please choose a country.',
            'accept.accepted' => 'You must accept the terms and conditions.'
        ];

        $fields = $request->validate($rules, $customMessages);
    
        $user = User::create([
            'first_name' => $fields['first_name'],
            'last_name' => $fields['last_name'],
            'email' => $fields['email'],
            'password' => bcrypt($fields['password']),
            'country_id' => $fields['country_id']
        ]);

        $token = $user->createToken('secret-token')->plainTextToken;

        $response = [
            'countries' => $this->countries(),
            'user' => $user,
            'token' => $token
        ];

        return response($response, 201);
    }
 }

The API routes (in routes\api.php):

Route::get('/register', [AuthController::class, 'register']);
Route::post('/register', [AuthController::class, 'register']);

On the front-end, I have:

const registrationForm = {
  data() {
    return {
      apiUrl: 'http://myapp.test/api',
      formSubmitted: false,
      countries: [],
      fields: {
        first_name: '',
        last_name: '',
        email: '',
        password: '',
        country_id: null,
      },
      errors: {},
    };
  },
  methods: {
    // get Countries
    async getCountries(){
      try {
        const response = await axios
          .get(`${this.apiUrl}/register`)
          .catch((error) => {
            console.log(error.response.data);
          });

        // Populate countries array
        this.countries = response.data.countries;

      } catch (error) {
        console.log(error);
      }
    },

    registerUser(){
      // Do Registrarion
      axios.post(`${this.apiUrl}/register`, this.fields).then(() =>{
        // Show success message
        this.formSubmitted = true;

        // Clear the fields
        this.fields = {}

      }).catch((error) =>{
        if (error.response.status == 422) {
          this.errors = error.response.data.errors;
        }
      });
    }
  },
  async created() {
    await this.getCountries();
  }
};

Vue.createApp(registrationForm).mount("#myForm");

In the Vue template:

<form id="myForm">
    <div v-if="formSubmitted" >
      <button type="button"  data-dismiss="alert">&times;</button>
      Your account was created :)
    </div>

    <div  :>
    <input type="text"  placeholder="First name" v-model="fields.first_name">
    <span v-if="errors.first_name" >{{ errors.first_name[0] }}</span>
  </div>

  <div  :>
    <input type="text"  placeholder="Last name" v-model="fields.last_name">
    <span v-if="errors.last_name" >{{ errors.last_name[0] }}</span>
  </div>

  <div  :>
    <input type="email"  placeholder="Enter email" v-model="fields.email">
    <span v-if="errors.email" >{{ errors.email[0] }}</span>
  </div>

  <div  :>
    <input type="password"  placeholder="Enter password" v-model="fields.password">
    <span v-if="errors.password" >{{ errors.password[0] }}</span>
  </div>

  <div  :>
    <input type="password"  placeholder="Confirm password" v-model="fields.password_confirmation">
    <span v-if="errors.password_confirmation" >{{ errors.password_confirmation[0] }}</span>
  </div>

  <div >
    <select  v-model="fields.country_id">
       <option value="0">Select your country</option>
       <option v-for="country in countries" :value="country.id">{{ country.name }}</option>
    </select>
  </div>

  <div  :>
    <input type="checkbox" name="accept" v-model="fields.accept">
    <p>I accept <a href="#" >The Terms and Conditions</a></p>
    <span v-if="errors && errors.accept" >{{ errors.accept[0] }}</span>
  </div>

  <div >
    <button @click.prevent="registerUser" type="submit" >Register</button>
  </div>
</form>

The problem

For a reason I was unable to figure out, the app throws this error in the browser (Network tab), with status code 422 Unprocessable Content:

{
 "message":"The given data was invalid.",
   "errors":{
      "first_name":[
         "The \"First name\" field is required."
      ],
      "last_name":[
         "The \"Last name\" field is required."
      ],
      "email":[
         "The \"Email\" field is required."
      ],
      "password":[
         "A password is required."
      ],
      "country_id":[
         "Please choose a country."
      ],
      "accept":[
         "The \"Terms and conditions\" must be accepted."
      ]
   }
}

Question

What am I doing wrong?

CodePudding user response:

The country_id field won't exist in the countries table. By default Laravel will look for the column in the database by the name of the field to be validated. So you should use this:

'country_id' => 'required|exists:countries,id'

https://laravel.com/docs/8.x/validation#specifying-a-custom-column-name

  •  Tags:  
  • Related