---
title: "Configuration — Comments for Laravel | Roundly"
description: "Every config key and its default — model, key type, approval, limits, blocklist, mentions, authorization, author resource, auto-moderation and media."
url: https://roundly-consulting.com/open-source/docs/comments-for-laravel/configuration
language: en
---

[All packages](https://roundly-consulting.com/open-source.md)

[Comments for Laravel](https://roundly-consulting.com/open-source/docs/comments-for-laravel.md)

# Configuration

The package works with zero configuration — every key has a sensible default. The published config/comments.php, with its docblocks trimmed:

```php
use RoundlyConsulting\Comments\Http\Resources\CommentAuthorResource;
use RoundlyConsulting\Comments\Models\Comment;

return [
    'model' => Comment::class,
    'key_type' => env('COMMENTS_KEY_TYPE', 'bigint'),
    'require_approval' => env('COMMENTS_REQUIRE_APPROVAL', false),
    'max_length' => env('COMMENTS_MAX_LENGTH', 5000),
    'max_depth' => env('COMMENTS_MAX_DEPTH', 5),
    'order' => env('COMMENTS_ORDER', 'latest'),
    'blocklist' => [],
    'blocklist_action' => env('COMMENTS_BLOCKLIST_ACTION', 'reject'),
    'mention_resolver' => null,
    'authorization' => env('COMMENTS_AUTHORIZATION', false),

    // The JSON resource CommentResource renders author through — see API resources.
    'resources' => [
        'author' => CommentAuthorResource::class,
    ],

    // Auto-hide a comment on an upheld report / threshold crossing (reports-for-laravel).
    'moderation' => [
        'on_resolved' => 'hide', // 'hide' | null — anything else throws
        'auto_hide' => true,     // react to the global reports threshold
    ],

    // Attachments + inline [media:UUID] rendering (media-library-for-laravel).
    'media' => [
        'attachments_bucket' => 'attachments',
        'visibility' => env('COMMENTS_MEDIA_VISIBILITY', 'private'),
        'disk' => env('COMMENTS_MEDIA_DISK'),
        'private_disk' => env('COMMENTS_MEDIA_PRIVATE_DISK', 'local'),
        'accepted_mime_types' => [],
        'max_file_size' => null,
        'responsive_widths' => null,
        'temporary_url_lifetime' => null,
        'inline' => [
            'enabled' => true,
            'default_variant' => '',
            'on_missing' => 'strip', // 'strip' | 'keep' (anything else throws)
        ],
    ],
];
```

## Every key

| Key | Default | Purpose |
| --- | --- | --- |
| `model` | Comment::class | Eloquent model that stores comments. Point it at your subclass of the packaged Comment; any other class throws. |
| `key_type` | bigint | Key type of the polymorphic columns: bigint, uuid or ulid — anything else throws. Read when the migration runs; keys are stored and read back as-is. |
| `require_approval` | false | New comments start pending and must be approved before they count as visible. |
| `max_length` | 5000 | Max body characters, at least 1; longer throws InvalidCommentBodyException. |
| `max_depth` | 5 | Max reply nesting, at least 1 (a top-level comment is depth 1; soft-deleted ancestors still count); also bounds threaded eager-loading. |
| `order` | latest | Order of approvedComments: latest (newest first) or oldest; anything else throws. |
| `blocklist` | \[\] | Banned words (whole-word, case-insensitive) or delimited regexes such as /badword/i. A blank or non-string entry throws. |
| `blocklist_action` | reject | On a match, on write and edit: reject (throw CommentRejectedException), pending or hidden; anything else throws. |
| `mention_resolver` | null | Invokable class-string or \[Class::class, 'method'\] pair, built through the container, resolving an @handle to a model — never a closure (config:cache can’t store one). null only stores handles; a value that doesn’t resolve to a callable — an unknown class, a missing method — throws. |
| `authorization` | false | Consult the Comment policy before every mutation — create, update, delete, restore, moderate, lock and unlock. |
| `resources.author` | CommentAuthorResource::class | JSON resource CommentResource renders author through; it receives the actor model. Point it at your own JsonResource to add a name or an avatar — blank or null is the default; anything that is not an instantiable JsonResource subclass throws. |
| `moderation.on_resolved` | hide | Hide a comment when a report against it is upheld; null (or a blank value) disables; anything else throws. |
| `moderation.auto_hide` | true | Hide a comment when it crosses the global reports.threshold. |
| `media.attachments_bucket` | attachments | Bucket name the comment registers on Media Library. |
| `media.visibility` | private | private (signed URLs) or public; anything else throws — a typo never picks a visibility for you. |
| `media.disk` | null | Disk for every attachment; null or blank = by visibility (private → media.private\_disk, public → the Media Library default disk). |
| `media.private_disk` | local | Disk for private attachments and their variants when media.disk is null — keep it non-public (COMMENTS\_MEDIA\_PRIVATE\_DISK). |
| `media.accepted_mime_types` | \[\] | Mime allowlist; \[\] accepts any type. |
| `media.max_file_size` | null | Max attachment size in bytes, at least 1, enforced on upload (FileUnacceptableForBucket); null = Media Library’s media.max\_file\_size. |
| `media.responsive_widths` | null | Width ladder for image attachments — a list of positive integers (variants named responsive-<width>); null = Media Library’s media.responsive.widths. |
| `media.temporary_url_lifetime` | null | Signed attachment URL lifetime in minutes, at least 1; null = the Media Library default. |
| `media.inline.enabled` | true | Expand \[media:UUID\] tokens in renderBody(). |
| `media.inline.default_variant` | '' (empty) | Variant used when a token names none — a string; empty = a responsive <img>. |
| `media.inline.on_missing` | strip | strip or keep tokens that don’t resolve; anything else throws. |

## Environment

The common switches are env-driven, so you rarely need to publish the file:

```bash
COMMENTS_KEY_TYPE=bigint
COMMENTS_REQUIRE_APPROVAL=true
COMMENTS_MAX_LENGTH=2000
COMMENTS_MAX_DEPTH=3
COMMENTS_ORDER=oldest
COMMENTS_BLOCKLIST_ACTION=pending
COMMENTS_AUTHORIZATION=true
COMMENTS_MEDIA_VISIBILITY=private
COMMENTS_MEDIA_PRIVATE_DISK=s3-private
```

Every bool switch is parsed as a boolean, so .env values mean what they say: true, 1, on and yes turn it on; false, 0, off and no turn it off. COMMENTS\_REQUIRE\_APPROVAL=1 holds new comments, COMMENTS\_AUTHORIZATION=off skips the policy. A blank value (COMMENTS\_AUTHORIZATION=) is not set, so the default applies. Anything else throws an InvalidConfigurationException instead of quietly reading as the default.

## Strict settings

Every other setting is just as strict, and every failure throws RoundlyConsulting\\PackageToolkit\\Exceptions\\InvalidConfigurationException naming the key. A setting that is not set — absent, null, or blank like a host’s COMMENTS\_MAX\_LENGTH= — takes its default; an optional one (media.disk, media.max\_file\_size, media.responsive\_widths, media.temporary\_url\_lifetime) stays unset:

- Integers accept an int or a plain integer string — every env value is a string — so COMMENTS\_MAX\_LENGTH=five or 5.5 throws rather than becoming 0. Lengths, depths, sizes, widths and lifetimes must be at least 1.
- A typo in order, blocklist\_action, moderation.on\_resolved, media.visibility or media.inline.on\_missing throws rather than picking a side — a media.visibility typo no longer reads as private, and an on\_resolved typo no longer turns auto-hide off.
- A non-string bucket or disk name throws, and so does a junk list (blocklist, accepted\_mime\_types, responsive\_widths).
- A resources.author that is not an instantiable JsonResource subclass — stdClass, a missing class, the JsonResource base, an abstract resource — throws the first time an author renders.

php artisan about renders a broken setting as INVALID instead of failing.

## Key type

key\_type decides how the polymorphic actor, commentable, mentionable and lockable columns are keyed: bigint (default), uuid or ulid — a blank value (COMMENTS\_KEY\_TYPE=) is not set and keeps bigint, any other value throws an InvalidConfigurationException. It is read when the migration runs, so set it before you migrate.

## Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

[More ways to support, including crypto](https://roundly-consulting.com/support-us.md)

By donating, you agree to our [donation terms](https://roundly-consulting.com/donation-terms.md).

[Support our open source work (opens in a new tab)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Join us on Patreon (opens in a new tab)](https://www.patreon.com/cw/roundly)

## Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.

[Get a quote in 48 hours](https://roundly-consulting.com/contact.md) [Browse all packages](https://roundly-consulting.com/open-source/docs/comments-for-laravel.md)
