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

PepsiIsBetter's avatar

isFillable() doesn't work when use Laravel's factory's has method

  • Laravel Version: 9.24.0
  • PHP Version: 8.1.7
  • Database Driver & Version: 8.0.29

Description:

model isFillable() method doesn't work as it should be when use Laravel's factory's has method.

Steps To Reproduce:

These are models

<?php
// app\Models\User.php

namespace App\Models;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable, ModelTrait;

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'name',
        'email',
        'password',
        'created_ip',
        'updated_ip'
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array<int, string>
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
        'created_at' => 'datetime',
        'updated_at' => 'datetime',
    ];

    public function posts()
    {
        return $this->hasMany(Post::class);
    }
}

<?php
// app\Models\Post.php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    use HasFactory, ModelTrait;

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'title',
    ];
}

<?php
// app\Models\ModelTrait.php

namespace App\Models;

use DateTimeInterface;

trait ModelTrait
{
    public static function boot()
    {
        parent::boot();

        static::creating(function ($model) {

            if (!$model->timestamps) {
                if ($model->isFillable('created_at')) {
                    $model->created_at = $model->freshTimestamp();
                }
            }

            if ($model->isFillable('created_ip')) {
                $model->created_ip = get_request_ip();
            }

            if ($model->isFillable('browser_info')) {
                $model->browser_info = get_browser_info();
            }
        });

        static::updating(function ($model) {
            if ($model->isFillable('updated_ip')) {
                $model->updated_ip = get_request_ip();
            }
        });
    }


    /**
     * Prepare a date for array / JSON serialization.
     *
     * @param  \DateTimeInterface  $date
     * @return string
     */
    protected function serializeDate(DateTimeInterface $date)
    {
        return $date->format('Y-m-d H:i:s');
    }
}

This is the helper

// app\helper.php

function get_request_ip()
{
    return request()->ip();
}

function get_browser_info()
{
    return request()->header('User-Agent');
}

These are migrations

<?php
// database\migrations\create_users_table.php

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

return new class extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->string('created_ip')->nullable();
            $table->string('updated_ip')->nullable();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('users');
    }
};

<?php
// database\migrations\create_posts_table.php

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

return new class extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->unsignedBigInteger('user_id');
            $table->string('title');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('posts');
    }
};

These are factories

<?php
// database\Factories\UserFactory .php

namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;

/**
 * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
 */
class UserFactory extends Factory
{
    /**
     * Define the model's default state.
     *
     * @return array<string, mixed>
     */
    public function definition()
    {
        return [
            'name' => $this->faker->name(),
            'email' => $this->faker->unique()->safeEmail(),
            'email_verified_at' => now(),
            'password' => 'yIXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
            'remember_token' => Str::random(10),
        ];
    }
}

<?php
// database\Factories\PostFactory .php

namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;

/**
 * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Post>
 */
class PostFactory extends Factory
{
    /**
     * Define the model's default state.
     *
     * @return array<string, mixed>
     */
    public function definition()
    {
        return [
            'title' => $this->faker->sentence()
        ];
    }
}

When I use factory to create model instance from php artisan tinker

This way works as expected:

$u=User::factory()->create();
$p=Post::factory()->create(['user_id'=>$u->id]);

But these ways don't work and throw an error:

$u=User::factory()->has(Post::factory())->create();
$u=User::factory()->hasPosts()->create();

The error message is

Illuminate\Database\QueryException with message 'SQLSTATE[42S22]: Column not found: 1054 Unknown column 'created_ip' in 'field list' (SQL: insert into posts (title, user_id, created_ip, browser_info, updated_at, created_at) values (Sunt vel sed dolores laborum., 6, 127.0.0.1, Symfony, 2022-08-16 04:04:43, 2022-08-16 04:04:43))'

Any ideas? Thank you!

0 likes
1 reply

Please or to participate in this conversation.