使用 Termwind 增强您的 Artisan 命令
发布于 作者: Ashley Allen
如果您正在构建 Laravel 应用程序、软件包或 CLI(命令行界面)应用程序,您很可能会在某个阶段创建自己的自定义 Artisan 命令。创建这些命令时,您可能希望使输出独特并从其他控制台输出中脱颖而出。为此,您可以使用很棒的 Termwind 软件包。
在本文中,我们将介绍 Termwind 是什么,如何安装它以及如何在自己的 Artisan 命令中使用它。然后,我们将逐步更新旧的示例 Artisan 命令以使用 Termwind,并了解它如何改善输出。
什么是 Termwind?
Termwind 是由 Nuno Maduro(以及其他很棒的贡献者,例如 Francisco Madeira)创建和维护的 PHP 软件包,它允许您在 PHP 代码中使用类似 Tailwind 的 CSS 类来为 CLI 输出添加样式。
如果您希望使命令的输出看起来独特并从人群中脱颖而出,这是一个非常棒的工具。因此,如果您正在构建 CLI 应用程序或提供任何 Artisan 命令的 Laravel 软件包,它非常方便使用。
它为您提供了使用 HTML 和类似 Tailwind 的类(例如 text-blue-500
、flex
和 space-x-1
)构建输出的能力。您可以在 软件包的文档中查看可用类的完整列表。
用法
安装
要开始在 Laravel 应用程序中使用 Termwind,您需要使用 Composer 安装它,方法是运行以下命令
composer require nunomaduro/termwind
就是这样!Termwind 现在已安装并可以使用。
使用内联 HTML 显示输出
现在我们已经安装了 Termwind,让我们看看如何使用它将一些输出呈现到 CLI。
呈现输出的最快方法是将一些 HTML 作为字符串直接传递给软件包的 render
函数。
为了更好地理解这一点,让我们看一个简单的例子。假设我们有一个 Artisan 命令,我们可以使用它来输出有关 Laravel 应用程序的一些统计信息。为了便于本文的理解,我们将使用硬编码的统计信息,这样我们就可以专注于 Termwind 本身。
那么,让我们看一下该命令
namespace App\Console\Commands; use Illuminate\Console\Command;use function Termwind\{render}; class AppStats extends Command{ protected $signature = 'app:stats'; protected $description = 'Display the application stats'; public function handle(): int { render(<<<'HTML' <div class="mx-2 my-1"> <div class="space-x-1"> <span class="px-1 bg-blue-500 text-white">Application Info</span> </div> <div class="mt-1"> <span class="font-bold text-green">Totals</span> <div class="flex space-x-1"> <span class="font-bold">Users</span> <span class="flex-1 content-repeat-[.] text-gray"></span> <span class="font-bold text-green">150</span> </div> <div class="flex space-x-1"> <span class="font-bold">Posts</span> <span class="flex-1 content-repeat-[.] text-gray"></span> <span class="font-bold text-green">200</span> </div> <div class="flex space-x-1"> <span class="font-bold">Comments</span> <span class="flex-1 content-repeat-[.] text-gray"></span> <span class="font-bold text-green">175</span> </div> </div> <div class="mt-1"> <span class="font-bold text-green">Health Checks</span> <div class="flex space-x-1"> <span class="font-bold">Mailcoach</span> <i class="text-gray">Newsletter</i> <span class="flex-1 content-repeat-[.] text-gray"></span> <span class="font-bold text-green">CONNECTED</span> </div> <div class="flex space-x-1"> <span class="font-bold">Vonage</span> <i class="text-gray">SMS</i> <span class="flex-1 content-repeat-[.] text-gray"></span> <span class="font-bold text-red">ERROR!</span> </div> </div> </div> HTML); return self::SUCCESS; }}
上面的命令将以下内容输出到 CLI
如您所见,我们可以使用 Termwind 生成一些非常酷的输出。但是,将 HTML 直接放在命令类中会很快变得很乱。它可读性或可维护性不强。例如,如果我们想在其他命令中使用相同的样式,我们可能会重复很多共享的 HTML。
不过,需要注意的是,对于少量输出,这种方法可能完全可以。只是当您开始拥有大量输出时,维护起来会变得有点乏味。
Termwind 的 style
函数可以帮助我们将样式分组在一起,以便我们可以在多个地方重复使用它们。但是,这并不一定解决构建输出结构的实际 HTML 的问题。我建议您查看 文档中的 style
函数,以了解如何使用它来简化维护工作。
使用视图显示输出
既然我们已经了解了如何直接将一些 HTML 输出到 CLI,让我们看看如何使用 Blade 视图来呈现输出。我更喜欢使用这种方法,因为它使 HTML 与命令类分离,并使跨不同命令的维护和重复使用变得更容易。
首先,让我们在项目的 resources/views
目录中创建一个新的 cli
目录。我们将在此处存储所有特定于 CLI 的视图。
然后,我们将在 cli
目录中创建一个新的 app-stats.blade.php
视图。这将是我们用来呈现 app:stats
命令输出的视图,它将包含我们之前示例中的 HTML(<<<'HTML'
和 HTML
之间的所有内容)。
现在我们已经将 HTML 放置在 Blade 视图中,我们可以更新命令类以使用它。我们将通过使用 view
帮助器函数来渲染视图并将它的输出传递给 Termwind 的 render
函数来实现这一点
namespace App\Console\Commands; use Illuminate\Console\Command;use function Termwind\{render}; class AppStats extends Command{ protected $signature = 'app:stats'; protected $description = 'Display the application stats'; public function handle(): int { render(view('cli.app-stats')); return self::SUCCESS; }
如您所见,这使得命令类更容易理解和维护。我们现在可以专注于命令的逻辑,而不必担心 HTML 混乱这个类。
使用这种方法的一个巨大的好处是,它还允许我们使用 Blade 组件来使我们的输出更具可重复使用性。我们可以为输出的不同部分创建组件,然后在我们的视图中使用它们。
让我们看看如何使用一些 Blade 组件来提高命令的可维护性。
我们将从在 resources/views
目录中创建一个新的 components/cli
目录开始。类似于我们的 resources/views/cli
目录,我们将在此处保存所有特定于 CLI 的 Blade 组件。
我们可以在上面的示例中确定命令输出的两个主要部分,可以将其拆分为组件
- 总计
- 健康检查
因此,我们将为每个部分创建一个组件。请记住,您可以根据项目的需要创建尽可能少或尽可能多的组件。
让我们从在 resources/views/components/cli
目录中创建一个新的 totals.blade.php
组件开始。此组件将用于呈现我们拥有的每个统计信息的总计。
@props([ 'title', 'value',]) <div class="flex space-x-1"> <span class="font-bold">{{ $title }}</span> <span class="flex-1 content-repeat-[.] text-gray"></span> <span class="font-bold text-green">{{ $value }}</span></div>
如您所见,此组件非常简单,并使用 @props
Blade 指令定义了 2 个不同的属性。它接受两个属性,title
和 value
。这是一种确保我们不会忘记将任何必需属性传递给组件的好方法。
我们现在还可以在 resources/views/components/cli
目录中创建一个新的 connection.blade.php
组件
@props([ 'title', 'subText', 'connected' => false,]) <div class="flex space-x-1"> <span class="font-bold">{{ $title }}</span> <i class="text-gray">{{ $subText }}</i> <span class="flex-1 content-repeat-[.] text-gray"></span> @if($connected) <span class="font-bold text-green">CONNECTED</span> @else <span class="font-bold text-red">ERROR!</span> @endif</div>
您可能已经注意到,该组件使用 @props
Blade 指令定义了 3 个不同的属性。这强制我们在使用该组件时必须传递一个 title
属性、subText
属性和一个可选的 connected
属性。
正如我们在之前的示例中看到的那样,如果 connected
属性设置为 true
,则该组件将以绿色输出 CONNECTED
文本。否则,它将以红色输出 ERROR!
文本。
现在我们已经创建了组件并已准备就绪,我们可以将 resources/views/cli/app-stats.blade.php
视图转换为使用它们
<div class="mx-2 my-1"> <div class="space-x-1"> <span class="px-1 bg-blue-500 text-white">Application Info</span> </div> <div class="mt-1"> <span class="font-bold text-green">Totals</span> <x-cli.stat title="Users" value="150" /> <x-cli.stat title="Posts" value="200" /> <x-cli.stat title="Comments" value="175" /> </div> <div class="mt-1"> <span class="font-bold text-green">Health Checks</span> <x-cli.connection title="Mailcoach" subText="Newsletter" :connected="true" /> <x-cli.connection title="Vonage" subText="SMS" :connected="false" /> </div></div>
如您所见,HTML 现在更易于阅读和理解。我们通过使用 <x-cli.stat ... />
和 <x-cli.connection ... />
Blade 语法来调用组件,从而减少了重复 HTML 的数量。我喜欢这种方法的一点是,因为我们可以使用 Blade,它使构建 CLI 输出的感觉与构建 Web 视图非常相似。
转换现有命令
现在我们已经了解了如何使用 Termwind 将 HTML 输出到 CLI,让我们简要了解一下如何转换现有命令以使用 Termwind。
假设我们有一个 Artisan 命令,它执行以下操作
- 询问用户搜索词。
- 在数据库中搜索任何电子邮件地址包含搜索词的用户。
- 将结果输出到 CLI。
这只是一个简单的示例,但它应该能让我们很好地展示 Termwind 提供的一些功能。为了便于说明,我们不会介绍如何搜索数据库以获取用户,因为这不是本文的重点。但是,我们可以假设下面示例中的 searchUsers
方法将返回一个 User
模型的 Collection
。
我们现有的命令可能看起来像这样
namespace App\Console\Commands; use App\Models\User;use Illuminate\Console\Command; final class UsersSearch extends Command{ protected $signature = 'users:search'; protected $description = 'Search for users in the system'; public function handle(): int { $searchTerm = $this->ask('Search term: '); $users = $this->searchUsers($searchTerm); $rows = $users->map(fn (User $user): array => [ $user->name, $user->email, $user->email_verified_at ?? 'No!', ])->all(); $this->info('Found '.count($users).' users'); $this->table(['Name', 'Email', 'Approved'], $rows); return self::SUCCESS; }}
上面的命令将提供以下输出
如果我们想更新命令以使用 Termwind,我们的类可能看起来像这样
namespace App\Console\Commands; use App\Models\User;use Illuminate\Console\Command;use function Termwind\{ask, render}; final class UsersSearch extends Command{ protected $signature = 'users:search'; protected $description = 'Search for users in the system'; public function handle(): int { $searchTerm = ask(<<<HTML <span class="mt-1 ml-2 mr-1 bg-green px-1 text-black"> Search term: </span> HTML); $users = $this->searchUsers($searchTerm); render(view('cli.user-search', [ 'users' => $users, ])); return self::SUCCESS; }}
让我们看看发生了什么变化。
您可能已经注意到,我们已经用 Termwind 的 ask
函数替换了 $this->ask
调用。这将为我们提供与现有命令类似的体验,但额外的好处是可以使用 Termwind 的样式。
我们还通过将命令其余的输出移动到一个 resources/views/cli/user-search.blade.php
Blade 视图中(就像我们在本文前面介绍的那样)来替换了 $this->info
和 $this->table
调用。正如我们所见,我们以与向 Web 视图传递数据完全相同的方式将 $users
集合传递给视图。Blade 视图看起来像这样
<div class="m-1"> <div class="text-right mb-1 w-full"> <span class="text-indigo-500">Found [<b>{{ $users->count() }}</b>] users</span> </div> @foreach($users as $user) <div> <div class="flex space-x-1"> <span class="font-bold">{{ $user->name }}</span> <span class="text-gray">[{{ $user->email }}]</span> <span class="flex-1 content-repeat-[.] text-gray"></span> <span class="text-gray">Approved:</span> @if($user->email_verified_at) <span class="font-bold text-green">{{ $user->email_verified_at }}</span> @else <span class="font-bold text-red">NO!</span> @endif </div> </div> @endforeach</div>
在 Blade 文件中,我们显示了找到的用户的总数,然后循环遍历传递给视图的 users
集合并输出用户的姓名、电子邮件以及他们是否已获批准。
由于进行了这些更改,命令现在输出以下内容
我们现在已经成功地将 Artisan 命令转换为使用 Termwind!
结论
希望这篇文章能让你对 Termwind 是什么以及如何使用它为你的 Artisan 命令构建出色的 CLI 输出有一个了解。你现在应该能够使用 Termwind 转换现有的命令,甚至从头开始构建新的命令。
如果你想了解更多关于 Termwind 的信息,你可以查看 GitHub 上的 文档。