Skip to main content

Overview

The Messages module provides a complete communication platform for client interaction. Track conversations, manage unread messages, and maintain chat history with integrated client profiles.

Chat Interface

Real-time chat interface with sent/received message tracking

Unread Counter

Visual indicators for unread messages requiring attention

Client Integration

Direct linking to client records for context

Message Types

Distinguish between sent and received messages

Mensaje Model

The Mensaje model handles all message operations:
app/Models/Mensaje.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Mensaje extends Model
{
    use HasFactory;

    protected $fillable = [
        'cliente_id',
        'contenido',
        'tipo',
        'leido',
    ];

    protected $casts = [
        'leido' => 'boolean',
    ];

    public function cliente()
    {
        return $this->belongsTo(Cliente::class);
    }
}

Model Relationships

Each message belongs to a client (belongsTo):
public function cliente()
{
    return $this->belongsTo(Cliente::class);
}
Usage Example:
$mensaje = Mensaje::find(1);
$cliente = $mensaje->cliente;
echo $cliente->nombre . ' ' . $cliente->apellido;
echo $cliente->email;

Boolean Casting

The model automatically casts the leido field to boolean:
protected $casts = [
    'leido' => 'boolean',
];
This allows clean boolean operations:
$mensaje = Mensaje::find(1);

// Check if read
if ($mensaje->leido) {
    echo 'Message has been read';
}

// Mark as read
$mensaje->leido = true;
$mensaje->save();

Database Schema

The mensajes table structure from the migration:
database/migrations/2026_03_03_191720_create_mensajes_table.php
Schema::create('mensajes', function (Blueprint $table) {
    $table->id();
    $table->foreignId('cliente_id')->constrained('clientes')->onDelete('cascade');
    $table->text('contenido');
    $table->enum('tipo', ['enviado', 'recibido'])->default('recibido');
    $table->boolean('leido')->default(false);
    $table->timestamps();
});

Table Fields

FieldTypeConstraintsDescription
idbigintPrimary keyUnique message identifier
cliente_idforeignIdRequiredReference to clients table
contenidotextRequiredMessage content/body
tipoenumDefault: ‘recibido’Message direction
leidobooleanDefault: falseRead status
timestampstimestampsAutocreated_at, updated_at

Foreign Key Constraint

Cascade Deletion: When a client is deleted, all their messages are automatically removed from the system.

Message Types

Received MessagesMessages sent by clients to the system/admin. This is the default type.
Mensaje::where('tipo', 'recibido')->get();
Displayed as:
  • Left-aligned chat bubbles
  • Client message style
  • Default status for new messages

Read Status

Messages track whether they have been read:

Unread (leido: false)

New messages requiring attention
Mensaje::where('leido', false)->count();
// Returns: 5 unread messages

Read (leido: true)

Messages that have been viewed
Mensaje::where('leido', true)->count();
// Returns: 229 read messages

Message View Features

The messages interface (mensajes.blade.php) provides:

Dashboard Metrics

Received

234 total messages in inbox

Unread

5 messages requiring attention

Sent

89 messages sent this month

Archived

412 archived messages

Split-Panel Interface

The interface is divided into two main sections:
Inbox ViewDisplays all conversations with:
  • Client name and avatar icon
  • Message preview
  • Timestamp (10:32 AM, Yesterday, etc.)
  • Unread badge for new messages
  • Scrollable list (max-height: 420px)
Features:
  • Search functionality
  • Click to open conversation
  • Visual distinction for unread messages

Notification Badge

The page header displays unread message count:
<h1>Mensajes <span class="badge bg-danger">5 nuevos</span></h1>

Usage Examples

Creating a New Message

use App\Models\Mensaje;

// Received message from client
$mensaje = Mensaje::create([
    'cliente_id' => 1,
    'contenido' => '¿Cuándo llega mi pedido?',
    'tipo' => 'recibido',
    'leido' => false
]);

// Sent message to client
$respuesta = Mensaje::create([
    'cliente_id' => 1,
    'contenido' => 'Tu pedido llegará mañana.',
    'tipo' => 'enviado',
    'leido' => true // Sent messages are already "read"
]);

Querying Messages with Client Data

// Get all unread messages with client info
$mensajes_sin_leer = Mensaje::with('cliente')
    ->where('leido', false)
    ->where('tipo', 'recibido')
    ->orderBy('created_at', 'desc')
    ->get();

foreach ($mensajes_sin_leer as $mensaje) {
    echo $mensaje->cliente->nombre . ': ' . $mensaje->contenido;
}

Marking Messages as Read

// Mark single message as read
$mensaje = Mensaje::find(1);
$mensaje->leido = true;
$mensaje->save();

// Mark all client messages as read
Mensaje::where('cliente_id', 1)
    ->where('leido', false)
    ->update(['leido' => true]);

Getting Client Conversation

// Get all messages for a specific client
$conversacion = Mensaje::where('cliente_id', 1)
    ->orderBy('created_at', 'asc')
    ->get();

foreach ($conversacion as $mensaje) {
    $tipo = $mensaje->tipo === 'recibido' ? 'Client' : 'Admin';
    echo "[{$tipo}] {$mensaje->contenido} ({$mensaje->created_at->format('H:i')})";
}

Unread Message Counter

// Total unread messages
$total_sin_leer = Mensaje::where('leido', false)
    ->where('tipo', 'recibido')
    ->count();

// Unread messages per client
$clientes_con_sin_leer = Mensaje::select('cliente_id', DB::raw('count(*) as total'))
    ->where('leido', false)
    ->where('tipo', 'recibido')
    ->groupBy('cliente_id')
    ->with('cliente')
    ->get();

Recent Messages

use Carbon\Carbon;

// Messages from today
$hoy = Mensaje::whereDate('created_at', Carbon::today())
    ->with('cliente')
    ->orderBy('created_at', 'desc')
    ->get();

// Messages from this week
$esta_semana = Mensaje::whereBetween('created_at', [
    Carbon::now()->startOfWeek(),
    Carbon::now()->endOfWeek()
])->count();

Client Activity

// Check if client has unread messages
$cliente = Cliente::find(1);
$tiene_sin_leer = $cliente->mensajes()
    ->where('leido', false)
    ->where('tipo', 'recibido')
    ->exists();

// Get last message from client
$ultimo_mensaje = $cliente->mensajes()
    ->latest()
    ->first();

Key Features

Text Storage

Messages stored as TEXT for unlimited length

Direction Tracking

Distinguish between sent and received messages

Boolean Read Status

Efficient true/false read tracking

Client Integration

Direct relationship with client records

Default Values

New messages default to ‘recibido’ and unread

Cascade Deletion

Auto-delete when client is removed

Timestamp Tracking

Automatic created_at for chronological sorting

Unread Counter

Real-time notification badge

Chat Interface

Split-panel design for conversations

Best Practices

Real-Time Updates: Consider implementing WebSockets (Laravel Echo + Pusher) for real-time message delivery.Auto-Read on View: Automatically mark messages as read when the conversation is opened.Notification System: Send email or push notifications for new unread messages.Message Archive: Implement soft deletes for message archiving instead of permanent deletion.Search Functionality: Add full-text search across message content for better findability.

Integration Example

Combining messages with client data:
// Get clients with unread messages
$clientes_activos = Cliente::whereHas('mensajes', function($query) {
    $query->where('leido', false)
          ->where('tipo', 'recibido');
})
->withCount(['mensajes as mensajes_sin_leer' => function($query) {
    $query->where('leido', false)
          ->where('tipo', 'recibido');
}])
->orderBy('mensajes_sin_leer', 'desc')
->get();

foreach ($clientes_activos as $cliente) {
    echo "{$cliente->nombre}: {$cliente->mensajes_sin_leer} unread";
}

Build docs developers (and LLMs) love