Conversation
Conversation is the top-level thread model.
Every private chat, self chat, and group chat starts here.
This record coordinates participants, messages, disappearing-message state, and conversation-level visibility rules.
Core fields:
| Field | Purpose |
|---|---|
id |
Primary key for the thread. |
type |
Conversation type enum: private, self, or group. |
disappearing_started_at |
When disappearing mode was enabled. |
disappearing_duration |
Disappearing duration in seconds. |
meta |
Nullable JSON metadata for low-risk app-specific or provider-specific data. |
created_at |
Thread creation timestamp. |
updated_at |
Latest activity timestamp used by unread and delete or clear logic. |
Important Relationships
participants(): HasMany
Returns the membership rows for everyone in the conversation.
$conversation->participants()->with('participantable')->get()
messages(): HasMany
Returns the messages that belong to the conversation.
$conversation->messages()->latest()->get()
lastMessage(): HasOne
Returns the latest message for previews and list ordering.
$conversation->lastMessage
group(): HasOne
Returns the group metadata row when the conversation is a group thread.
$conversation->group
attachments(): Builder
Builds an attachment query for all message attachments in the conversation.
$conversation->attachments()->latest()->get()
actions(): MorphMany
Returns actions recorded directly against the conversation model.
$conversation->actions()->latest()->get()
Important Helpers
participant(Model|Authenticatable $user, bool $withoutGlobalScopes = false): ?Participant
Resolves the participant row for a given app model.
$conversation->participant(auth()->user())
addParticipant(Model $user, ParticipantRole $role = ParticipantRole::PARTICIPANT, bool $undoAdminRemovalAction = false): Participant
Adds a participant while enforcing private, self, and admin-removal rules.
$conversation->addParticipant($user)
peerParticipant(Model|Authenticatable $reference): ?Participant
Resolves the other participant in a private conversation.
$conversation->peerParticipant(auth()->user())
peerParticipants(Model $reference): Collection
Returns every participant except the given reference model.
$conversation->peerParticipants(auth()->user())
getReceiver()
Resolves the other side of a private conversation for UI usage. This helper depends on an authenticated user being available internally, so it is best used inside auth-aware UI flows. When you already have the current user model, prefer peerParticipant() because it accepts the current user explicitly.
$peerParticipant = $conversation->peerParticipant(auth()->user())
markAsRead(?Model $user = null): void
Updates conversation_read_at for the given user or the authenticated user.
$conversation->markAsRead()
readBy(Model|Participant $user): bool
Checks whether the conversation is fully read for a participant.
$conversation->readBy(auth()->user())
getUnreadCountFor(Model $model): int
Counts unread messages for a given participantable model.
$conversation->getUnreadCountFor(auth()->user())
hasDisappearingTurnedOn(): bool
Returns true when disappearing mode is active.
$conversation->hasDisappearingTurnedOn()
turnOnDisappearing(int $durationInSeconds): void
Enables disappearing messages and currently requires at least one hour.
$conversation->turnOnDisappearing(86400)
turnOffDisappearing(): void
Disables disappearing messages.
$conversation->turnOffDisappearing()
deleteFor(Model|Authenticatable $user): ?bool
Deletes the conversation for one participant and may fully delete the record when package rules allow.
$conversation->deleteFor(auth()->user())
hasBeenDeletedBy(Model|Authenticatable $user): bool
Checks whether delete-for-user is still active for a participant.
$conversation->hasBeenDeletedBy(auth()->user())
clearFor(Model|Authenticatable $user): void
Clears conversation history for one participant without fully deleting the thread.
$conversation->clearFor(auth()->user())
isPrivate(): bool
Returns true when the conversation is a private conversation between two different users.
$conversation->isPrivate()
isSelf(): bool
Returns true when the conversation is a self conversation for a single user.
$conversation->isSelf()
isGroup(): bool
Returns true when the conversation is a group conversation.
$conversation->isGroup()
isOwner(Model|Authenticatable $model): bool
Checks whether the given user is the owner of the conversation. In group conversations this is the group owner. In private conversations both participants are effectively owners, so this can return true for either side.
$conversation->isOwner(auth()->user())
isAdmin(Model|Authenticatable $model): bool
Checks whether the given user has admin-level access in the conversation. In groups this returns true for admins and for the owner, because the owner is also treated as an admin.
$conversation->isAdmin(auth()->user())
Important Query Helpers
whereParticipantable(Model $participantable)
Filters conversations that contain a specific model as participant.
Conversation::query()->whereParticipantable(auth()->user())->get()
whereHasParticipant($userId, $userType)
Filters conversations by raw participantable id and morph type.
Conversation::query()->whereHasParticipant($user->getKey(), $user->getMorphClass())->get()
withoutBlanks()
Hides conversations with no visible messages for the authenticated user.
Conversation::query()->withoutBlanks()->get()
withoutCleared()
Hides conversations cleared by the authenticated participant until new activity appears.
Conversation::query()->withoutCleared()->get()
withoutDeleted()
Hides conversations deleted by the authenticated participant until new activity appears.
Conversation::query()->withoutDeleted()->get()
withDeleted()
Includes conversations even if the authenticated participant has deleted them.
Conversation::query()->withDeleted()->get()
Aggregates And Eager Loading
Use Eloquent relationship counts when you need message or member totals for conversation lists:
use Wirechat\Wirechat\Models\Conversation;
$conversations = Conversation::query()
->withCount(['messages', 'participants'])
->get();
foreach ($conversations as $conversation) {
$conversation->messages_count;
$conversation->participants_count;
}
Use eager loading to keep custom conversation lists from running the same relationship queries repeatedly:
$conversations = $user->conversations()
->with(['lastMessage', 'participants.participantable'])
->get();
When rendering a full conversation transcript, load the message relationships you need for that view:
$conversation->load(['messages.participant.participantable', 'messages.attachment']);
Legacy helpers receiverParticipant() and authParticipant() still exist, but new code should prefer peerParticipant() and participant().
Typical safe customizations:
- Add app-specific relations such as tickets, orders, or inbox metadata.
- Add accessors for labels or badges.
- Add custom scopes for conversation lists.
- Keep
participants()andmessages()intact because most of Wirechat depends on them.