I am updating a
demo chat app written with the
Laravel framework using
Reverb. I must say, it takes some getting used to, as a lot of things happen behind the scenes, using certain naming conventions and strings, and you are not able to follow it with ctrl+click from
PhpStorm. Laravel uses
Convention over Configuration, i.e. it expects you to follow certain naming conventions.
To run the app locally, you have to open 4 terminals and run:
- php artisan serve starts a built-in development server for your Laravel application. It's a convenient way to test and develop your application locally without the need for a full-fledged web server.
- php artisan queue:listen initiates a worker process to listen for and process jobs in your Laravel application's queue. It's crucial for background tasks, email sending, and other time-consuming operations.
- php artisan reverb:start initiates the Laravel Reverb WebSocket server, enabling real-time communication between your Laravel application and clients.
- npm run build optimizes the code for performance, minifies it, and bundles it into a single file or multiple files, making it suitable for deployment to a web server.
Passing a variable from controller to blade view:
Controller passing $users to view:
$users = User::where('id', '!=', Auth::id())->get();
return view('proceed', compact('users'));
Using $users in view:
<select id="userSelect" class="form-control">
<option value="" disabled selected>Select user...</option>
@foreach($users as $user)
<option value="{{ $user->id }}">{{ $user->name }}</option>
@endforeach
</select>
Naming conventions are very important. For example, if you want to use parameters in routes/channels.php, you have to use the following syntax:
Broadcast::channel('channelBetweenUsers.{user1_id}.{user2_id}', function ($user, $user1_id, $user2_id) {
return (int) $user->id === (int) $user1_id || (int) $user->id === (int) $user2_id;
});
The call order is broadcastOn(), new PrivateChannel("channelName") → routes/channels.php, Broadcast::channel("channelName"). If there is no matching channelName in routes/channels.php, the communication will fail.
The
$user1_id, and $user2_id parameters are passed by
broadcastOn() in
new PrivateChannel call parameter.
$user represents the authenticated user and is always available. The other parameters, $user1_id and $user2_id, are extracted from the channel name and passed to the callback function in Broadcast::channel(...).
Laravel accessor methods allow you to define logic that will be executed when an attribute is accessed, enabling you to perform calculations, transformations, or other operations before returning the value. PhpStorm will mark them with "no usages".
Frontend (with React) rendering call order: HomeController.php → home.blade.php → app.blade.php → app.js → Main.jsx → ChatBox.jsx → Message.jsx, MessageInput.jsx
Middleware is a mechanism to intercept and modify HTTP requests before they reach the application's routes and controllers. It provides a flexible way to implement cross-cutting concerns like authentication, authorization, rate limiting, logging.
The __() function is a Laravel helper function used for translating text. It looks up the given key (e.g. {{ __('Logout') }}) in the localization files and returns the appropriate translation based on the current locale, e.g. 'Çıkış'.
When you want to execute a job sending and receiving messages asynchronously, you can use queues. A queue driver acts as a bridge between your Laravel application and the underlying queue system. It handles the communication and management of tasks or jobs that are placed on the queue. A queue worker is a process that continuously monitors the queue for new jobs. Once a job is available, it processes the job and removes it from the queue. The simplest way is to use the database as a queue driver. However, database operations, especially writes, can be relatively slow. Using a database as a queue driver could introduce noticeable delays in message delivery, especially as the number of users and messages increases. But until you pass 100 users messaging each other in the same time frame (concurrent), a database can handle it.
For higher concurrent messaging, you can use Redis as your queue driver, which is an in-memory data store that excels at handling high-throughput, low-latency tasks. But it adds to setup complexity because you have install the Redis server and configure it (hostname, port, authentication). You still need to issue php artisan queue:listen command to connect the Redis queue to your app.
While RabbitMQ can also be used for chat applications, it might introduce unnecessary complexity and overhead, especially for smaller-scale chat apps. Redis' focus on pub/sub and in-memory storage makes it a more suitable choice for this particular use case.
Messaging steps:
- Client1 sends a message to Client2 via POST request → web server → router → controller
- Controller adds message to queue via SendMessage::dispatch($message)
- Queue driver stores the job.
- A queue worker gets the job from the stored job queue (via polling for database or polling/push notification for Redis).
- Worker processes job via SendMessage::handle(), obtaining the WebSocket channel via GotMessage::dispatch() → constructor → broadcastOn()
- The worker broadcasts the message to all connected clients via WebSocket channel. The worker maintains a persistent WebSocket connection to the Reverb server, allowing it to broadcast messages efficiently, avoid unnecessary routing through the controller.
- Client 2 receives the message via WebSocket.
Traits vs Interface: Traits provide reusable methods and properties, the class using the trait does not have to call or implement it. Interfaces define a contract for method signatures that must be implemented. Traits help overcome PHP's single inheritance limitation by allowing code reuse across unrelated classes.
Queues vs Threads: Queues handle background tasks that do not need immediate execution (e.g., sending notifications or processing data). Threads are more suitable for scenarios requiring simultaneous execution of tasks that are tightly coupled or need to share state. PHP does not have built-in support for multithreading, it can utilize extensions such as parallel for multithreaded capabilities.
PHP
constructor property promotion:
class MyClass {
public function __construct(public int $val) {
// No need to manually assign $this->val = $val;
}
}
$obj = new MyClass(5);
echo $obj->val; // Output: 5
Queue workers vs defer(): defer() does not guarantee that messages will be processed in order or without conflicts, especially if multiple users are sending messages simultaneously. Queue workers offer better reliability for handling failed jobs, retries, and monitoring. While defer() might seem like a quick solution, utilizing queue workers is the recommended approach for handling message sending and receiving in a Laravel Reverb chat application to ensure optimal performance and responsiveness.
php artisan down: Puts the application into maintenance mode, all incoming HTTP requests will receive a 503 "Service Unavailable" response, and a maintenance mode page will be displayed. This is useful when you need to perform maintenance or updates on your Laravel application and you want to prevent users from accessing the site during that time. To take the application out of maintenance mode, you can use the php artisan up command.