We help IT Professionals succeed at work.

Laravel form validation error

Marco Gasi
Marco Gasi asked
on
Hi everybody.
I'm learning Laravel and I've got an issue with the Auth code. The issue is that Laravel seems to pretend that to register a new user all registration form fields be filled, even the not required ones.

This is my RegisterController.php
<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use App\User;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Http\Request;
use Illuminate\Auth\Events\Registered;

class RegisterController extends Controller
{
  /*
  |--------------------------------------------------------------------------
  | Register Controller
  |--------------------------------------------------------------------------
  |
  | This controller handles the registration of new users as well as their
  | validation and creation. By default this controller uses a trait to
  | provide this functionality without requiring any additional code.
  |
  */

  use RegistersUsers;

  /**
   * Where to redirect users after registration.
   *
   * @var string
   */
  protected $redirectTo = RouteServiceProvider::HOME;

  /**
   * Create a new controller instance.
   *
   * @return void
   */
  public function __construct()
  {
    $this->middleware('guest');
  }

  /**
   * Get a validator for an incoming registration request.
   *
   * @param  array  $data
   * @return \Illuminate\Contracts\Validation\Validator
   */
  protected function validator(array $data)
  {
    return Validator::make($data, [
      'name' => ['required', 'string', 'max:255'],
      'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
      'password' => ['required', 'string', 'min:8', 'confirmed'],
      'company' => ['string'],
      'website' => ['string'],
      'phone' => ['numeric'],
      'mobile' => ['numeric'],
      'type' => ['string'],
    ]);
  }

  /**
   * Create a new user instance after a valid registration.
   *
   * @param  array  $data
   * @return \App\User
   */
  protected function create(array $data)
  {
    return User::create([
      'name' => $data['name'],
      'email' => $data['email'],
      'password' => Hash::make($data['password']),
     //using data_get doesn't change anything
      'company' => data_get( $data, 'company'),
      'website' => data_get($data, 'website'),
      'phone' => data_get($data, 'phone'),
      'mobile' => data_get($data, 'mobile'),
    	'type' => data_get($data, 'type'),
      // 'company' => $data['company'],
      // 'website' => $data['website'],
      // 'phone' => $data['phone'],
      // 'mobile' => $data['mobile'],
    	// 'type' => $data['type'],
    ]);
  }

  /**
   * https://laraveldaily.com/auth-after-registration-redirect-to-previous-intended-page/
   * Handle a registration request for the application.
   *
   * @param  \Illuminate\Http\Request  $request
   * @return \Illuminate\Http\Response
   */
  public function register(Request $request)
  {
    $this->validator($request->all())->validate();

    event(new Registered($user = $this->create($request->all())));

    $this->guard()->login($user);

    return $this->registered($request, $user)
      ?: redirect()->intended($this->redirectPath());
  }

}

Open in new window


I originally followed a tutorial. Later I wanted to add some field to user table and I used this migration:
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class AddPersonalDataToUserTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->string('company')->nullable();
            $table->string('website')->nullable();            
            $table->string('phone')->nullable();            
            $table->string('mobile')->nullable();            
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->dropColumn(['company',  'website', 'phone', 'mobile']);
        });
    }
}

Open in new window


I thought that leaving added columns empty whould have worked fine but I've got validation errors like "The website must be a string", so I googled a bit and I have created a new migration:
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class AddDefaultNullToSomeColumn extends Migration
{
	/**
	 * Run the migrations.
	 *
	 * @return void
	 */
	public function up()
	{
		Schema::table('users', function (Blueprint $table) {
			$table->string('company')->nullable()->default(null)->change();
			$table->string('website')->nullable()->default(null)->change();
			$table->string('phone')->nullable()->default(null)->change();
			$table->string('mobile')->nullable()->default(null)->change();
		});
	}

	/**
	 * Reverse the migrations.
	 *
	 * @return void
	 */
	public function down()
	{
		Schema::table('users', function (Blueprint $table) {
			$table->string('company')->nullable()->change();
			$table->string('website')->nullable()->change();
			$table->string('phone')->nullable()->change();
			$table->string('mobile')->nullable()->change();
		});
	}
}

Open in new window


and finally, here there is my registration form
@extends('master')
@section('title', __('auth.registerTitle') )

@section('content')
<div class="hero-wrap hero-wrap-2" style="background-image: url('/images/bg_2b.jpg');" data-stellar-background-ratio="0.5">
  <div class="overlay"></div>
  <div class="container">
    <div class="row no-gutters slider-text align-items-center justify-content-center">
      <div class="col-md-8 ftco-animate text-center text-center mt-5">
        <p class="breadcrumbs mb-0"><span class="mr-3"><a href="/">{{ __('menu.home') }} <i class="ion-ios-arrow-forward"></i></a></span> <span>{{ __('auth.menu-register') }}</span></p>
        <h1 class="mb-3 bread">{{ __('auth.menu-register') }}</h1>
      </div>
    </div>
  </div>
</div>

<section class="ftco-section ftco-partner">
  <div class="container">
    <div class="row block-9 justify-content-center mb-5">
      <div class="col-md-10 mb-md-5">

        <h2 class="float-left">{{ __('auth.registerTitle') }}</h2>
        <form method="post">
          @foreach ($errors->all() as $error)
            <p class="alert alert-danger">{{ $error }}</p>
          @endforeach

          {{ csrf_field() }}
          <div class="form-group">
          <label for="name" class="col-lg-12 control-label">{{ __('auth.name') }}</label>
            <div class="col-lg-12">
              <input type="text" class="form-control" id="name" placeholder="{{ __('auth.name') }}" name="name" value="{{ old('name') }}">
            </div>
          </div>

          <div class="form-group">
            <label for="email" class="col-lg-12 control-label">{{ __('auth.email') }}</label>
            <div class="col-lg-12">
              <input type="email" class="form-control" id="email" placeholder="{{ __('auth.email') }}" name="email" value="{{ old('email') }}">
            </div>
          </div>

          <div class="form-group">
            <label for="password" class="col-lg-12 control-label">{{ __('auth.pwd') }}</label>
            <div class="col-lg-12">
              <input type="password" class="form-control"  name="password">
            </div>
          </div>

          <div class="form-group">
            <label for="password" class="col-lg-12 control-label">{{ __('auth.pwdrepeat') }}</label>
            <div class="col-lg-12">
              <input type="password" class="form-control"  name="password_confirmation">
            </div>
          </div>

          <div class="form-group">
            <label for="company" class="col-lg-12 control-label">{{ __('auth.company') }}</label>
            <div class="col-lg-12">
              <input type="text" class="form-control" id="company" placeholder="{{ __('auth.company') }}" name="company" value="{{ old('company') }}">
            </div>
          </div>

          <div class="form-group">
            <label for="website" class="col-lg-12 control-label">{{ __('auth.website') }}</label>
            <div class="col-lg-12">
              <input type="text" class="form-control" id="website" placeholder="{{ __('auth.website') }}" name="website" value="{{ old('website') }}">
            </div>
          </div>

          <div class="form-group">
            <label for="phone" class="col-lg-12 control-label">{{ __('auth.phone') }}</label>
            <div class="col-lg-12">
              <input type="text" class="form-control" id="website" placeholder="{{ __('auth.phone') }}" name="phone" value="{{ old('phone') }}">
            </div>
          </div>

          <div class="form-group">
            <label for="mobile" class="col-lg-12 control-label">{{ __('auth.mobile') }}</label>
            <div class="col-lg-12">
              <input type="text" class="form-control" id="mobile" placeholder="{{ __('auth.mobile') }}" name="mobile" value="{{ old('mobile') }}">
            </div>
          </div>

          {{-- <div class="form-group">
            <label for="type" class="col-lg-12 control-label">{{ __('auth.type') }}</label>
            <div class="col-lg-12">
                <select class="form-control" name="type" id="type">
                    <option value="admin">Admin</option>
                    <option value="super_admin">Super Admin</option>
                    <option value="member">Member</option></select>
            </div>
          </div> --}}

          <div class="form-group">
            <div class="col-lg-10 col-lg-offset-2">
							<input type="hidden" name="type" id="type" value="member" />
              <button type="reset" class="btn btn-default">{{ __('auth.cancel') }}</button>
              <button type="submit" class="btn btn-primary">{{ __('auth.submit') }}</button>
            </div>
          </div>
        </form>
      </div>
    </div>
  </div>
</section>
@endsection

Open in new window

Actually, the db table allows those fields to be NULL and their default value is NULL but I still get those errors and to successfully register a new user I must fill all fields in the form.
What I am missing here?
Comment
Watch Question

SILVER EXPERT
Most Valuable Expert 2018
Distinguished Expert 2019
Commented:
Hey Marco,

In your controller, you're defining the validation rules for your data:

return Validator::make($data, [
    'name' => ['required', 'string', 'max:255'],
    'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
    'password' => ['required', 'string', 'min:8', 'confirmed'],
    'company' => ['string'],
    'website' => ['string'],
    'phone' => ['numeric'],
    'mobile' => ['numeric'],
    'type' => ['string'],
]);

Open in new window

You can see here that you're specifying the company & website MUST be string, so it won't accept a NULL. If you want to allow your user NOT to fill in these details, then you have a couple of choices, depending on the version of Laravel you're running. In version later than 5.3, you have the nullable validation rule, so you could add that:

'company' => ['nullable', 'string'],
'website' => ['nullable', 'string'],

Open in new window

If you're using a version older than 5.2 (not recommened), then just leave the validation rule out completely:

return Validator::make($data, [
    'name' => ['required', 'string', 'max:255'],
    'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
    'password' => ['required', 'string', 'min:8', 'confirmed'],
    'type' => ['string'],
]);

Open in new window

In later versions of Laravel, you may actually be better off using a Form Validator class.

What version are you using?
Marco GasiFreelancer
BRONZE EXPERT
Top Expert 2010

Author

Commented:
Hi Chris, thank you for your answer.
I'm using Laravel 6.0.
I thought that just declaring fields as nullable in the migration were enough :)
Going to try your suggestion.
Marco GasiFreelancer
BRONZE EXPERT
Top Expert 2010

Author

Commented:
Thank you, Chris, it works like a charm.
SILVER EXPERT
Most Valuable Expert 2018
Distinguished Expert 2019

Commented:
No worries Marco,

Glad I could help.

FYI -  the Validations in your controller, aren't directly related to the tables (although it will help to prevent invalid data being saved to your DB)
Marco GasiFreelancer
BRONZE EXPERT
Top Expert 2010

Author

Commented:
the Validations in your controller, aren't directly related to the tables (although it will help to prevent invalid data being saved to your DB)
I'm not sure to understand what you mean.... Actually, no, I'm absolutely sure I don't :)
SILVER EXPERT
Most Valuable Expert 2018
Distinguished Expert 2019

Commented:
Hey Marco,

In your comments you said that the DB columns allow null but you still get the error.

My point was that you're dealing with 2 separate issues here - when you post a form to a controller, you validate the data against a set of rules. Your data either passes or fails the validation. This has nothing to do with your Database, the Tables or the Columns - you're simply validating the data a user has submitted.

After the data has been validated, you then store that data into the database - as a separate process.

That's why I said that the Validations aren't related to your Tables.
Marco GasiFreelancer
BRONZE EXPERT
Top Expert 2010

Author

Commented:
Okay, thank you so much for the additional input, very clear. Ciao.