Artisan 命令
Laravel 附带了一个 PHP 可执行的 artisan
文件,为框架提供了一个命令行界面文件。 通过这个 CLI,您可以访问像php artisan migrate
和php artisan make:model Post
这样的命令。 你可以用命令做很多事情。 请务必阅读Laravel 文档中的artisan控制台。
假设我们想为我们的终端用户提供一个简单的 artisan 命令,通过: php artisan blogpackage:install
发布配置文件。
创建一个新命令
在 src/
目录中创建一个新的 Console
文件夹,并创建一个名为 InstallBlogPackage.php
的新文件。 这个类将继承 Laravel 的 Command
类,并提供 $signature
(命令)和 $description
属性。 在 handle()
方法中,我们指定命令将执行的操作。 在这种情况下,我们提供了一些反馈,说明我们正在“安装”这个包,并且我们将调用另一个 artisan 命令来发布配置文件。 最后,我们让用户知道我们完成了。
// 'src/Console/InstallBlogPackage.php'
<?php
namespace JohnDoe\BlogPackage\Console;
use Illuminate\Console\Command;
class InstallBlogPackage extends Command
{
protected $signature = 'blogpackage:install';
protected $description = 'Install the BlogPackage';
public function handle()
{
$this->info('Installing BlogPackage...');
$this->info('Publishing configuration...');
$this->call('vendor:publish', [
'--provider' => "JohnDoe\BlogPackage\BlogPackageServiceProvider",
'--tag' => "config"
]);
$this->info('Installed BlogPackage');
}
}
在服务提供者中注册该命令
我们需要向最终用户展示这个包的功能,从而在扩展包的服务提供者中注册它。
因为我们只想从命令行提供这个功能,所以我们将它添加到 if-runningInConsole()-语句中(不要忘记导入类) :
// 'BlogPackageServiceProvider.php'
use JohnDoe\BlogPackage\Console\InstallBlogPackage;
public function boot()
{
if ($this->app->runningInConsole()) {
// publish config file
$this->commands([
InstallBlogPackage::class,
]);
}
}
测试 artisan 命令
为了测试我们的命令是否有效,让我们在 Unit test 文件夹中创建一个名为 InstallBlogPackageTest.php
的新单元测试。
由于我们使用 Orchestra Testbench,我们在 config path()
处有一个 config 文件夹,其中包含普通 Laravel 安装程序可能拥有的每个文件。 (你可以使用dd(config_path())
检查这个目录的位置)。 因此,我们可以很容易地断言这个目录在运行我们的 artisan 命令之后应该有我们的blogpackage.php
配置文件。 为了确保我们从一个干净的状态开始,让我们首先删除所有上次测试产生的配置文件。
// 'tests/Unit/InstallBlogPackageTest.php'
<?php
namespace JohnDoe\BlogPackage\Tests\Unit;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\File;
use JohnDoe\BlogPackage\Tests\TestCase;
class InstallBlogPackageTest extends TestCase
{
/** @test */
function the_install_command_copies_a_the_configuration()
{
// make sure we're starting from a clean state
if (File::exists(config_path('blogpackage.php'))) {
unlink(config_path('blogpackage.php'));
}
$this->assertFalse(File::exists(config_path('blogpackage.php')));
Artisan::call('blogpackage:install');
$this->assertTrue(File::exists(config_path('blogpackage.php')));
}
}
隐藏命令
在某些情况下,您可能希望将该命令从 Artisan 命令列表中排除。您可以在命令类上定义 $ hidden
属性,该属性不会在 Artisan 命令列表中显示特定命令。但是请注意,该命令仍然可以使用,只是被隐藏。
class InstallBlogPackage extends Command
{
protected $hidden = true;
protected $signature = 'blogpackage:install';
protected $description = 'Install the BlogPackage';
public function handle()
{
// ...
}
}
创建生成器命令
Laravel 提供了一种简单的方法来创建 Generator 命令,即具有签名的命令,例如 php artisan make:controller
之类的命令。这些命令将通用的预定义模板 (stub) 修改为特定的应用程序。例如,通过自动注入正确的命名空间。
为了创建一个 Generator 命令,您必须扩展 Illuminate\Console\GeneratorCommand
类,并覆盖以下属性和方法:
protected $name
: 命令的名称protected $description
: 命令说明protected $type
: 命令生成类的类型protected function getStub()
: 返回 stub 模板文件路径的方法protected function getDefaultNamespace($rootNamespace)
: 所生成类的默认命名空间public function handle()
: 命令的主体
GeneratorCommand
基类提供了一些辅助方法:
getNameInput()
: 返回从命令行执行传递的参数qualifyClass(string $name)
: 返回给定参数的验证后类名getPath(string $name)
: 返回给定参数的文件路径
考虑以下 php artisan make:foo MyFoo
命令的示例:
<?php
namespace JohnDoe\BlogPackage\Console;
use Illuminate\Console\GeneratorCommand;
class MakeFooCommand extends GeneratorCommand
{
protected $name = 'make:foo';
protected $description = 'Create a new foo class';
protected $type = 'Foo';
protected function getStub()
{
return __DIR__ . '/stubs/foo.php.stub';
}
protected function getDefaultNamespace($rootNamespace)
{
return $rootNamespace . '\Foo';
}
public function handle()
{
parent::handle();
$this->doOtherOperations();
}
protected function doOtherOperations()
{
// Get the fully qualified class name (FQN)
$class = $this->qualifyClass($this->getNameInput());
// get the destination path, based on the default namespace
$path = $this->getPath($class);
$content = file_get_contents($path);
// Update the file content with additional data (regular expressions)
file_put_contents($path, $content);
}
}
请注意,该类将根据 getDefaultNamespace()
方法中指定的名称空间导出到目录。
创建 stub
您可以将 stub 存储在其他目录中,但是现在考虑将存根存储在Console\stub
目录中。对于我们的 Foo
类生成器,存根可能如下所示:
// 'stubs/foo.php.stub'
<?php
namespace DummyNamespace;
use JohnDoe\BlogPackage\Foo;
class DummyClass implements Foo
{
public function myFoo()
{
// foo
}
}
请注意,DummyNamespace
和 DummyClass
是占位符(在 GeneratorCommand
基类中严格定义:Laravel 希望这些特定名称被替换)并自动用正确的值替换。
测试生成器命令
我们可以在 tests / Feature
目录中为该命令添加功能测试,称为 MakeFooCommandTest.php
,它可以验证是否创建了新文件并包含正确的内容:
<?php
namespace JohnDoe\BlogPackage\Tests\Feature;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Artisan;
use JohnDoe\BlogPackage\Tests\TestCase;
class MakeFooCommandTest extends TestCase
{
/** @测试 */
function it_creates_a_new_foo_class()
{
// Foo类的目标路径
$fooClass = app_path('Foo/MyFooClass.php');
// 确保我们从清空状态开始
if (File::exists($fooClass)) {
unlink($fooClass);
}
$this->assertFalse(File::exists($fooClass));
// 运行make命令
Artisan::call('make:foo MyFooClass');
// 声明一个新文件已创建
$this->assertTrue(File::exists($fooClass));
// 声明文件包含正确的内容
$expectedContents = <<<CLASS
<?php
namespace App\Foo;
use JohnDoe\BlogPackage\Foo;
class MyFooClass implements Foo
{
public function myFoo()
{
// foo
}
}
CLASS;
$this->assertEquals($expectedContents, file_get_contents($fooClass));
}
}
创建仅测试命令
在某些情况下,您只想使用某个命令进行测试,而不在应用程序本身中使用。例如,当您的程序包提供 Command 类可以使用的 Trait
时。要测试,您想使用实际命令进行测试。
该命令本身不会向软件包添加功能,因此不应发布。但是,仅从服务提供商中排除命令并不是解决方案,因为您希望能够在测试中运行该命令。
优雅的解决方案 suggested by Marcel Pociot 是通过在 Laravel 的 Application::starting()
命令中加入钩子,从而 只 在测试中注册该命令:
<?php
namespace JohnDoe\BlogPackage\Tests\Feature;
use JohnDoe\BlogPackage\Tests\Commands\TestCommand;
use Illuminate\Console\Application;
use Illuminate\Support\Facades\Artisan;
use Orchestra\Testbench\TestCase;
class TestCommandTest extends TestCase
{
/** @test **/
public function it_does_a_certain_thing()
{
Application::starting(function ($artisan) {
$artisan->add(app(TestCommand::class));
});
// Running the command
Artisan::call('test-command:run');
// assertions...
}
}
本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
推荐文章: