如果您正在构建 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 上的 文档。