支持 Laravel.io 的持续开发 →

通过 Discord 通知收集 Laravel 项目反馈

17 Jul, 2024 7 min read

如何在 Laravel 项目中创建一个反馈模块,并在提交消息时接收 Discord 通知。

capsules-discord-000.png

您可以在这个 Github 仓库找到示例 Laravel 项目。

在 Capsules 或 X 了解更多信息。

在网站上遇到联系表单或电子邮件地址很常见,允许用户与网站管理员联系。这些表单通常要求提供电子邮件地址、主题和标题。本文提出了一种更开放的匿名选择,用 Discord 替代这种标准格式。

按钮提供对包含反馈字段表单的访问权限,如果希望收到消息的回复,还可以可选地提供一个电子邮件地址字段。提交后,将自动生成 Discord 通知通知管理员。不会生成电子邮件,也不会在数据库中存储任何数据。

最初,我们的空白 Laravel 项目中仅配置了一条路由和一个页面。

routes/web.php

<?php

use Illuminate\Support\Facades\Route;
use Inertia\Inertia;

Route::get( '/', fn() => Inertia::render( 'Welcome' ) );

/resources/js/pages/Welcome.vue

<script setup>

import logotype from '/public/assets/capsules-logotype-background.svg';

</script>

<template>

    <div class="w-screen h-screen flex flex-col items-center justify-center text-center bg-primary-white">

        <img class="w-24 h-24" v-bind:src="logotype">

        <h1 class="mt-4 text-6xl font-bold select-none header-mode" v-text="'Capsules Codes'" />

    </div>

</template>

capsules-discord-001.png

反馈组件可以完全包含在一个 Vue 文件中。HTML 结构包括一个按钮和一个表单。以下是模块的内容。

resources/js/components/Feedback.vue

<script setup>

import { ref } from 'vue';
import { router } from '@inertiajs/vue3';
import logotype from '/public/assets/capsules-logotype.svg';

const isOpen = ref( false );
const isSent = ref( false );
const errors = ref( {} );

const message = ref( '' );
const email = ref( '' );

function toggle()
{
    if( ! isOpen.value )
    {
        message.value = '';
        email.value = '';

        isSent.value = false;
        errors.value = {};
    }

    isOpen.value = ! isOpen.value;
}

function submit()
{
    errors.value = {};

    const data = email.value ? { email : email.value, message : message.value } : { message : message.value };

    router.post( '/feedbacks', data, { onError : error => { errors.value = error; }, onSuccess : () => { isSent.value = true; } } );
}

</script>

<template>

    <div class="m-8 flex flex-col-reverse items-end space-y-reverse space-y-4">

        <button class="w-12 h-12 flex items-center justify-center" v-on:click="toggle()">

            <div v-show="! isOpen" class="w-full h-full rounded-xl bg-white flex items-center justify-center drop-shadow-2xl hover:bg-primary-blue hover:bg-opacity-5"><img class="h-8 w-8" v-bind:src="logotype"></div>

            <div v-show="! isOpen" class="absolute top-0 left-0 w-full h-full rounded-xl bg-white flex items-center justify-center animate-ping opacity-50"><img class="h-8 w-8" v-bind:src="logotype"></div>

            <svg v-show="isOpen" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 text-primary-blue"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /></svg>

        </button>

        <div v-if="isOpen">

            <div v-if="! isSent" class="font-mono rounded-xl bg-white drop-shadow-xl ">

                <div class="p-2">

                    <form class="flex flex-col" v-on:submit.prevent="submit()">

                        <label for="message" hidden />

                        <textarea
                            id="message"
                            class="mb-2 p-2 outline-none rounded-md resize-none text-xs bg-slate-100"
                            v-bind:class="{ 'border border-solid border-red-500 text-red-500' : errors && errors[ 'message' ] } "
                            type="text"
                            cols="30"
                            rows="10"
                            v-bind:placeholder="'Your message'"
                            v-model="message"
                        />

                        <div class="flex">

                            <label for="email" hidden />

                            <input
                                id="email"
                                class="px-2 grow outline-none rounded-md text-xs bg-slate-100"
                                v-bind:class=" { 'border border-solid border-red-500 text-red-500' : errors && errors[ 'mail' ] } "
                                type="text"
                                v-bind:placeholder="'Your email - Optional'"
                                v-model="email"
                            >

                            <button
                                class="ml-2 px-4 py-2 inline-flex items-center rounded-md text-sm font-medium text-primary-blue bg-primary-blue bg-opacity-50 hover:bg-opacity-60"
                                type="submit"
                            >

                                <p v-text="'Send'" />

                            </button>

                        </div>

                    </form>

                    <div>

                        <p v-for=" ( error, key ) in errors " v-bind:key="key" class="first:mt-4 ml-1 text-[10px] text-red-500" v-text="error" />

                    </div>

                </div>

            </div>

            <div v-else class="font-mono p-4 flex items-center justify-center space-x-4 bg-white rounded-xl drop-shadow-xl">

                <p class="w-full text-center text-xs text-primary-black" v-text="'Thank you for your feedback !'" />

                <p v-text="'🎉'" />

                <img class="h-8 w-8" v-bind:src="logotype">

            </div>

        </div>

    </div>

</template>

此组件表示一个按钮,当点击时,通过isOpen变量显示表单。当点击“发送”按钮时,会调用submit()方法,向/feedbacks路由发送POST请求。如果一切正常,isSent变量变为true,并在表单中替换为感谢信息。否则,错误的字段将以红色突出显示。

现在,是时候将这个组件添加到Welcome页面了。

resources/js/pages/Welcome.vue

<script setup>

import Feedback from '/resources/js/components/Feedback.vue';
import logotype from '/public/assets/capsules-logotype-background.svg';

</script>

<template>

    <Feedback class="fixed z-10 bottom-0 right-0" />

    <div class="w-screen h-screen flex flex-col items-center justify-center text-center bg-primary-white">

        <img class="w-24 h-24" v-bind:src="logotype">

        <h1 class="mt-4 text-6xl font-bold select-none header-mode" v-text="'Capsules Codes'" />

    </div>

</template>

capsules-discord-002.png

导入Feedback组件并将其放置在屏幕的右下角。现在客户端模块正在工作,是时候创建路由、实现验证并将数据发送到Discord。对于本文,不需要创建特定的控制器。

app/Http/FeedbackRequest.php

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class FeedbackRequest extends FormRequest
{
    public function rules() : array
    {
        return [
            'message' => [ 'required', 'min:1', 'max:499' ],
            'email' => [ 'sometimes', 'email' ],
        ];
    }
}

FeedbackRequest允许在数据未正确发送时返回错误。

capsules-discord-003.png

routes/web.php

<?php

use Illuminate\Support\Facades\Route;
use Inertia\Inertia;
use App\Http\Requests\FeedbackRequest;

Route::get( '/', fn() => Inertia::render( 'Welcome' ) );

Route::post( 'feedbacks', function( FeedbackRequest $request ){} );

capsules-discord-004.png

下一步是将Laravel项目连接到Discord工作区。为此,需要创建一个webhook。转到“Discord服务器设置 > 集成 > 查看webhooks > 新建Webhook”。需要一个名称和通道。

webhook现在可用,可以按下复制Webhook URL按钮复制其URL。

需要将此webhook添加到LOG_DISCORD_WEBHOOK_URL环境变量,该变量可在配置文件config/logging.php中访问。

config/logging.php

<?php

return [

    'channels' => [

        'discord' => [
		
            'driver' => 'discord',
            'url' => env( 'LOG_DISCORD_WEBHOOK_URL' )
		
        ]

    ]

];

.env

LOG_DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/{webhook-key}

现在可以从/feedbacks路由发送通知。

routes/web.php

<?php

use Illuminate\Support\Facades\Route;
use Inertia\Inertia;
use App\Http\Requests\FeedbackRequest;
use Illuminate\Support\Facades\Notification;
use App\Notifications\FeedbackReceived;

Route::get( '/', fn() => Inertia::render( 'Welcome' ) );

Route::post( 'feedbacks', fn( FeedbackRequest $request ) => Notification::route( 'discord', config( 'logging.channels.discord.url' ) )->notify( new FeedbackReceived( $request ) ) );

接下来就是创建FeedbackReceived通知。

app/Notifications/FeedbackReceived.php

<?php

namespace App\Notifications;

use Illuminate\Notifications\Notification;
use App\Http\Requests\FeedbackRequest;
use App\Notifications\Discord\DiscordChannel;
use App\Notifications\Discord\DiscordMessage;

class FeedbackReceived extends Notification
{
    private FeedbackRequest $request;

    public function __construct( FeedbackRequest $request )
    {
        $this->request = $request;
    }

    public function via() : string
    {
        return DiscordChannel::class;
    }

    public function toDiscord() : DiscordMessage
    {
        $email = $this->request->email ?? 'Anonymous';

        return ( new DiscordMessage() )->content( "New Capsules Codes Feedback : \"{$this->request->message}\" by {$email}" );
    }
}

app/Notifications/Discord/DiscordChannel.php

<?php

namespace App\Notifications\Discord;

use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Http;

class DiscordChannel
{
    public function send( object $notifiable, Notification $notification ) : void
    {
        $discordMessage = $notification->toDiscord();

        $discordWebhook = $notifiable->routeNotificationFor( 'discord' );

        Http::post( $discordWebhook, $discordMessage->toArray() );
    }
}

app/Notifications/Discord/DiscordMessage.php

<?php

namespace App\Notifications\Discord;

use Carbon\Carbon;

class DiscordMessage
{
    protected string $content = '';

    public function content( string $content ) : self
    {
        $this->content = $content;

        return $this;
    }

    public function toArray() : array
    {
        return [
        
            "embeds" => [

                [
                    "title" => $this->title,
                    "type" => "rich",
                    "timestamp" => Carbon::now(),
                    "color" => "14497651"
                ]

            ]
            
        ]; 
    }
}

capsules-discord-005.png

一个通知 strengthens出现了!

很高兴这能帮到你。

上次更新1周前。

driesvints 喜欢了这篇文章

1
喜欢这篇文章吗? 告诉作者并给他们点赞!
mho (MHO) 全职副业全栈Web开发者 | 设计师 工作地址:http://capsules.codes

你可能喜欢其他的文章

2024年3月11日

如何使用 Larastan 将您的 Laravel 应用从 0 到 9 升级

在 Laravel 应用执行之前发现错误是可能的,多亏了 Larastan,它是一个...

阅读文章
2024年7月19日

不使用特质标准化 API 响应

我发现大多数用于 API 响应的库都使用特质来实现,并且...

阅读文章
2024年7月12日

Laravel 高级:您可能不知道的 10 个顶级验证规则

您知道 Laravel 中所有可用的验证规则吗?再想想!Laravel 有许多现成的...

阅读文章

我们想要感谢这些 极佳公司 对我们的支持

您的标志在这里?

Laravel.io

Laravel 问题的解决、知识分享和社区建设的门户。

© 2024 Laravel.io - 版权所有。