Sir George
Sir George

Reputation: 433

Laravel inter-user messaging system

I am developing a messaging system for my app, and currently, I have two tables:

1) messages table:

Schema::create('messages', function (Blueprint $table) {
    $table->increments('id');
    $table->text('subject');
    $table->integer('sender_id');
    $table->text('body');
    $table->timestamps('create_date');
    $table->integer('sent_to_id');
});

2) users table:

Schema::create('users', function (Blueprint $table) {
    $table->increments('id');
    $table->string('name');
    $table->string('email')->unique();
    $table->string('password');
    $table->rememberToken();
    $table->timestamps();
});

Currently, I can store messages and their subjects to the messages table. Now, I want to pick the id of the user I'm sending the message to. So that I can use it to create a conversation.

Is there any way I can list all the users and pick the preferred user that I want to send message to? or anyone with a messaging technique to help out?

Upvotes: 3

Views: 8699

Answers (1)

sepehr
sepehr

Reputation: 18445

Your question is a bit vague and too broad. Anyway, I assume you want to enable users to send messages to other existing users in the system.

Before we start, please note that there are a lot of design decisions involved. I'll simply go with the easiest and quickest approach as it's just a demonstration to get you started.

In order to enable users to send messages to each other, you need to setup foreign keys in your database migrations, setup eloquent relationships and create helper methods to be used in your controller classes.

1. Foreign keys

Pretty self-explanatory; but if you have no idea what I'm talking about, have a look at the documentation on foreign key constraints.

Your users table migration needs no change for now, so I skip ahead to messages table migration:

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateMessagesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('messages', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('sender_id');
            $table->integer('sent_to_id');
            $table->text('body');
            $table->text('subject');

            // It's better to work with default timestamp names:
            $table->timestamps(); 

            // `sender_id` field referenced the `id` field of `users` table:
            $table->foreign('sender_id')
                  ->references('id')
                  ->on('users');

            // Let's add another foreign key on the same table,
            // but this time fot the `sent_to_id` field:
            $table->foreign('sent_to_id')
                  ->references('id')
                  ->on('users');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('messages');
    }
}

This sets up foreign key constraints on your messages table as the comments explain. Don't forget to: php artisan migrate:refresh [-seed].

2. Model relationships

First, have a look at the documentation on eloquent relationships. Then see how we setup the relationship between two of your models; Message and User.

Here's your App\Message class:

<?php

namespace App;

use App\User;
use Illuminate\Database\Eloquent\Model;

class Message extends Model
{
    protected $fillable = ['body', 'subject', 'sent_to_id', 'sender_id'];

    // A message belongs to a sender
    public function sender()
    {
        return $this->belongsTo(User::class, 'sender_id');
    }

    // A message also belongs to a receiver    
    public function receiver()
    {
        return $this->belongsTo(User::class, 'sent_to_id');
    }
}

And your App\User class:

<?php

namespace App;

use App\Message;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use Notifiable;

    protected $fillable = ['name', 'email', 'password'];

    // A user can send a message
    public function sent()
    {
        return $this->hasMany(Message::class, 'sender_id');
    }

    // A user can also receive a message
    public function received()
    {
        return $this->hasMany(Message::class, 'sent_to_id');
    }
}

With these files in place, you'll have a solid eloquent API at your disposal. Look at these examples:

$user->sent       // All messages sent by this user
$user->received   // All messages received by this user

App\User::has('sent')->get() // Retrieve all users that have at lest one sent message
App\User::has('sent', '>', 2)->get() 

$user->sent()->where('subject', 'Happy new year!')->first();

See the documentation for more examples or play with them in php artisan tinker to see how powerful it is.

3. Messaging implementation

Now that you have your migrations and models in place. You need to utilize them to implement your inter-user messaging system.

This needs a lot of boilerplate code like routes, controllers, and views that I'm not gonna write down here. But I'm sure you can find your way through. I just add a sample usage example here.

I assume you gonna need a controller in order to let a user send a message to another one. Here's an example:

<?php

namespace App\Http\Controllers;

use Auth;
use App\User;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class ConversationController extends Controller
{
    public function sendMessage(Request $request)
    {
        // Do the validation...

        // Send the message from the current user to the user with ID of 1,
        // You probably always want the current logged-in user as the sender.
        // We talk about the recipient later...
        //
        Auth::user()->sent()->create([
            'body'       => $request->body,
            'subject'    => $request->subject,
            'sent_to_id' => 1,
        ]);   

        // Set flash message, render view, etc...
    }
}

This will send a message from the logged-in user to the user with ID of 1. Your question is was mainly about the recipient and how you can list users and choose from them. We'll get to that later.

4. Clean up

This works, but it's not so clean. Let's wrap the logic in a model method instead:

class User extends Authenticatable
{
    // ...

    public function sendMessageTo($recipient, $message, $subject)
    {
        return $this->sent()->create([
            'body'       => $message,
            'subject'    => $subject,
            'sent_to_id' => $recipient,
        ]);   
    }
}

Add this method to your App\User model and have your controller code like this:

Auth::user()->sendMessageTo(1, $request->subject, $request->body);

Better, right?

4. Recipients

The only remaining question is about the recipient ID. You probably have a form that users use to compose messages. It probably has a textarea named body and subject input named subject. You just need another input, so that the user can pass the ID of the recipient user as well. Then you just say:

Auth::user()->sendMessageTo($request->recipient, $request->subject, $request->body);

You probably want to use a dropdown (or an autocomplete widget) that lists the names of users. When rendering the form, you can add pass your users data to the view like this:

<?php

namespace App\Http\Controllers;

use Auth;
use App\User;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class ConversationController extends Controller
{
    public function composeMessage()
    {
        // ...

        // Get a collection of `[id => name]` probable recipients,
        // so that the logged-in user can choose from. Note that 
        // you probably want to exclude the current user herself
        // from the list.
        $users = User::where('id', '!=', Auth::id())->pluck('name', 'id');

        return view('view.name', compact('users'));
    }
}

Then, in your view you can render this data like this:

{{ Form::select('recipient', $users) }}
// See: https://laravelcollective.com/docs/5.3/html

Easy, right? It's untested code but hopefully, it gives you the idea.

After all this, why not using a well-designed, well-tested messaging package instead? Meet Laravel Messenger.

Upvotes: 13

Related Questions