I have a Laravel 5.3 app that includes a users
table. One of the fields on that table is api_token
, which in the database definition is set to NOT NULL. If I use Postman and hit my API endpoint for creating a user and don't include the api_token
field, I receive the following error: SQLSTATE[HY000]: General error: 1364 Field 'api_token' doesn't have a default value
.
I haven't added code to the generate the token yet, so that's exactly what I'd expect. However, I have this PHPUnit test:
/** @test */
public function a_user_can_be_created()
{
$this->user_data = [
'first_name' => 'Jane',
'last_name' => 'Smith',
'email_address' => '[email protected]',
'phone' => '1234567890',
'username' => 'jsmith',
'password' => 'testing'
];
$this->post('/api/users', $this->user_data, array(
'Accept' => 'application/json'
))
->assertResponseStatus(201)
->seeJsonEquals(['status' => 'success'])
->seeInDatabase('users', $this->user_data);
}
The problem is that the test passes. How can this test be successful when the database should be throwing an exception when the user is saved?
I've 'died and dumped' at various points and confirmed that the data is in fact being saved to the database when the automated test runs. I've also tried using the same DB for both Postman and the automated test (instead of a separate testing DB) - same result.
I tried to build the minimal version of your API and create a similar test on the latest Laravel 5.3.22 version. I also used MariaDB 10.1.13 for the database. And it turns out the test failed as we expected, because of the empty api_token
.
Here's my migration file for creating the users
table:
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateUsersTable extends Migration
{
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('first_name');
$table->string('last_name');
$table->string('email_address')->unique();
$table->string('phone');
$table->string('username');
$table->string('password');
$table->string('api_token');
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('users');
}
}
Here's my app\User.php
file looks like. Pretty minimal, I only update the $fillable
property to match our users
table structure:
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use Notifiable;
protected $fillable = [
'first_name',
'last_name',
'email_address',
'phone',
'username',
'password',
'api_token',
];
protected $hidden = [
'password',
];
}
I create a new API route on routes/api.php
file to create a new user. Super simple without any validation involved:
Route::post('users', function (Request $request) {
$data = $request->only([
'first_name',
'last_name',
'email_address',
'phone',
'username',
'password',
'api_token',
]);
if (is_string($data['password'])) {
$data['password'] = bcrypt($data['password']);
}
$user = App\User::create($data);
return response()->json([
'status' => 'success',
], 201);
});
And finally here's my test file for testing our create user API.
class ApiTest extends TestCase
{
/** @test */
public function a_user_can_be_created()
{
$user = [
'first_name' => 'Jane',
'last_name' => 'Smith',
'email_address' => '[email protected]',
'phone' => '1234567890',
'username' => 'jsmith',
];
$this->json('POST', 'api/users', $user)
->assertResponseStatus(201)
->seeJsonEquals(['status' => 'success'])
->seeInDatabase('users', array_except($user, ['password']));
}
}
The only difference is I used array_except()
to exclude the password
column from database testing since we hashed the value before storing it in the database. But of course this does not relate to your issue.
When I run the test without the api_token
being set, the test failed as expected. The API return a status code of 500, while we expected it to be 201.
Also as a reference, when I set the api_token
column as NOT nullable, then force to create a new user with an empty api_token
, the exception message I received is like this:
QueryException in Connection.php line 769:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'api_token' cannot be null (SQL: insert into users...
Hope this help you debug your issue!
I assume you are using mysql. In mysql there is a problem with NOT NULL
. It goes like this. Although the field is NOT NULL
you can leave it empty in mysql and the query passes. NOT NULL
in mysql means that you can't have NULL
value in that field not that you can't leave it empty. I assume you create the user as this
User::create($request->all())
If you try to print the generated sql query
dd((new User($request->all())->toSql())
You will see that the generated query contains ''
value for api_token
field
I switched to posgres mainly because this problem.
If you want to solve this problem you have couple of options. One is to leave the api_token
field nullable. The second one is to fill the model manually
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With