Skip to content

Scaffold your project like a 🧙

If you're looking how a complete scaffold looks, check this Post model with just 8 lines of code!:

Blueprint

yaml
models:
  User:
    - posts: hasMany

  Post:
    id: uuid
    title: string
    body: text
    private_notes: json
    published_at: timestamp nullable
    user: belongsTo
    comments: hasMany

  Comments:
    body: text
    user: belongsTo
    post: belongsTo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

Model

php
<?php

namespace App\Models;

use App\Models\User;
use App\Models\Comment;
use Illuminate\Database\Eloquent\Concerns\HasUuids;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Factories\HasFactory;

/**
 * @mixin \Illuminate\Database\Eloquent\Builder
 *
 * @method static \Illuminate\Database\Eloquent\Builder|static query()
 * @method static static make(array $attributes = [])
 * @method static static create(array $attributes = [])
 * @method static static forceCreate(array $attributes)
 * @method \App\Models\Post firstOrNew(array $attributes = [], array $values = [])
 * @method \App\Models\Post firstOrFail($columns = ['*'])
 * @method \App\Models\Post firstOrCreate(array $attributes, array $values = [])
 * @method \App\Models\Post firstOr($columns = ['*'], \Closure $callback = null)
 * @method \App\Models\Post firstWhere($column, $operator = null, $value = null, $boolean = 'and')
 * @method \App\Models\Post updateOrCreate(array $attributes, array $values = [])
 * @method null|static first($columns = ['*'])
 * @method static static findOrFail($id, $columns = ['*'])
 * @method static static findOrNew($id, $columns = ['*'])
 * @method static null|static find($id, $columns = ['*'])
 *
 * @property-read string $id
 *
 * @property string $title
 * @property string $body
 * @property array $private_notes
 * @property \Illuminate\Support\Carbon|null $published_at
 *
 * @property-read \App\Models\User $user
 * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Comment>|\App\Models\Comment[] $comments
 *
 * @property-read \Illuminate\Support\Carbon $created_at
 * @property-read \Illuminate\Support\Carbon $updated_at
 *
 * @method static \Database\Factories\PostFactory factory($count = null, $state = [])
 */
class Post extends Model
{
    use SoftDeletes;
    use HasFactory;
    use HasUuids;

    /**
     * The attributes that should be cast.
     *
     * @var array
     */
    protected $casts = ['private_notes' => 'array', 'published_at' => 'timestamp'];

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = ['title', 'body', 'private_notes'];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array
     */
    protected $hidden = ['private_notes'];

    /**
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo|\App\Models\User
     */
    public function user()
    {
        return $this->belongsTo(User::class);
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\HasMany|\App\Models\Comment
     */
    public function comments()
    {
        return $this->hasMany(Comment::class);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86

Migration

php
<?php

use App\Models\User;
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->uuid('id')->primary();
            $table->foreignIdFor(User::class);
            $table->string('title');
            $table->text('body');
            $table->json('private_notes');
            $table->timestamp('published_at')->nullable();
            $table->softDeletes();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('posts');
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

Factory

php
<?php

namespace Database\Factories;

use App\Models\Post;
use Illuminate\Database\Eloquent\Factories\Factory;

/**
 * @implements \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Post>
 */
class PostFactory extends Factory
{
    /**
     * The name of the factory's corresponding model.
     *
     * @var string
     */
    protected $model = Post::class;

    /**
     * Define the model's default state.
     *
     * @return array
     */
    public function definition()
    {
        throw new \RuntimeException("The factory for model [Post] has no defined attributes yet.");
        // return [
        //     // ...
        // ];
    }

    /**
     * Define the deleted state.
     *
     * @return static
     */
    public function trashed()
    {
        return $this->state(function (array $attributes): array {
            // Ensure it's marked as deleted *after* the date it was created.
            return [
                $this->newModel()->getDeletedAtColumn() => fake()->dateTimeBetween(
                    $attributes[$this->newModel()->getCreatedAtColumn()] ?? '-30 years', 'now'
                ),
            ];
        });
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

Seeder

php
<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use App\Models\Post;

class PostSeeder extends Seeder
{
    /**
     * Run the database seeder.
     *
     * @return void
     */
    public function run()
    {
        DB::transaction($this->before(...));
        DB::transaction($this->createRecords(...));
        DB::transaction($this->createAdditionalRecords(...));
        DB::transaction($this->createStates(...));
        DB::transaction($this->after(...));
    }

    /**
     * Run before seeding the database.
     *
     * @return void
     */
    protected function before()
    {
        // ...
    }

    /**
     * Populate the model records using the factory definition.
     *
     * @return void
     */
    protected function createRecords()
    {
        // This makes 2 and a half pages of your model. You can change it if you want.
        $amount = (int) ((new Post())->getPerPage() * 2.5);

        Post::factory()->times($amount)->create();
    }

    /**
     * Creates additional records to populate the database.
     *
     * @return void
     */
    protected function createAdditionalRecords()
    {
        // This method is a convenient way to add personalized records.
        //
        // Post::factory()->create(['name' => 'John Doe']);
    }

    /**
     * Creates additional records by using states.
     *
     * @return void
     */
    protected function createStates()
    {
        // Add here any states you want to make.
        //
        // Post::factory()->times(10)->myAwesomeState()->create();
    }

    /**
     * Run after seeding the database.
     *
     * @return void
     */
    protected function after()
    {
        // ...
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

All of that and much more!