Day 2: Models & Migrations

Summary

Going into today’s session I had the following goals:

  • Create a Site model
  • Create a sites table that can be “owned” by a user
  • Create a site belonging to a user

I referenced the Laravel migrations doc at laravel.com/docs/12.x/migrations and asked Gemini a few clarifying questions when I ran into issues that I couldn’t quickly find an answer to.

I was also aquainted with php artisan today, and it feels very much like working with Ruby on Rails scaffolding.

The Work Session

I created the model and a database migation for Site using php artisan.

1
2
3
4
5
$ php artisan make:model Site -m

   INFO  Model [app/Models/Site.php] created successfully.

   INFO  Migration [database/migrations/2026_01_13_054336_create_sites_table.php] created successfully.

I really appreciated that it told me the path to the files it created making finding them really easy.

When editing the migration file I learned about nullable() to make a database field nullable because I want users to have the option to use a custom domain but not force them to if having a subdomain is fine and unique() to make a field unique so site slugs are always unique.

I ran into an issue where I didn’t define the user_id column before attempting to make it a foreign key so I got an error trying to run the migration.

I was able to get past this by defining the column first and then creating a foreign key.

1
2
3
4
5
6
7
8
9
Schema::create('sites', function (Blueprint $table) {
    $table->id();
    $table->timestamps();
    $table->unsignedBigInteger('user_id');
    $table->foreign('user_id')->references('id')->on('users');
    $table->string('name');
    $table->string('slug');
    $table->string('custom_domain')->nullable();
});

Running the migration I attempted to use php artisan tinker to create a new site belonging to my user, but I was met with an error! This is where I should note that my foreign key was originally called owner_id, not user_id.

1
Illuminate\Database\QueryException  SQLSTATE[HY000]: General error: 1 table sites has no column named user_id (Connection: sqlite, Database: database/database.sqlite, SQL: insert into "sites" ("name", "slug", "user_id", "updated_at", "created_at") values (Mountain Lotus Digital, mountainlotus, 1, 2026-01-13 06:11:18, 2026-01-13 06:11:18)).

Through a bit of research I learned that Laravel by default will expect the foreign key column to be named _id and since I was referencing the users table user_id was expected. I saw there were ways to work around this by defining constraints and some other stuff but I figured just using user_id was the easiest (and probably correct) thing to do.

So I updated my migration file and ran php artisan migrate:fresh to start with a fresh database.

I used artisan to check the Site model and make sure that user_id was present. It was!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
$ php artisan model:show Site

  App\Models\Site ....................................................................................................
  Database .................................................................................................... sqlite
  Table ........................................................................................................ sites

  Attributes ............................................................................................. type / cast
  id increments, unique ................................................................................ integer / int
  created_at nullable ............................................................................ datetime / datetime
  updated_at nullable ............................................................................ datetime / datetime
  user_id .................................................................................................... integer
  name fillable .............................................................................................. varchar
  slug fillable .............................................................................................. varchar
  custom_domain nullable, fillable ........................................................................... varchar

Now I headed back into tinker to see if I could finally create a site that belonged to a user. Since I had done migrate:fresh I had to recreate my user and then create the site.

 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
$ php artisan tinker
Psy Shell v0.12.18 (PHP 8.4.1 — cli) by Justin Hileman
New PHP manual is available (latest: 3.0.1). Update with `doc --update-manual`
> $user = App\Models\User::create([
   'email' => '[redacted]',
    'password' => bcrypt('password'),
]);
= App\Models\User {#6146
    name: "Joe Nichols",
    email: "[redacted]",
    #password: "[redacted]",
    updated_at: "2026-01-13 06:17:10",
    created_at: "2026-01-13 06:17:10",
    id: 1,
  }

> $user->sites()->create([
me' => 'Mountain Lotus Digital',
    'slug' => 'mountainlotus',
]);
= App\Models\Site {#6892
    name: "Mountain Lotus Digital",
    slug: "mountainlotus",
    user_id: 1,
    updated_at: "2026-01-13 06:17:16",
    created_at: "2026-01-13 06:17:16",
    id: 1,
  }

Success!

Recap

To recap I learned to create the Site model, create a sites table, run migrations and use tinker to create a site that belongs to a user all in a Laravel way.

Todays code commit is commit b26d6a1.