CamKem's avatar
Level 10

Factory for Polymorphic Many to Many Relationship

Hello, I am wanting to add to my model factories the ability to add a polymorphic entry when I seed the database. I have the following relationships & models:

Articles (Contains the Topicable trait)
	|--- id
	|--- title
	|--- body

Thread (Contains the Topicable trait)
	|--- id
	|--- title
	|--- body

Topics
	|--- id
	|--- title
	|--- color

Topicables
	|--- id
	|--- topic_id
	|--- topicable_id
	|--- topicable_type

This is my topicable trait:

use App\Models\Topic;

trait Topicable
{

    public function topic($topic): array
    {
        return $this->topics()->syncWithoutDetaching($topic);
    }

    public function untopic($topic): int
    {
        return $this->topics()->detach($topic);
    }

    public function topics()
    {
        return $this->morphToMany(Topic::class, 'topicable')->withTimestamps();
    }
    
}

How can I add to my Threads & Articles factories, the ability to:

  1. Get the topic ID's from the topics that have already been seeded.
  2. Add a random topic to the Topicables tables for the thread that is created.

I did some research which suggested that I could configure the factory like so (but it doesn't work):

<?php

namespace Database\Factories;

use App\Models\Thread;
use App\Models\Topic;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Carbon;

class ThreadFactory extends Factory
{
    protected $model = Thread::class;

    /**
     * Define the model's default state.
     *
     * @return array
     */
    public function definition(): array
    {

        $userids = User::pluck('id')->toArray();

        // slug is set in the model, using the sluggable package

        return [
            'title' => $this->faker->sentence(),
            'body' => $this->faker->paragraphs(3, true),
            'created_at' => Carbon::now(),
            'updated_at' => Carbon::now(),
            'user_id' => $this->faker->randomElement($userids),
        ];
    }

    // set up the polymorphic relationship with the topics table
    public function configure(): ThreadFactory
    {

        // get a random topic id from the topics table
        $topic = Topic::inRandomOrder()->first();

        // return the thread with the topic attached
        return $this->afterCreating(function (Thread $thread) use ($topic) {
            $thread->topics($topic);
        });
    }
}

Thanks for your help!

0 likes
2 replies
LaryAI's avatar
Level 58

To add a random topic to the Topicables table for the thread that is created, you can use the afterCreating method in the ThreadFactory class. Here's an example:

use App\Models\Thread;
use App\Models\Topic;
use Illuminate\Database\Eloquent\Factories\Factory;

class ThreadFactory extends Factory
{
    protected $model = Thread::class;

    public function definition()
    {
        return [
            'title' => $this->faker->sentence,
            'body' => $this->faker->paragraphs(3, true),
        ];
    }

    public function configure()
    {
        return $this->afterCreating(function (Thread $thread) {
            $topic = Topic::inRandomOrder()->first();
            $thread->topics()->attach($topic);
        });
    }
}

This will attach a random topic to the thread that is created. You can do the same thing for the ArticleFactory.

CamKem's avatar
CamKem
OP
Best Answer
Level 10

Sorry was just a typo after all that, haha...

It should have been:

$thread->topic($topic);

Please or to participate in this conversation.