Skip to main content

Black Friday 2025! Only until December 1st: coupon FRIDAY25 for 40% off Yearly/Lifetime membership!

Read more here

Category: API Resource with Relationship

Premium
3 min read

In this lesson, let's show a column from the relationship in the posts table. We will add a category for every post.

finished category relation


First, we need to create a Category model and migration.

php artisan make:model Category -m

database/migrations/xxxx_create_categories_table.php:

public function up(): void
{
Schema::create('categories', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
}

app/Models/Category.php:

class Category extends Model
{
protected $fillable = [
'name',
];
}

Next, we need to create a migration...

The Full Lesson is Only for Premium Members

Want to access all of our courses? (29 h 14 min)

You also get:

54 courses
Premium tutorials
Access to repositories
Private Discord
Get Premium for $129/year or $29/month

Already a member? Login here

Comments & Discussion

SH
SHahab hashemi ✓ Link copied!

while i use public function category(): BelongsTo in app/Models/Post.php i get this error App\Models\Post::category(): Return value must be of type App\Models\BelongsTo, Illuminate\Database\Eloquent\Relations\BelongsTo returned i removed : BelongsTo from function category and now work.

PK
Povilas Korop ✓ Link copied!

It also needs to have BelongsTo in the use section on top.

use Illuminate\Database\Eloquent\Relations\BelongsTo;
J
jwinder ✓ Link copied!

Povillas question:

    $table->foreignId('category_id')->after('content')->constrained();

why would we need this to be in a seperate migration (add category to posts table), couldnt we just add thiis to create_categories_table migration?

M
Modestas ✓ Link copied!

You technically can add that, but it hides the intention under another name. Imagine, you are looking where category_id is defined and you don't find it in migrations list. It's there in the database, but there is no migration. So you just have to guess which file contains in... Takes more time :)

N
Nerijus ✓ Link copied!

In this case project continues and we add a feature. Imagine if project is in production how then migration would run?

J
jwinder ✓ Link copied!

Thank you for the clarification!

G
geochpok ✓ Link copied!

IMHO, generarily you need categories migration to go BEFORE your posts migration in order to use this foreign key in the posts migration itself. Which is not true in this case, if you follow along: categories migration has been created after the posts one. To avoid errors when running migrations, it's wise to create all the tables and add foreign keys afterwards when they are all present in the database.

M
Modestas ✓ Link copied!

Sorry, what are you talking about?

We have created posts, then created a categories table. Once both of them are up - we then create another migration that adds the connection between them.

This is by far, the most common and realistic approach that you will encounter when creating a product. Unless of course, you don't write any code until you have a full database layout already figured out and stationary :)

Z
Zubair ✓ Link copied!

Is the category seeder and posts.catetgory_id population functions left out? The screenshots has it populated

Z
Zubair ✓ Link copied!

Also, when migrating with constrained() I get the error

Cannot add a NOT NULL column with default value NULL (Connection: sqlite, SQL: alter table "posts" add column "category_id" integer not null)

JM
Jayenne Montana ✓ Link copied!

It looks like those steps have been overlooked from this page.

Add the a category_id to the PostFactory 'category_id' => 1, so that the posts seeder can default to an id.

Next, create a CategorySeeder with php artisan make:seeder CategorySeeder

Then add the following to the run method...

public function run(): void
    {
        Category::factory()->count(1)->create();
    }

Then create a new factory for the category with php artisan make:factory CategoryFactory

Then add this to the returned array the definition() function... 'name' => $this->faker->words(3, true),

Lastly, add the CategorySeeder to the DatabaseSeeder before calling the PostsSeeder like so...

$this->call([CategorySeeder::class]);
$this->call([PostSeeder::class]);

Then once you rerun php migrate:fresh --seed you'll have all you need.

I added the "default" category_Id to the factory as It's probably best not set in the migration as this might have ugly consequenses in real world situations.

You could go further by change 1 to 10 categories in the CategorySeeder then setting PostFactory (or even in the PostSeeder) to pick a random id for use as category_id like this...

public function definition(): array
    {
        $categoryId = $this->getRandomCategoryId();
        return [
            'title'       => $this->faker->word(),
            'content'     => $this->faker->paragraphs(asText: true),
            'category_id' => $categoryId,
        ];
    }

    private function getRandomCategoryId(): int
    {
        $category = Category::inRandomOrder()->first();

        return $category->id ?? 1;
    }
Z
Zubair ✓ Link copied!

Thanks, I did something of that nature👍