Artisan 命令行

未匹配的标注
本文档最新版为 11.x,旧版本可能放弃维护,推荐阅读最新版!

Artisan Console

介绍

Artisan 是 Laravel 自带的命令行接口。Artisan 以 artisan 脚本的方式存在于应用的根目录中,并提供了许多有用的命令,来帮你构建应用程序。你可以使用 list 命令查看所有可用的 Artisan 命令:

php artisan list

每个命令都有一个「help」帮助界面,它展示并描述了该命令的可用参数和选项。要查看帮助界面,请在命令前加上 help 即可:

php artisan help migrate

Laravel Sail

如果你使用 Laravel Sail 作为本地开发环境,记得使用 sail 命令行来调用 Artisan 命令。Sail 会在应用的 Docker 容器中执行 Artisan 命令:

./vendor/bin/sail artisan list

Tinker (REPL)

Laravel Tinker 是为 Laravel 提供的一个强大的 REPL(交互式解释器),由 PsySH 驱动支持。

安装

所有 Laravel 应用程序默认都包含 Tinker。
不过,如果你之前从应用中移除了它,可以通过 Composer 重新安装:

composer require laravel/tinker

[!注意]
想要在与 Laravel 应用交互时支持 热重载、多行代码编辑和自动补全 吗?可以看看 Tinkerwell

使用方法

Tinker 允许你在命令行与整个 Laravel 应用程序进行交互,包括 Eloquent 模型、任务 (jobs)、事件 (events) 等。
要进入 Tinker 环境,请运行 tinker Artisan 命令:

php artisan tinker

你可以通过 vendor:publish 命令发布 Tinker 的配置文件:

php artisan vendor:publish --provider="Laravel\Tinker\TinkerServiceProvider"

[!警告]
dispatch 辅助函数以及 Dispatchable 类中的 dispatch 方法依赖于垃圾回收来将任务放入队列。
因此,在使用 Tinker 时,应使用 Bus::dispatchQueue::push 来派发任务。

命令允许清单

Tinker 使用一个 “allow” 清单来决定在其 shell 中允许运行哪些 Artisan 命令。
默认情况下,你可以运行以下命令:

  • clear-compiled
  • down
  • env
  • inspire
  • migrate
  • migrate:install
  • up
  • optimize
'commands' => [
    // App\Console\Commands\ExampleCommand::class,
],

不应被别名化的类

通常,Tinker 会在你交互过程中自动为类添加别名。
不过,你可能希望某些类永远不要被别名化
你可以在 tinker.php 配置文件的 dont_alias 数组中列出这些类:

'dont_alias' => [
    App\Models\User::class,
],

编写命令

除了 Artisan 提供的命令之外,你还可以构建自己的自定义命令。命令通常存储在 app/Console/Commands 目录中;然而,只要你指示 Laravel 扫描其他目录中的 Artisan 命令,你也可以自由选择自己的存储位置。

生成命令 (Generating Commands)

要创建一个新命令,可以使用 make:command Artisan 命令。该命令会在 app/Console/Commands 目录中创建一个新的命令类。
如果应用中不存在该目录,不必担心 —— 当你第一次运行 make:command Artisan 命令时,它会被自动创建:

php artisan make:command SendEmails

命令结构 (Command Structure)

在生成命令之后,你应该为类的 signaturedescription 属性定义合适的值。
这些属性会在 list 屏幕上显示你的命令时使用。
其中,signature 属性还允许你定义 命令的输入期望

当命令被执行时,handle 方法会被调用。你可以将命令逻辑放在这个方法中。

让我们看一个示例命令。注意,在命令的 handle 方法中,我们可以请求所需的任何依赖项。Laravel 的 服务容器 会自动注入所有在方法签名中通过类型提示声明的依赖:

<?php

namespace App\Console\Commands;

use App\Models\User;
use App\Support\DripEmailer;
use Illuminate\Console\Command;

class SendEmails extends Command
{
    /**
     * 控制台命令的名称和签名。
     *
     * @var string
     */
    protected $signature = 'mail:send {user}';

    /**
     * 控制台命令的描述。
     *
     * @var string
     */
    protected $description = 'Send a marketing email to a user';

    /**
     * 执行控制台命令。
     */
    public function handle(DripEmailer $drip): void
    {
        $drip->send(User::find($this->argument('user')));
    }
}

[!注意]
为了更好地复用代码,建议保持控制台命令简洁,并让它们委托给应用服务来完成任务。
在上面的示例中,请注意我们注入了一个服务类来完成发送邮件的“繁重工作”。

退出码

如果 handle 方法没有返回任何内容并且命令成功执行,那么命令将以 0 退出码退出,表示成功。
但是,handle 方法也可以选择返回一个整数,以手动指定命令的退出码:

$this->error('Something went wrong.');

return 1;

如果你希望从命令中的任何方法中“失败”命令,可以使用 fail 方法。
fail 方法会立即终止命令的执行并返回退出码 1

$this->fail('Something went wrong.');

闭包命令(Closure Commands)

基于闭包的命令提供了一种替代方案,使你不必将控制台命令定义为类。
就像路由闭包是控制器的一种替代方式一样,可以把命令闭包看作是命令类的一种替代方式。

虽然 routes/console.php 文件并不会定义 HTTP 路由,但它定义了进入应用的基于控制台的入口点(路由)。
在这个文件中,你可以使用 Artisan::command 方法来定义所有基于闭包的控制台命令。
command 方法接受两个参数:命令签名 和一个闭包,该闭包会接收命令的参数和选项:

Artisan::command('mail:send {user}', function (string $user) {
    $this->info("Sending email to: {$user}!");
});

该闭包会绑定到底层的命令实例,因此你可以完全访问命令类中通常能用到的所有辅助方法。

类型提示依赖(Type-Hinting Dependencies)

除了接收命令的参数和选项外,命令闭包还可以类型提示你希望从 服务容器 中解析的其他依赖:

use App\Models\User;
use App\Support\DripEmailer;

Artisan::command('mail:send {user}', function (DripEmailer $drip, string $user) {
    $drip->send(User::find($user));
});

闭包命令描述(Closure Command Descriptions)

在定义基于闭包的命令时,你可以使用 purpose 方法为命令添加描述。
当你运行 php artisan listphp artisan help 时,这个描述会被显示出来:

Artisan::command('mail:send {user}', function (string $user) {
    // ...
})->purpose('Send a marketing email to a user');

可隔离命令

[!警告]
若要使用此功能,你的应用必须使用以下其中一种作为默认缓存驱动:
memcachedredisdynamodbdatabasefilearray
此外,所有服务器必须连接到同一个中央缓存服务器。

有时候,你可能希望确保某个命令同一时间只能运行一个实例
要实现这一点,你可以在命令类上实现 Illuminate\Contracts\Console\Isolatable 接口:

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Contracts\Console\Isolatable;

class SendEmails extends Command implements Isolatable
{
    // ...
}

当命令被标记为 Isolatable 后,Laravel 会自动为该命令添加一个 --isolated 选项。
当你在运行命令时加上该选项,Laravel 会确保没有其他相同命令实例正在运行。
Laravel 通过应用的默认缓存驱动尝试获取一个原子锁来实现这一点。

如果发现已有其他实例在运行,该命令将不会执行;但命令仍然会以成功的退出状态码退出:

php artisan mail:send 1 --isolated

如果你希望在命令无法执行时指定退出状态码,可以通过 isolated 选项传入你想要的状态码:

php artisan mail:send 1 --isolated=12

锁 ID(Lock ID)

默认情况下,Laravel 会使用命令的名称来生成字符串键(key),该键用于在应用的缓存中获取原子锁。不过,你也可以通过在 Artisan 命令类中定义 isolatableId 方法来自定义这个 key。
这样你就可以把命令的参数或选项整合进 key 中:

/**
 * 获取命令的隔离 ID。
 */
public function isolatableId(): string
{
    return $this->argument('user');
}

锁过期时间

默认情况下,隔离锁会在命令执行完成后自动过期。
或者,如果命令被中断而未能完成,锁会在 1 小时后过期。

你也可以通过在命令中定义 isolationLockExpiresAt 方法,来自定义锁的过期时间:

use DateTimeInterface;
use DateInterval;

/**
 * 决定命令的隔离锁何时过期。
 */
public function isolationLockExpiresAt(): DateTimeInterface|DateInterval
{
    return now()->addMinutes(5);
}

定义输入预期(Defining Input Expectations)

在编写控制台命令时,我们通常需要通过参数(arguments)选项(options)来收集用户输入。
Laravel 提供了一种非常方便的方式 —— 使用命令类中的 signature 属性来定义你期望的输入。
signature 属性允许你用一种类似路由的直观语法,在一行中定义命令的名称、参数和选项。

参数(Arguments)

所有用户传入的 参数选项 都用大括号 {} 包裹。
例如,下面的命令定义了一个必填参数:user

/**
 * 命令的名称和签名。
 *
 * @var string
 */
protected $signature = 'mail:send {user}';

你也可以让参数变成可选,或者为参数定义一个默认值

// 可选参数...
'mail:send {user?}'

// 带默认值的可选参数...
'mail:send {user=foo}'

选项(Options)

选项(Options)和参数类似,也是另一种用户输入方式。
选项在命令行中以两个短横线(--)开头。
选项分为两类:

  1. 带值的选项(需要用户传入具体的值)
  2. 不带值的选项(布尔开关,用来控制某个功能开/关)
    我们先来看一个布尔开关类型的例子:
/**
 * 命令的名称和签名。
 *
 * @var string
 */
protected $signature = 'mail:send {user} {--queue}';

在这个例子中,调用 Artisan 命令时,可以指定 --queue 选项。
如果传入了 --queue,那么该选项的值为 true
如果没有传入,则该选项的值为 false

php artisan mail:send 1 --queue

带值的选项(Options With Values)

接下来,我们看看需要传入的选项。
如果用户必须为某个选项指定值,那么需要在选项名后面加上 =

/**
 * 命令的名称和签名。
 *
 * @var string
 */
protected $signature = 'mail:send {user} {--queue=}';

在这个例子中,用户可以这样传值:
如果调用命令时没有传入 --queue,那么其值将是 null

php artisan mail:send 1 --queue=default

你也可以为选项定义一个默认值:
如果用户没有传入该选项,那么会使用默认值:

'mail:send {user} {--queue=default}'

选项快捷方式(Option Shortcuts)

在定义选项时,你可以为它指定一个快捷方式
使用 | 作为分隔符,将快捷方式和完整选项名写在一起即可:

'mail:send {user} {--Q|queue}'

在终端调用命令时,选项的快捷方式前缀使用单个短横线-)。
如果选项需要值,注意不能带 =,而是紧跟在快捷方式后面:

php artisan mail:send 1 -Qdefault

输入数组(Input Arrays)

如果你希望某个参数或选项接收多个输入值,可以在定义时加上 *
先来看一个参数数组的例子:

'mail:send {user*}'

执行命令时,user 参数可以连续传入多个值。
比如下面的命令会让 user 参数的值变成一个数组 [1, 2]

php artisan mail:send 1 2

* 也可以和 可选参数 一起使用,允许传入零个或多个值:

'mail:send {user?*}'

选项数组(Option Arrays)

如果你希望某个选项能接收多个值,可以这样定义:

'mail:send {--id=*}'

调用时,用户可以多次传入同名选项,每个值会自动收集成一个数组:

php artisan mail:send --id=1 --id=2

输入描述(Input Descriptions)

你可以为参数和选项添加描述信息,只需在定义时用冒号分隔名称和说明。
如果定义太长,可以换行书写,保持清晰:

/**
 * The name and signature of the console command.
 *
 * @var string
 */
protected $signature = 'mail:send
                        {user : The ID of the user}
                        {--queue : Whether the job should be queued}';

提示缺失的输入

如果你的命令包含必填参数,但用户在执行时没有提供这些参数,那么用户会收到一条错误信息。
作为替代方案,你也可以让命令在缺少必填参数时自动提示用户输入,只需要实现 PromptsForMissingInput 接口即可:

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Contracts\Console\PromptsForMissingInput;

class SendEmails extends Command implements PromptsForMissingInput
{
    /**
     * 命令的名称和签名。
     *
     * @var string
     */
    protected $signature = 'mail:send {user}';

    // ...
}

如果 Laravel 需要从用户那里收集必填参数,它会自动向用户提问,
并会智能地使用参数名或参数描述来组织问题

如果你希望自定义提示用户输入的提问方式,可以实现 promptForMissingArgumentsUsing 方法,
并返回一个以参数名为键、问题为值的数组:

/**
 * 使用返回的问题来提示缺失的输入参数。
 *
 * @return array<string, string>
 */
protected function promptForMissingArgumentsUsing(): array
{
    return [
        'user' => 'Which user ID should receive the mail?',
    ];
}

你还可以通过元组的方式,提供提示问题和占位符(placeholder):

return [
    'user' => ['Which user ID should receive the mail?', 'E.g. 123'],
];

如果你想对提示过程拥有完全的控制权,你可以提供一个 闭包
由该闭包负责提示用户并返回答案:

use App\Models\User;
use function Laravel\Prompts\search;

// ...

return [
    'user' => fn () => search(
        label: 'Search for a user:',
        placeholder: 'E.g. Taylor Otwell',
        options: fn ($value) => strlen($value) > 0
            ? User::where('name', 'like', "%{$value}%")->pluck('name', 'id')->all()
            : []
    ),
];

[!注意]
更完整的 Laravel Prompts 文档包含了关于可用提示及其用法的更多信息。

如果你希望提示用户去选择或输入选项,你可以在命令的 handle 方法中包含提示。
然而,如果你只希望在用户已经因为缺失参数而被自动提示之后再进一步提示用户,那么你可以实现 afterPromptingForMissingArguments 方法:

use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use function Laravel\Prompts\confirm;

// ...

/**
 * 在用户被提示输入缺失参数之后执行操作。
 */
protected function afterPromptingForMissingArguments(InputInterface $input, OutputInterface $output): void
{
    $input->setOption('queue', confirm(
        label: 'Would you like to queue the mail?',
        default: $this->option('queue')
    ));
}

命令的输入 / 输出(Command I/O)

获取输入(Retrieving Input)

当你的命令在执行时,你很可能需要访问命令所接受的参数和选项的值。
要做到这一点,你可以使用 argumentoption 方法。
如果某个参数或选项不存在,将会返回 null

/**
 * 执行控制台命令。
 */
public function handle(): void
{
    $userId = $this->argument('user');
}

如果你需要将所有参数一次性作为 array 获取,可以调用 arguments 方法:

$arguments = $this->arguments();

选项(Options)的获取方式和参数一样简单:你可以使用 option 方法。
如果你想要将所有选项一次性作为数组获取,可以调用 options 方法:

// 获取某个具体的选项...
$queueName = $this->option('queue');

// 获取所有选项作为数组...
$options = $this->options();

提示输入(Prompting for Input)

[!注意]
Laravel Prompts 是一个 PHP 包,用于为命令行应用添加美观且用户友好的表单,
它提供了类似浏览器的功能,包括占位符文本输入验证

除了显示输出之外,你还可以在命令执行过程中要求用户提供输入。
ask 方法会向用户显示给定的问题,接收他们的输入,然后将用户输入返回给你的命令:

/**
 * 执行控制台命令。
 */
public function handle(): void
{
    $name = $this->ask('What is your name?');

    // ...
}

ask 方法还可以接受一个可选的第二个参数,用来指定如果用户没有输入时应返回的默认值:

$name = $this->ask('What is your name?', 'Taylor');

secret 方法和 ask 类似,但用户在控制台输入时不会显示他们的输入。
此方法在请求敏感信息(例如密码)时非常有用:

$password = $this->secret('What is the password?');

请求确认(Asking for Confirmation)

如果你需要向用户请求一个简单的“是或否”确认,可以使用 confirm 方法。
默认情况下,该方法会返回 false。但是,如果用户在提示中输入 yyes,方法将返回 true

if ($this->confirm('Do you wish to continue?')) {
    // ...
}

如有必要,你可以通过向 confirm 方法传递第二个参数 true 来指定确认提示的默认值为 true

if ($this->confirm('Do you wish to continue?', true)) {
    // ...
}

自动完成(Auto-Completion)

anticipate 方法可用于提供可能选择的自动完成提示。
用户仍然可以输入任何答案,而不受自动完成提示的限制:

$name = $this->anticipate('What is your name?', ['Taylor', 'Dayle']);

或者,你可以将一个闭包作为 anticipate 方法的第二个参数传入。
每当用户输入一个字符时,闭包就会被调用。
闭包应接受一个字符串参数,表示用户当前已输入的内容,并返回用于自动完成的选项数组:

use App\Models\Address;

$name = $this->anticipate('What is your address?', function (string $input) {
    return Address::whereLike('name', "{$input}%")
        ->limit(5)
        ->pluck('name')
        ->all();
});

多选题(Multiple Choice Questions)

如果你需要在提问时给用户一个预定义的选项集合,可以使用 choice 方法。
如果用户没有选择任何选项,你可以通过传入默认值的数组索引作为第三个参数来指定默认返回值:

$name = $this->choice(
    'What is your name?',
    ['Taylor', 'Dayle'],
    $defaultIndex
);

此外,choice 方法还接受可选的第四和第五个参数,用于确定选择有效答案的最大尝试次数,以及是否允许多选:

$name = $this->choice(
    'What is your name?',
    ['Taylor', 'Dayle'],
    $defaultIndex,
    $maxAttempts = null,
    $allowMultipleSelections = false
);

输出信息(Writing Output)

要向控制台输出信息,你可以使用 lineinfocommentquestionwarnerror 方法。
这些方法会根据用途使用合适的 ANSI 颜色。
例如,我们向用户显示一些一般信息。通常,info 方法在控制台中会显示为绿色文本:

/**
 * 执行控制台命令。
 */
public function handle(): void
{
    // ...

    $this->info('The command was successful!');
}

要显示错误信息,可以使用 error 方法。错误信息通常以红色显示:

$this->error('Something went wrong!');

你可以使用 line 方法显示普通的、不带颜色的文本:

$this->line('Display this on the screen');

你可以使用 newLine 方法显示一个空行:

// 写一个空行...
$this->newLine();

// 写三个空行...
$this->newLine(3);

表格(Tables)

table 方法可以方便地格式化多行多列的数据。
你只需要提供列名和表格数据,Laravel 会自动计算表格的宽度和高度:

use App\Models\User;

$this->table(
    ['Name', 'Email'],
    User::all(['name', 'email'])->toArray()
);

进度条(Progress Bars)

对于长时间运行的任务,显示进度条可以帮助用户了解任务完成的进度。
使用 withProgressBar 方法,Laravel 会显示一个进度条,并在遍历给定可迭代值的每个元素时推进进度:

use App\Models\User;

$users = $this->withProgressBar(User::all(), function (User $user) {
    $this->performTask($user);
});

有时候,你可能需要更手动地控制进度条的推进。
首先,定义任务需要迭代的总步数,然后在处理每个元素后推进进度条:

$users = App\Models\User::all();

$bar = $this->output->createProgressBar(count($users));

$bar->start();

foreach ($users as $user) {
    $this->performTask($user);

    $bar->advance();
}

$bar->finish();

[!注意]
如需更多高级选项,请参考 Symfony Progress Bar 组件文档

注册命令(Registering Commands)

默认情况下,Laravel 会自动注册 app/Console/Commands 目录下的所有命令。
但是,你也可以使用 withCommands 方法在应用的 bootstrap/app.php 文件中指示 Laravel 扫描其他目录的 Artisan 命令:

->withCommands([
    __DIR__.'/../app/Domain/Orders/Commands',
])

如果需要,你还可以通过将命令类名传递给 withCommands 方法来手动注册命令:

use App\Domain\Orders\Commands\SendEmails;

->withCommands([
    SendEmails::class,
])

当 Artisan 启动时,你应用中的所有命令都会由 服务容器 解析并注册到 Artisan。

以编程方式执行命令(Programmatically Executing Commands)

有时候,你可能希望在 CLI 之外执行 Artisan 命令。例如,你可能希望从路由或控制器中执行 Artisan 命令。
可以使用 Artisan 门面(Facade)的 call 方法来实现。
call 方法的第一个参数可以是命令的签名名称或类名,第二个参数是命令参数数组。方法会返回命令的退出码:

use Illuminate\Support\Facades\Artisan;

Route::post('/user/{user}/mail', function (string $user) {
    $exitCode = Artisan::call('mail:send', [
        'user' => $user, '--queue' => 'default'
    ]);

    // ...
});

你也可以直接将完整的 Artisan 命令作为字符串传给 call 方法:

Artisan::call('mail:send 1 --queue=default');

传递数组值(Passing Array Values)

如果你的命令定义了一个接受数组的选项,你可以传递一个值数组给该选项:

use Illuminate\Support\Facades\Artisan;

Route::post('/mail', function () {
    $exitCode = Artisan::call('mail:send', [
        '--id' => [5, 13]
    ]);
});

传递布尔值

如果你需要为不接受字符串值的选项指定值,例如 migrate:refresh 命令中的 --force 标志,你应该将选项的值设置为 truefalse

$exitCode = Artisan::call('migrate:refresh', [
    '--force' => true,
]);

将 Artisan 命令入队(Queueing Artisan Commands)

使用 Artisan 门面的 queue 方法,你甚至可以将 Artisan 命令入队,由你的 队列工作器 在后台处理。在使用此方法之前,请确保你已经配置好队列并正在运行队列监听器:

use Illuminate\Support\Facades\Artisan;

Route::post('/user/{user}/mail', function (string $user) {
    Artisan::queue('mail:send', [
        'user' => $user, '--queue' => 'default'
    ]);

    // ...
});

使用 onConnectiononQueue 方法,你可以指定 Artisan 命令应派发到哪个连接或队列:

Artisan::queue('mail:send', [
    'user' => 1, '--queue' => 'default'
])->onConnection('redis')->onQueue('commands');

从其他命令调用命令(Calling Commands From Other Commands)

有时,你可能希望从已有的 Artisan 命令中调用其他命令。可以使用 call 方法来实现。该 call 方法接受命令名称和一个命令参数/选项数组:

/**
 * 执行控制台命令。
 */
public function handle(): void
{
    $this->call('mail:send', [
        'user' => 1, '--queue' => 'default'
    ]);

    // ...
}

如果你希望调用另一个控制台命令并屏蔽其所有输出,可以使用 callSilently 方法。callSilently 方法的签名与 call 方法相同:

$this->callSilently('mail:send', [
    'user' => 1, '--queue' => 'default'
]);

信号处理(Signal Handling)

正如你所知,操作系统允许向正在运行的进程发送信号。例如,SIGTERM 信号是操作系统请求程序终止的方式。如果你希望在 Artisan 控制台命令中监听信号并在信号触发时执行代码,可以使用 trap 方法:

/**
 * 执行控制台命令。
 */
public function handle(): void
{
    $this->trap(SIGTERM, fn () => $this->shouldKeepRunning = false);

    while ($this->shouldKeepRunning) {
        // ...
    }
}

如果希望同时监听多个信号,可以向 trap 方法传递一个信号数组:

$this->trap([SIGTERM, SIGQUIT], function (int $signal) {
    $this->shouldKeepRunning = false;

    dump($signal); // SIGTERM / SIGQUIT
});

模板(Stub)自定义(Stub Customization)

Artisan 控制台的 make 命令用于创建各种类,例如控制器、作业、迁移和测试。这些类是通过“stub”文件生成的,生成内容会根据你的输入进行填充。然而,你可能希望对 Artisan 生成的文件做一些小修改。为此,你可以使用 stub:publish 命令将最常用的 stub 发布到你的应用中,以便进行自定义:

php artisan stub:publish

发布的 stub 文件会位于应用根目录下的 stubs 目录中。对这些 stub 的任何修改都会在你使用 Artisan 的 make 命令生成相应类时生效。

事件(Events)

在运行命令时,Artisan 会触发三个事件:

  • Illuminate\Console\Events\ArtisanStarting
  • Illuminate\Console\Events\CommandStarting
  • Illuminate\Console\Events\CommandFinished

ArtisanStarting 事件在 Artisan 开始运行时立即触发;
CommandStarting 事件在命令运行前立即触发;
CommandFinished 事件在命令执行完毕后触发。

本文章首发在 LearnKu.com 网站上。

本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://learnku.com/docs/laravel/12.x/ar...

译文地址:https://learnku.com/docs/laravel/12.x/ar...

上一篇 下一篇
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
贡献者:3