Livewire

Laracord's HTTP server while implementing Laravel's routing also brings in the necessary requirements for Livewire.

While Laracord has not been thoroughly tested with everything Livewire has to offer, it has been tested with basic full page components and can be a very quick and powerful way to create a basic web interface to interact with your bot instance in real-time without Javascript.

Note

While this provides a great way to easily manage a personal bot, it is strongly discouraged to use the Laracord HTTP server to host any kind of user-facing application.

In this example, we will install Livewire and create a simple message component that is capable of showing every user visible to the bot in a select dropdown and firing off a message using the bot instance when clicking send.

Install Livewire

Start by installing Livewire using Composer:

$ composer require livewire/livewire

Once installed, you will need to generate an application key (APP_KEY). This can be done using the laracord binary:

$ php laracord key:generate

The final step is to add the Livewire service provider to providers in config/app.php:

'providers' => [
    App\Providers\BotServiceProvider::class,
    Livewire\LivewireServiceProvider::class,
],

Creating a Layout

Before creating a component, you will need to create an app.blade.php layout for Livewire to use. You can create this using livewire:layout:

$ php laracord livewire:layout

Once app.blade.php has been created, you will need to add the @livewireStyles and @livewireScripts Blade directives to the layout.

Here is a full example including Tailwind's CDN since Laracord does not include frontend tooling out of the box:

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{ $title ?? 'Laracord' }}</title>

    <script src="https://cdn.tailwindcss.com"></script>
    @livewireStyles
  </head>
  <body>
    {{ $slot }}
    @livewireScripts
  </body>
</html>

Message Component

For this example, we will create a simple full page component. We can start by using the make:livewire console command:

$ php laracord make:livewire Message

Creating a Route

After the component is created, we can create a route for it in app/Bot.php:

<?php

namespace App;

use App\Livewire\Message;
use Illuminate\Support\Facades\Route;
use Laracord\Laracord;

class Bot extends Laracord
{
    /**
     * The HTTP routes.
     */
    public function routes(): void
    {
        Route::middleware('auth')->group(function () {
            Route::get('/message', Message::class);
        });
    }
}

Note

It is strongly recommended to put routes behind the auth middleware if the bot is publicly accessible. See HTTP Server Security to learn more.

Creating the Component

Once our route is configured, we can switch over to the message component that was generated by make:livewire located in app/Livewire/Message.php and add in the logic:

<?php

namespace App\Livewire;

use Laracord\HasLaracord;
use Livewire\Attributes\Locked;
use Livewire\Component;

class Message extends Component
{
    use HasLaracord;

    /**
     * The selected user.
     *
     * @var string
     */
    public $user;

    /**
     * The message to send.
     *
     * @var string
     */
    public $message;

    /**
     * The Discord users.
     *
     * @var array
     */
    #[Locked]
    public $members;

    /**
     * Render the component.
     *
     * @return \Illuminate\View\View
     */
    public function render()
    {
        $this->members = collect($this->discord()->users->map(fn ($user) => [
            'id' => $user->id,
            'username' => $user->username,
        ]))->keyBy('id');

        return view('livewire.message');
    }

    /**
     * Send a message to the selected user.
     *
     * @return void
     */
    public function sendMessage()
    {
        $this->validate([
            'user' => 'required',
            'message' => 'required|min:3|max:150',
        ]);

        if (! $this->members->has($this->user)) {
            $this->addError('user', 'The selected user does not exist.');

            return;
        }

        $this
            ->message($this->message)
            ->sendTo($this->user);

        $this->reset('message');

        session()->flash('success', 'The message has been sent.');
    }
}

Note

Thanks to the WithLaracord trait, the methods we are used to using when writing commands are available in the Livewire component such as bot(), discord(), console(), and message().

Let's break down the above component. To start, we create our properties:

  • A $user property to wire to our user select.
  • A $message property to wire to our message textarea.
  • A locked $members property to hold the array of user's in the bot repository.

In the render() method, we set our members property by mapping the Discord users by id and username and then keying them by id. Keying them by id will be convenient for validation.

In the sendMessage() method, we start by using basic Laravel validation to check that the user and message are filled out and the message meets a generic minimum/maximum length.

Once validate() passes, we do our own quick validation to ensure that the user attempting to be messaged is in the users list and hasn't been tampered with.

Once validation is complete, we can send the message using the message() method similar to how we would in a command, reset the message textarea, and flash a success message.

Creating a View

The component view is located in resources/views/livewire/message.blade.php and will be composed of a simple select dropdown, a textarea, and a send message button. I won't go into specifics for this one:

<div class="max-w-2xl p-8 mx-auto">
  <h1 class="mb-6 text-3xl">Send a Message</h1>

  <div class="grid gap-8">
    <div>
      <select wire:model="user" class="w-full px-3 py-2 border">
        <option value="">Select a member...</option>
        @foreach ($members as $member)
          <option value="{{ $member['id'] }}">{{ $member['username'] }}</option>
        @endforeach
      </select>

      @error('user')
        <div class="mt-1 text-red-500">{{ $message }}</div>
      @enderror
    </div>

    <div>
      <textarea
        class="w-full px-3 py-2 border"
        wire:model="message"
        rows="5"
        placeholder="Enter a message..."
      ></textarea>

      @error('message')
        <div class="mt-1 text-red-500">{{ $message }}</div>
      @enderror
    </div>

    <button class="px-4 py-2 text-white bg-blue-500" wire:click="sendMessage">
      Send Message
    </button>

    @session('success')
      <div class="px-4 py-3 text-green-700 bg-green-100 rounded">
        <b>Success</b>
        <p>{{ session('success') }}</p>
      </div>
    @endsession
  </div>
</div>

Once your component is created, it should be ready and accessible at localhost:8080/messages by default once booting your bot.

Screenshot