Message
Message represents an individual chat item inside a conversation.
Wirechat now treats participant_id as the source of truth for ownership and sender identity, so this model should stay aligned with the participant layer.
Core fields:
| Field | Purpose |
|---|---|
conversation_id |
Parent conversation id. |
participant_id |
Sender participant id. |
reply_id |
Parent message reference for threaded replies. |
body |
Message text or link content. |
type |
Message type enum such as text, link, or attachment. |
kept_at |
Timestamp used to preserve a disappearing message. |
deleted_at |
Soft-delete timestamp for delete-for-everyone flows. |
Wirechat will automatically keep the type aligned with the message body.
If the body contains a recognized URL, the message is marked as link. If the body no longer contains a URL,
the type falls back to text. Attachment messages remain attachment regardless of body changes.
Important Relationships
conversation(): BelongsTo
Returns the parent conversation.
$message->conversation
participant(): BelongsTo
Returns the sender participant row.
$message->participant
attachment(): MorphOne
Returns the attachment model when the message is an attachment message.
$message->attachment
parent(): BelongsTo
Returns the message this message replies to.
$message->parent
reply(): HasOne
Returns a reply to this message when one exists.
$message->reply
actions(): MorphMany
Returns actions recorded against the message, including delete-for-user actions.
$message->actions()->latest()->get()
Important Accessors And Helpers
$message->user
Returns the underlying app model behind the sender participant.
$sender = $message->user
$message->sendable
Legacy accessor alias for $message->user. New code should prefer $message->user.
$message->sendable
$message->sendable_id
Legacy accessor for the underlying participantable id. New code should usually work through $message->participant or $message->user instead.
$message->sendable_id
$message->sendable_type
Legacy accessor for the underlying participantable morph type. New code should usually work through $message->participant or $message->user instead.
$message->sendable_type
$message->resolved_link
Normalizes the message body into a full URL when it is a standalone link. Returns null for mixed-content messages.
$url = $message->resolved_link
hasAttachment(): bool
Checks whether the message currently has an attachment record.
$message->hasAttachment()
isAttachment(): bool
Checks whether the message type is attachment.
$message->isAttachment()
isLink(): bool
Checks whether the message type is link. This is set automatically when the message body contains a URL.
$message->isLink()
readBy(Model|Participant $user): bool
Checks whether the message is effectively read for a participant or app model.
$message->readBy(auth()->user())
ownedBy(Model $user): bool
Checks whether the given user owns the message by comparing against the sender participant.
$message->ownedBy(auth()->user())
belongsToAuth(): bool
Checks whether the currently authenticated user owns the message.
$message->belongsToAuth()
hasReply(): bool
Checks whether this message already has a reply.
$message->hasReply()
hasParent(): bool
Checks whether this message is itself a reply to another message.
$message->hasParent()
deleteFor(Model|Authenticatable $user): ?bool
Deletes the message only for one participant and may fully delete it once package rules are satisfied.
$message->deleteFor(auth()->user())
deleteForEveryone(Model $user): void
Deletes the message globally when the actor owns it or when a group admin is allowed to do it.
$message->deleteForEveryone(auth()->user())
isEmoji(): bool
Checks whether the message body contains only emoji characters.
$message->isEmoji()
Important Query Helpers
whereIsNotOwnedBy(Model|Authenticatable $user)
Filters out messages owned by the given participantable model.
$conversation->messages()->whereIsNotOwnedBy(auth()->user())->get()