Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 40 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,10 @@ php artisan vendor:publish --tag=happenv-filament-comments-migrations
php artisan migrate
```

The table has a polymorphic `commentable` relation created with `nullableUuidMorphs()` (so `commentable_id` is a UUID
column), an `author_id` foreign key to the `users` table, a `content` text column and timestamps. Edit the published
migration before running it if your commentable models do not use UUID keys or your authors are not stored in `users`.
The table has an auto-incrementing `id`, a polymorphic `commentable` relation created with `nullableMorphs()` (so
`commentable_id` is an integer column), an `author_id` foreign key to the `users` table, a `content` text column and
timestamps. Edit the published migration before running it if your models use UUID or ULID keys (see
[UUID or ULID keys](#uuid-or-ulid-keys)) or your authors are not stored in `users`.

> [!IMPORTANT]
> If you have not set up a custom theme and are using Filament Panels, follow the instructions in the Filament docs
Expand Down Expand Up @@ -85,12 +86,19 @@ return [
* Model of comment authors. Defaults to the model of the "users" auth provider.
*/
'author_model' => null,

/*
* Model of comments. Must extend Happenv\FilamentComments\Models\Comment,
* e.g. to add HasUuids together with a matching migration.
*/
'comment_model' => null,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, and it went further than stale docs: that section documented the container binding, so removing it would have broken apps that use it. Fixed in 841ccd0:

  • CommentModel::resolve() reads comment_model first and falls back to the class the container binds to Comment. The bindIf is back.
  • The "Comment model" section now leads with the config option and keeps bind() as the older way that still works.
  • Two new tests cover it: a container binding is used when the config isn't set, and the config wins over a binding.

];
```

| Key | Default | What it does |
|----------------|---------|--------------|
| `author_model` | `null` | Eloquent model of comment authors (`Comment::author()`, the default mention provider). `null` falls back to `auth.providers.users.model`. |
| `comment_model` | `null` | Eloquent model of comments, used by `HasComments`. Must extend `Happenv\FilamentComments\Models\Comment`. `null` uses the package's own model. |

The author model does not depend on the logged-in user. If neither setting points to an Eloquent model, a
`LogicException` is thrown. For example:
Expand All @@ -106,6 +114,35 @@ php artisan vendor:publish --tag=happenv-filament-comments-views
php artisan vendor:publish --tag=happenv-filament-comments-translations
```

### UUID or ULID keys

The migration uses standard auto-incrementing keys. If your models use UUIDs (or ULIDs), change the published
migration before running it:

- **Commentable models with UUID keys** — replace `nullableMorphs('commentable')` with `nullableUuidMorphs('commentable')`
(or `nullableUlidMorphs()`).
- **UUID keys for the comments themselves** — replace `$table->id()` with `$table->uuid('id')->primary()`, and point
`comment_model` to your own model that adds `HasUuids`:

```php
namespace App\Models;

use Happenv\FilamentComments\Models\Comment as BaseComment;
use Illuminate\Database\Eloquent\Concerns\HasUuids;

class Comment extends BaseComment
{
use HasUuids;
}
```

```php
// config/filament-comments.php
'comment_model' => App\Models\Comment::class,
```

Relationships you define yourself (such as `internalNotes()` below) should use the same model.

## Usage

### Make a model commentable
Expand Down
6 changes: 6 additions & 0 deletions config/filament-comments.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,10 @@
* Model of comment authors. Defaults to the model of the "users" auth provider.
*/
'author_model' => null,

/*
* Model of comments. Must extend Happenv\FilamentComments\Models\Comment,
* e.g. to add HasUuids together with a matching migration.
*/
'comment_model' => null,
];
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ public function up(): void
Schema::create('comments', function (Blueprint $table): void {
$table->id();

$table->nullableUuidMorphs('commentable');
// Commentable models with UUID or ULID keys: nullableUuidMorphs() / nullableUlidMorphs().
$table->nullableMorphs('commentable');

$table->foreignId('author_id')->constrained('users');

Expand Down
3 changes: 2 additions & 1 deletion src/Concerns/HasComments.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Happenv\FilamentComments\Concerns;

use Happenv\FilamentComments\Models\Comment;
use Happenv\FilamentComments\Support\CommentModel;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphMany;

Expand All @@ -19,6 +20,6 @@ trait HasComments
*/
public function comments(): MorphMany
{
return $this->morphMany(resolve(Comment::class)::class, 'commentable');
return $this->morphMany(CommentModel::resolve(), 'commentable');
}
}
7 changes: 0 additions & 7 deletions src/FilamentCommentsServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
namespace Happenv\FilamentComments;

use Happenv\FilamentComments\Livewire\CommentsList;
use Happenv\FilamentComments\Models\Comment;
use Livewire\Livewire;
use Override;
use Spatie\LaravelPackageTools\Package;
Expand All @@ -23,12 +22,6 @@ public function configurePackage(Package $package): void
->discoversMigrations();
}

#[Override]
public function packageRegistered(): void
{
$this->app->bindIf(Comment::class, Comment::class);
}

#[Override]
public function packageBooted(): void
{
Expand Down
2 changes: 1 addition & 1 deletion src/Models/Comment.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
/**
* @property int|string $id
* @property string $commentable_type
* @property string $commentable_id
* @property int|string $commentable_id
* @property int|string $author_id
* @property string $content
* @property CarbonInterface $created_at
Expand Down
27 changes: 27 additions & 0 deletions src/Support/CommentModel.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace Happenv\FilamentComments\Support;

use Happenv\FilamentComments\Models\Comment;
use LogicException;

final class CommentModel
{
/**
* Class of comments: the `filament-comments.comment_model` config, or the package's own model.
*
* @return class-string<Comment>
*/
public static function resolve(): string
{
$model = config('filament-comments.comment_model') ?? Comment::class;

if (! is_string($model) || ! is_a($model, Comment::class, true)) {
throw new LogicException('[filament-comments.comment_model] must extend ' . Comment::class . '.');
}

return $model;
}
}
73 changes: 73 additions & 0 deletions tests/Feature/CommentModelTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

declare(strict_types=1);

use Happenv\FilamentComments\Filament\Components\Comments;
use Happenv\FilamentComments\Livewire\CommentsList;
use Happenv\FilamentComments\Models\Comment;
use Happenv\FilamentComments\Support\CommentModel;
use Happenv\FilamentComments\Tests\Fixtures\Models\Post;
use Happenv\FilamentComments\Tests\Fixtures\Models\User;
use Happenv\FilamentComments\Tests\Fixtures\Models\UuidComment;
use Happenv\FilamentComments\Tests\Fixtures\Models\UuidPost;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str;
use Livewire\Livewire;

it('creates the comments table with standard integer keys', function (): void {
expect(Schema::getColumnType('comments', 'id'))->toBe('integer')
->and(Schema::getColumnType('comments', 'commentable_id'))->toBe('integer');
});

it('stores comments on a model with integer keys by default', function (): void {
$post = Post::factory()->create();
$this->actingAs(User::factory()->create());

expect($post->comments()->getRelated())->toBeInstanceOf(Comment::class);

commentsList($post)
->fillForm(['content' => '<p>Hello</p>'])
->call('submitComment')
->assertHasNoFormErrors();

expect($post->comments()->sole())
->commentable_id->toEqual($post->getKey())
->commentable->is($post)->toBeTrue();
});

it('uses the comment model set in the config', function (): void {
config(['filament-comments.comment_model' => UuidComment::class]);

$post = UuidPost::create(['title' => 'A post with UUID keys']);
$this->actingAs(User::factory()->create());

expect($post->comments()->getRelated())->toBeInstanceOf(UuidComment::class);

$settings = Comments::make()->toSettings();

Livewire::test(CommentsList::class, ['record' => $post, 'settings' => $settings])
->fillForm(['content' => '<p>Hello UUID</p>'])
->call('submitComment')
->assertHasNoFormErrors()
->assertSee('Hello UUID');

$comment = $post->comments()->sole();

expect($comment)->toBeInstanceOf(UuidComment::class)
->and(Str::isUuid($comment->getKey()))->toBeTrue()
->and($comment->commentable_id)->toBe($post->getKey());

// A deep link finds the comment by its UUID.
Livewire::withQueryParams([$settings->commentIdParameter() => $comment->getKey()])
->test(CommentsList::class, ['record' => $post, 'settings' => $settings])
->assertDispatched('highlight-comment', commentId: $comment->getKey(), relationship: 'comments');
});

it('refuses a comment model that does not extend the package model', function (mixed $model): void {
config(['filament-comments.comment_model' => $model]);

CommentModel::resolve();
})->throws(LogicException::class)->with([
'another model' => User::class,
'not a class' => 'nope',
]);
3 changes: 0 additions & 3 deletions tests/Fixtures/Models/Post.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
use Happenv\FilamentComments\Models\Comment;
use Happenv\FilamentComments\Tests\Fixtures\Factories\PostFactory;
use Illuminate\Database\Eloquent\Attributes\UseFactory;
use Illuminate\Database\Eloquent\Concerns\HasUuids;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphMany;
Expand All @@ -21,8 +20,6 @@ class Post extends Model
/** @use HasFactory<PostFactory> */
use HasFactory;

use HasUuids;

protected $guarded = [];

/**
Expand Down
18 changes: 18 additions & 0 deletions tests/Fixtures/Models/UuidComment.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace Happenv\FilamentComments\Tests\Fixtures\Models;

use Happenv\FilamentComments\Models\Comment;
use Illuminate\Database\Eloquent\Concerns\HasUuids;

/**
* An app's own comment model with UUID keys, set as `filament-comments.comment_model`.
*/
class UuidComment extends Comment
{
use HasUuids;

protected $table = 'uuid_comments';
}
20 changes: 20 additions & 0 deletions tests/Fixtures/Models/UuidPost.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace Happenv\FilamentComments\Tests\Fixtures\Models;

use Happenv\FilamentComments\Concerns\HasComments;
use Illuminate\Database\Eloquent\Concerns\HasUuids;
use Illuminate\Database\Eloquent\Model;

/**
* A commentable model with UUID keys, for the `comment_model` override.
*/
class UuidPost extends Model
{
use HasComments;
use HasUuids;

protected $guarded = [];
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,25 @@ public function up(): void
});

Schema::create('posts', function (Blueprint $table): void {
$table->id();
$table->string('title');
$table->timestamps();
});

// An app that switches to UUIDs: its own comment model on its own table,
// with UUID keys for the comments and for the commentable models.
Schema::create('uuid_posts', function (Blueprint $table): void {
$table->uuid('id')->primary();
$table->string('title');
$table->timestamps();
});

Schema::create('uuid_comments', function (Blueprint $table): void {
$table->uuid('id')->primary();
$table->nullableUuidMorphs('commentable');
$table->foreignId('author_id')->constrained('users');
$table->text('content');
$table->timestamps();
});
}
};
Loading