I have 3 models (User, Post, and Comment).
User model:
public function posts() // A user can have many posts
{
return $this->hasMany('App\Post', 'from_id');
}
Post model:
protected $with = [ // I'm using with() here
'from',
'for',
'comments',
];
public function from() // Post owner
{
return $this->belongsTo(User::class, 'from_id');
}
public function for() // To whom this post is addressed
{
return $this->belongsTo(User::class, 'for_id');
}
public function comments() // All comments for this post
{
return $this->morphMany(Comment::class, 'commentable');
}
Post migration:
$table->id();
$table->string('uuid');
$table->foreign('from_id')->references('id')->on('users');
$table->unsignedBigInteger('from_id');
$table->foreign('for_id')->references('id')->on('users');
$table->unsignedBigInteger('for_id')->nullable();
$table->text('body');
$table->timestamps();
$table->softDeletes();
Comment model:
protected $with = [ // I'm also using with() here
'from',
'children',
];
public function from() // Comment owner
{
return $this->belongsTo(User::class, 'from_id', 'id');
}
public function commentable()
{
return $this->morphTo();
}
public function children() // child comments for this comment (?) Not sure how it's working or not
{
return $this->hasMany(Comment::class, 'parent_id');
}
Comment migration:
$table->id();
$table->string('uuid');
$table->unsignedBigInteger('commentable_id');
$table->string('commentable_type');
$table->foreign('from_id')->references('id')->on('users');
$table->unsignedBigInteger('from_id');
$table->foreign('parent_id')->references('id')->on('comments');
$table->unsignedBigInteger('parent_id')->nullable();
$table->text('body');
$table->timestamps();
$table->softDeletes();
Note that I have two dummy users in my Users table.
Then let's say we have two records inside our Posts table:
|------------------------------------------------------|
| id | uuid | from_id | for_id | body | ... |
|------------------------------------------------------|
| 1 | .... | 1 | null | ... | ... |
| 2 | .... | 1 | null | ... | ... |
So let's create a comment for id #1:
php artisan tinker
>> $post = App\Post::first();
>> $post->comments()->create(['from_id' => 2, 'body' => 'Test comment']);
>> App\Comment {#3744
from_id: 2,
body: "Test comment",
commentable_id: 1,
commentable_type: "App\Post",
uuid: "68bc8dbd-9769-44d7-8139-3e4e14d3df4f",
updated_at: "2020-09-01 15:00:38",
created_at: "2020-09-01 15:00:38",
id: 1,
}
Now let's see the first posts with comments (I'm using protected $with
in my Post model):
php artisan tinker
>> $post = App\Post::first();
>> App\Post {#4030
id: 1,
uuid: "e9503551-99ac-495f-902e-b505408ab9ef",
from_id: 1,
for_id: null,
body: "Vero vel officia qui et. Veritatis laudantium itaque nisi sint repellendus laborum. Nihil at aliquam alias in.",
created_at: "2020-09-01 14:59:11",
updated_at: "2020-09-01 14:59:11",
deleted_at: null,
comments: Illuminate\Database\Eloquent\Collection {#4039
all: [
App\Comment {#4049
id: 1,
uuid: "68bc8dbd-9769-44d7-8139-3e4e14d3df4f",
commentable_id: 1,
commentable_type: "App\Post",
from_id: 2,
parent_id: null,
body: "Test comment",
created_at: "2020-09-01 15:00:38",
updated_at: "2020-09-01 15:00:38",
deleted_at: null,
from: App\User {#4061
id: 2,
name: "Prof. Runte Jr.
email: "abigail@example.test",
...
},
children: Illuminate\Database\Eloquent\Collection {#4040
all: [],
},
},
],
},
}
We can see the first post now has a comment, alright.
Now I want to create a child comment for the first comment inside the post before:
php artisan tinker
// get the first post, and then get the first comment of it
>> $post = App\Post::first()->comments()->first();
>> App\Comment {#4060
id: 1,
uuid: "68bc8dbd-9769-44d7-8139-3e4e14d3df4f",
commentable_id: 1,
commentable_type: "App\Post",
from_id: 2,
parent_id: null,
body: "Test comment",
created_at: "2020-09-01 15:00:38",
updated_at: "2020-09-01 15:00:38",
deleted_at: null,
from: App\User {#4073
id: 2,
name: "Prof. Runte Jr.
email: "abigail@example.test",
...
},
children: Illuminate\Database\Eloquent\Collection {#4030
all: [],
},
}
// Now we want to create child comment
// Get the first post
>> $post = App\Post::first()
// Get the first comment from it
->comments()->first()
// Get children relationship (IDK if this the right words to put it) and create a child comment
->children()->create(['from_id' => 1, 'body' => 'Testing child comment']);
This is what tinker returns:
Illuminate/Database/QueryException with message 'SQLSTATE[HY000]: General error: 1364 Field 'commentable_id' doesn't have a default value (SQL: insert into
comments
(from_id
,body
,parent_id
,uuid
,updated_at
,created_at
) values (1, Testing child comment, 1, aa3d624f-3984-438c-adb0-26086459de33, 2020-09-01 15:38:36, 2020-09-01 15:38:36))'
So my question is:
How to make a child comment inside polymorphic relationship (morphMany()
)? I already set the column for parent_id
which belongs to comment id.
As comments()
return collection of comments. So, You should use this one like:
$post = App\Post::first();
foreach($post->comments() as $comment){
$comment->children()->create([
'from_id' => 1,
'body' => 'Testing child comment'
]);
}
You can also use save()
method as:
$post = App\Post::first();
$comment = new App\Comment([
'from_id' => 1,
'body' => 'Testing child comment'
]);
$child = $post->comments()->first()->save($comment);
And You have to change in your App\Comment
method children()
with this :
public function children()
{
return $this->morphMany(Comment::class, 'commentable');
}