PHPackages                             asciisd/nova-chat - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. asciisd/nova-chat

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

asciisd/nova-chat
=================

A reusable, contract-driven WhatsApp-style chat tool for Laravel Nova.

v1.0.2(4w ago)164MITPHPPHP ^8.3

Since May 11Pushed 4w agoCompare

[ Source](https://github.com/asciisd/nova-chat)[ Packagist](https://packagist.org/packages/asciisd/nova-chat)[ RSS](/packages/asciisd-nova-chat/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (4)Dependencies (1)Versions (8)Used By (0)

asciisd/nova-chat
=================

[](#asciisdnova-chat)

[![Latest Version on Packagist](https://camo.githubusercontent.com/c418b23094d39c456ce4512683dde8f9f8f6675b85a305646a9781e917ca9e58/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f617363696973642f6e6f76612d636861742e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/asciisd/nova-chat)[![Total Downloads](https://camo.githubusercontent.com/2d5fe38c8d8c0e084b2eecea9328bbcf3cfabde80c195e80a817dc07b82f430f/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f617363696973642f6e6f76612d636861742e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/asciisd/nova-chat)[![PHP Version](https://camo.githubusercontent.com/43fac01c7b71bed5afafe3d0ed725d89552533a04761f269411b597e94c1c620/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f617363696973642f6e6f76612d636861742e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/asciisd/nova-chat)[![License](https://camo.githubusercontent.com/f5b617609f61f52d985985cbb7ea58753bcf543ffd7f16be2b7661ba53fb0d72/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f617363696973642f6e6f76612d636861742e7376673f7374796c653d666c61742d737175617265)](LICENSE)

A reusable, contract-driven WhatsApp-style chat tool for Laravel Nova.

The package never assumes a single shared `chat_messages` table. Each project plugs in its own host model (a "topic"), its own message model, and its own author models. Everything connects through three small interfaces.

[![Nova Chat — sidebar, thread, and composer](docs/screenshots/thread.png)](docs/screenshots/thread.png)

Requirements
------------

[](#requirements)

- PHP ^8.3
- Laravel Nova ^5.0

Installation
------------

[](#installation)

```
composer require asciisd/nova-chat
php artisan vendor:publish --tag=nova-chat-config
php artisan migrate
```

The package ships its own auto-loaded migration for the `nova_chat_blocked_participants` table that backs admin moderation — no `vendor:publish` step is required for it.

Register the tool in `app/Providers/NovaServiceProvider.php`:

```
public function tools(): array
{
    return [
        new \Asciisd\NovaChat\NovaChat,
    ];
}
```

Build the Vue bundle inside the package:

```
cd vendor/asciisd/nova-chat
npm install
npm run build
```

(If you're consuming via a path repo / monorepo, build inside the package source directory once and rebuild when you change Vue files.)

How it works
------------

[](#how-it-works)

The package provides three interfaces. Implement them on **your** models — the package never references domain classes directly.

### 1. The host model (the "thread")

[](#1-the-host-model-the-thread)

```
use Asciisd\NovaChat\Contracts\Chattable;
use Asciisd\NovaChat\Concerns\HasChat;

class Signal extends Model implements Chattable
{
    use HasChat;

    public function chatMessages(): HasMany
    {
        return $this->hasMany(SignalMessage::class);
    }

    public function chatTitle(): string     { return $this->title; }
    public function chatSubtitle(): ?string { return strtoupper($this->order_type); }
    public function chatBadge(): ?string    { return $this->status?->value; }
}
```

### 2. The message model

[](#2-the-message-model)

```
use Asciisd\NovaChat\Contracts\ChatMessage;
use Asciisd\NovaChat\Concerns\AsChatMessage;

class SignalMessage extends Model implements ChatMessage
{
    use AsChatMessage;

    protected $fillable = ['signal_id', 'body', 'attachments', 'read_at', 'is_from_admin', 'author_type', 'author_id'];
    protected $casts    = ['attachments' => 'array', 'read_at' => 'datetime', 'is_from_admin' => 'bool'];

    public function chattable(): BelongsTo
    {
        return $this->belongsTo(Signal::class, 'signal_id');
    }
}
```

#### `is_from_admin` is auto-derived

[](#is_from_admin-is-auto-derived)

You do **not** need to set `is_from_admin` yourself when inserting messages — the `AsChatMessage` trait fills it from the author at insert time:

```
// From admin Nova UI (the package's own POST endpoint does this):
$signal->chatMessages()->create([
    'author_type' => $admin->getMorphClass(), 'author_id' => $admin->id,
    'body' => 'reply', 'is_from_admin' => true,   // explicit — kept
]);

// From your custom user-side endpoint:
$signal->chatMessages()->create([
    'author_type' => $user->getMorphClass(), 'author_id' => $user->id,
    'body' => 'question',                          // no is_from_admin → derived from $user->isChatAdmin()
]);
```

The trait only auto-derives when `is_from_admin` is **not** in the assigned attributes, so any value you explicitly pass is kept. Cost: one extra `SELECT` against the author table per `creating` event.

#### Required columns on your message table

[](#required-columns-on-your-message-table)

ColumnTypeNotes`id`bigint pkstandardFK to hostbigintname is flexible (`signal_id`, `ticket_id`, …)`author_type` / `author_id`polymorphic morph`$table->morphs('author')``body`textmessage content`is_from_admin`bool, default `false`set at write time; cheap unread queries`read_at`timestamp nullableread receipts`created_at` / `updated_at`timestampssidebar ordering + relative timeRecommended optional: `reference` (ulid), `attachments` (json).

Recommended for moderation (required if you want soft-delete via the admin endpoint): `deleted_at` (`$table->softDeletes()`), `deleted_by_type` / `deleted_by_id` (`$table->nullableMorphs('deleted_by')`), and `deletion_reason` (text nullable). Without `deleted_at` + the `SoftDeletes`trait on your message model, `DELETE /messages/{id}` returns a 422 with an actionable error.

Recommended indexes: `(fk, created_at)` and `(fk, is_from_admin, read_at)`.

A reference migration is shipped in `database/stubs/chat_messages_table.stub`, and you can scaffold a fresh one in seconds:

```
php artisan nova-chat:make-table
# prompts for table name, host model class, and FK column
# writes database/migrations/_create__table.php
```

Pass arguments to skip the prompts:

```
php artisan nova-chat:make-table order_messages --host="App\\Models\\Order"
```

### 3. Author models (Admin / User / Customer / …)

[](#3-author-models-admin--user--customer--)

```
use Asciisd\NovaChat\Contracts\ChatParticipant;
use Asciisd\NovaChat\Concerns\AsChatParticipant;

class Admin extends Authenticatable implements ChatParticipant
{
    use AsChatParticipant;

    public function isChatAdmin(): bool { return true; }
}

class User extends Authenticatable implements ChatParticipant
{
    use AsChatParticipant; // isChatAdmin() defaults to false
}
```

### 4. Register the topic in `config/nova-chat.php`

[](#4-register-the-topic-in-confignova-chatphp)

```
'admin_guard' => 'admin',

'morph_map' => [
    'admin'  => \App\Models\Admin::class,
    'user'   => \App\Models\User::class,
    'signal' => \App\Models\Signal::class,
],

'topics' => [
    'signal' => [
        'model'         => \App\Models\Signal::class,
        'message_model' => \App\Models\SignalMessage::class,
        'label'         => 'Signals',
        'icon'          => 'currency-dollar',
        'default'       => true,
    ],
],

'poll_interval_ms' => [
    'sidebar' => 4000,
    'thread'  => 3000,
],
```

Add more topics any time — the sidebar grows a tab switcher automatically.

[![Sidebar search filtering conversations by title](docs/screenshots/search.png)](docs/screenshots/search.png)

The sidebar's search box filters by `title` and `reference` on whichever topic is active.

API surface (admin auth required)
---------------------------------

[](#api-surface-admin-auth-required)

All routes live under `/nova-vendor/nova-chat/` and are protected by Nova's API middleware (which resolves the configured `admin_guard`):

MethodPathGET`/topics`GET`/topics/{topic}/conversations`GET`/topics/{topic}/conversations/{id}/messages`POST`/topics/{topic}/conversations/{id}/messages`DELETE`/topics/{topic}/conversations/{id}/messages/{message}`POST`/topics/{topic}/conversations/{id}/read`GET`/blocks`POST`/blocks`DELETE`/blocks/{participant_type}/{participant_id}`Moderation
----------

[](#moderation)

[![Per-message admin actions menu — Delete message and Block author](docs/screenshots/actions_menu.png)](docs/screenshots/actions_menu.png)

Two admin abilities, both opt-in via `config('nova-chat.moderation')`:

- **Block a participant globally.** Admins click "Block author" from any message bubble. The block is stored in the package-owned table `nova_chat_blocked_participants` and exposed on every `ChatParticipant`via `$user->isChatBlocked()`. The package's admin POST endpoint is unaffected (it only authors messages on behalf of admins) — your user-side write endpoint MUST gate on `isChatBlocked()` to actually enforce the block:

    ```
    if ($user->isChatBlocked()) {
        abort(403, 'You have been blocked from chatting.');
    }
    ```
- **Soft-delete a user message.** Add `use Illuminate\Database\Eloquent\SoftDeletes;`to your message model and migrate a `deleted_at` column (the stub does this for new tables). Admins can then click "Delete message" on any bubble; the row is soft-deleted with `deleted_by_*` and `deletion_reason`recorded for audit. Admins continue to see the row grayed-out via `withTrashed()`; the consumer's user-side endpoint naturally hides it through the SoftDeletes global scope.

Both abilities can be turned off without a code change:

```
'moderation' => [
    'allow_block'  => false,
    'allow_delete' => false,
],
```

Laravel Boost integration
-------------------------

[](#laravel-boost-integration)

This package ships AI guidelines and skills for [Laravel Boost](https://laravel.com/docs/13.x/boost):

- `resources/boost/guidelines/nova-chat.md` — high-level conventions, always loaded when Boost detects the package.
- `resources/boost/skills/nova-chat-development/SKILL.md` — on-demand integration playbook (six-step walkthrough for adding a new Chattable host, troubleshooting matrix, API contract).

Run `php artisan boost:install` (or `boost:update --discover` after `composer require`) to publish them into the consuming app.

v1 caveats
----------

[](#v1-caveats)

- **Polling only.** No Reverb/Pusher. Default cadence: sidebar 4 s, thread 3 s. Polling pauses while the tab is hidden.
- **Text only.** The `attachments` JSON column is preserved in the schema but not exposed in the v1 UI.

License
-------

[](#license)

MIT — see [LICENSE](LICENSE).

###  Health Score

45

—

FairBetter than 91% of packages

Maintenance94

Actively maintained with recent releases

Popularity14

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

7

Last Release

28d ago

Major Versions

v0.1.3 → v1.0.02026-05-12

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/77636067?v=4)[ASCII SD](/maintainers/asciisd)[@asciisd](https://github.com/asciisd)

---

Top Contributors

[![aemaddin](https://avatars.githubusercontent.com/u/11630742?v=4)](https://github.com/aemaddin "aemaddin (16 commits)")

### Embed Badge

![Health badge](/badges/asciisd-nova-chat/health.svg)

```
[![Health](https://phpackages.com/badges/asciisd-nova-chat/health.svg)](https://phpackages.com/packages/asciisd-nova-chat)
```

###  Alternatives

[ebess/advanced-nova-media-library

Laravel Nova tools for managing the Spatie media library.

6163.5M22](/packages/ebess-advanced-nova-media-library)[optimistdigital/nova-sortable

This Laravel Nova package allows you to reorder models in a Nova resource's index view using drag &amp; drop.

2862.1M6](/packages/optimistdigital-nova-sortable)[outl1ne/nova-sortable

This Laravel Nova package allows you to reorder models in a Nova resource's index view using drag &amp; drop.

2862.0M9](/packages/outl1ne-nova-sortable)[eminiarts/nova-tabs

Laravel Nova - Tabs.

4654.2M20](/packages/eminiarts-nova-tabs)[markwalet/nova-modal-response

A Laravel Nova asset for Modal responses on an action.

17818.7k](/packages/markwalet-nova-modal-response)[spatie/nova-tags-field

A tags field for Nova apps

3012.1M7](/packages/spatie-nova-tags-field)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
