30、Laravel 开发实用技巧:输出处理、测试与常用工具

Laravel 开发实用技巧:输出处理、测试与常用工具

在 Laravel 开发中,我们常常会遇到各种需求,比如处理命令输出、进行任务调度、测试代码以及使用各种实用的辅助函数和集合操作。下面将详细介绍这些方面的内容和使用方法。

命令输出处理

在 Laravel 中,对于命令的输出处理有多种方式。
- 追加输出到文件 :如果想将命令的输出追加到文件中,可以使用 appendOutputTo() 方法。示例如下:

$schedule->command('do:thing')->daily()->appendOutputTo($filePath);
  • 将输出通过邮件发送 :若要将输出通过邮件发送给指定收件人,需要先将输出写入文件,再使用 emailOutputTo() 方法。示例如下:
$schedule->command('do:thing')
    ->daily()
    ->sendOutputTo($filePath)
    ->emailOutputTo('me@myapp.com');

需要注意的是,要确保 Laravel 的基本邮件配置正确。而且, sendOutputTo() appendOutputTo() emailOutputTo() 方法仅适用于命令调度任务,不能用于闭包调度事件。

  • 使用 HTTP “ping” 进行监控 :为了确保任务正确运行,可以使用一些服务进行监控,如 Laravel Envoyer 和 Dead Man’s Snitch。这些服务期望接收 HTTP “ping”,Laravel 提供了 pingBefore() thenPing() 方法来实现。示例如下:
$schedule->command('do:thing')
    ->daily()
    ->pingBefore($beforeUrl)
    ->thenPing($afterUrl);

若要使用这些 ping 功能,需要使用 Composer 引入 Guzzle: "guzzlehttp/guzzle":"~5.3|~6.0"

任务钩子

在任务执行前后执行特定操作,可以使用 before() after() 钩子。示例如下:

$schedule->command('do_thing')
    ->daily()
    ->before(function () {
        // 准备操作
    })
    ->after(function () {
        // 清理操作
    });
测试

在 Laravel 中测试队列作业或其他队列任务很简单。在 phpunit.xml 配置文件中, QUEUE_DRIVER 环境变量默认设置为 sync ,这意味着测试将同步运行作业或其他队列任务,无需依赖任何队列系统。

  • 检查作业是否触发 :可以使用 expectsJobs() 方法来检查作业是否被触发。示例如下:
public function test_changing_number_of_subscriptions_crunches_reports()
{
    $this->expectsJobs(App\Jobs\CrunchReports::class);
    ...
}

在 Laravel 5.3 及更高版本中,还可以使用闭包来验证已调度的作业是否符合给定条件。示例如下:

use Illuminate\
public function test_changing_subscriptions_triggers_crunch_job()
{
    ...
    Bus::assertDispatched(CrunchReports::class, function ($e) {
        return $e->subscriptions->contains(5);
    });
    // 也可以使用 assertNotDispatched
}
  • 测试事件是否触发 :测试事件是否触发有三种方式:
    1. 直接测试预期的行为是否发生,而不关注事件本身。
    2. 明确断言事件是否触发,适用于 Laravel 5.2。示例如下:
public function test_usersubscribed_event_fires()
{
    $this->expectsEvents(App\Events\UserSubscribed::class);
    ...
}
3. 使用闭包验证触发的事件是否符合给定条件,这是 Laravel 5.3 新增的功能。示例如下:
public function test_usersubscribed_event_fires()
{
    ...
    Event::assertFired(UserSubscribed::class, function ($e) {
        return $e->user->email = 'user-who-subscribed@mail.com';
    });
    // 也可以使用 assertNotFired()
}
  • 禁用事件监听器 :在测试过程中,如果不想触发事件监听器,可以使用 withoutEvents() 方法。示例如下:
public function test_something_subscription_related()
{
    $this->withoutEvents();
    ...
}
常用辅助函数

Laravel 提供了许多实用的全局辅助函数,下面介绍一些常见的函数。

数组操作辅助函数
函数名 功能 示例
array_first($array, $closure, $default = null) 返回数组中第一个通过闭包测试的值,可设置默认值
$people = [
    [
        'email' => 'm@me.com',
        'name' => 'Malcolm Me'
    ],
    [
        'email' => 'j@jo.com',
        'name' => 'James Jo'
    ]
];
$value = array_first($people, function ($key, $person) {
    return $person['email'] == 'j@jo.com';
});

| array_get($array, $key, $default = null) | 轻松从数组中获取值,不会因键不存在而报错,支持点符号遍历嵌套数组 |

$array = ['owner' => ['address' => ['line1' => '123 Main St.']]];
$line1 = array_get($array, 'owner.address.line1', 'No address');
$line2 = array_get($array, 'owner.address.line2');

| array_has($array, $key) | 检查数组是否包含特定键,支持点符号遍历嵌套数组 |

$array = ['owner' => ['address' => ['line1' => '123 Main St.']]];
if (array_has($array, 'owner.address.line2')) {
    // Do stuff
}

| array_pluck($array, $key, $indexKey) | 返回数组中对应键的值组成的数组,可指定索引键 |

$array = [
    ['owner' => ['id' => 4, 'name' => 'Tricia']],
    ['owner' => ['id' => 7, 'name' => 'Kimberly']],
];
$array = array_pluck($array, 'owner.name');
// Returns ['Tricia', 'Kimberly'];
$array = array_pluck($array, 'owner.name', 'owner.id');
// Returns [4 => 'Tricia', 7 => 'Kimberly'];
字符串操作辅助函数
函数名 功能 示例
e($string) 等同于 htmlentities() ,用于安全输出字符串
e('<script>do something nefarious</script>');
// Returns &lt;script&gt;do something nefarious&lt;/script&gt;

| starts_with($haystack, $needle) ends_with($haystack, $needle) str_contains($haystack, $needle) | 检查字符串是否以指定字符串开头、结尾或包含指定字符串 |

if (starts_with($url, 'https')) {
    // Do something
}
if (ends_with($abstract, '...')) {
    // Do something
}
if (str_contains($description, '1337 h4x0r')) {
    // Run away
}

| str_limit($string, $numCharacters, $concatenationString = '...') | 限制字符串长度,超过长度可添加拼接字符串 |

$abstract = str_limit($loremIpsum, 30);
// Returns "Lorem ipsum dolor sit amet, co..."
$abstract = str_limit($loremIpsum, 30, "&hellip;");
// Returns "Lorem ipsum dolor sit amet, co&hellip;"

| str_is($pattern, $string) | 检查字符串是否匹配指定模式,支持通配符 |

str_is('*.dev', 'myapp.dev');       // true
str_is('*.dev', 'myapp.dev.co.uk'); // false
str_is('*dev*', 'myapp.dev');       // true
str_is('*myapp*', 'www.myapp.dev'); // true
str_is('my*app', 'myfantasticapp'); // true
str_is('my*app', 'myapp');          // true

| str_random($length) | 返回指定长度的随机字母数字混合大小写字符串 |

$hash = str_random(64);

| str_slug($string, $separator = '-') | 从字符串生成 URL 友好的 slug |

str_slug('How to Win Friends and Influence People');
// Returns 'how-to-win-friends-and-influence-people'
应用路径辅助函数
函数名 功能 示例
app_path($append = '') 返回应用目录的路径,可追加路径
app_path();
// Returns /home/forge/myapp.com/app

| base_path($append = '') | 返回应用根目录的路径,可追加路径 |

base_path();
// Returns /home/forge/myapp.com

| config_path($append = '') | 返回配置文件目录的路径,可追加路径 |

config_path();
// Returns /home/forge/myapp.com/config

| database_path($append = '') | 返回数据库文件目录的路径,可追加路径 |

database_path();
// Returns /home/forge/myapp.com/database

| storage_path($append = '') | 返回存储目录的路径,可追加路径 |

storage_path();
// Returns /home/forge/myapp.com/storage
URL 辅助函数
函数名 功能 示例
action('Controller@method’, $params = [], $absolute = true) 根据控制器和方法名返回正确的 URL,可传递参数,可设置是否为绝对 URL
<a href="{{ action('PeopleController@index' }}">See all People</a>
// Returns <a href="http://myapp.com/people">See all People</a>
<a href="{{ action('PeopleController@show', ['id' => 3] }}">See Person #3</a>
// Returns <a href="http://myapp.com/people/3">See Person #3</a>

| route($routeName, $params = [], $absolute = true) | 根据路由名称返回 URL,可传递参数,可设置是否为绝对 URL |

<a href="{{ route('people.index') }}">See all People</a>
// Returns <a href="http://myapp.com/people">See all People</a>
<a href="{{ action('people.show', ['id' => 3]) }}">See Person #3</a>
// Returns <a href="http://myapp.com/people/3">See Person #3</a>

| url($string) secure_url($string) | 将路径字符串转换为完整的 URL, secure_url() 强制使用 HTTPS |

url('people/3');
// Returns http://myapp.com/people/3

| elixir($filePath) | 如果资产使用 Elixir 版本化系统,根据非版本化路径返回版本化文件的完整 URL |

<link rel="stylesheet" href="{{ elixir('css/app.css') }}">
// Returns something like /build/css/app-eb555e38.css
其他辅助函数
  • abort($code, $message, $headers) abort_unless($boolean, $code, $message, $headers) abort_if($boolean, $code, $message, $headers) :用于抛出 HTTP 异常。示例如下:
public function controllerMethod(Request $request)
{
    abort(403, 'You shall not pass');
    abort_unless($request->has('magicToken'), 403);
    abort_if($request->user()->isBanned, 403);
}
  • auth() :返回 Laravel 认证器的实例,用于获取当前用户、检查登录状态等。示例如下:
$user = auth()->user();
if (auth()->check()) {
    // Do something
}
  • back() :生成 “重定向回上一个页面” 的响应。示例如下:
Route::get('post', function () {
    ...
    if ($condition) {
        return back();
    }
});
  • collect($array) :将数组转换为集合。示例如下:
$collection = collect(['Rachel', 'Hototo']);
  • config($key) :返回任何点符号表示的配置项的值。示例如下:
$defaultDbConnection = config('database.default');
  • csrf_field() csrf_token() :用于在表单提交中添加 CSRF 验证。示例如下:
<form>
    {{ csrf_field() }}
</form>
// or
<form>
    <input type="hidden" name="_token" value="{{ csrf_token() }}">
</form>
  • dd($variable...) :用于调试,对所有参数执行 var_dump() 并退出应用。示例如下:
dd($var1, $var2, $state); // Why is this not working???
  • env($key, $default = null) :返回给定键的环境变量值。需要注意,在配置文件之外使用 env() 会导致 Laravel 的某些功能不可用,建议通过配置项读取环境变量。示例如下:
$key = env('API_KEY', '');
  • dispatch($job) :调度作业。示例如下:
dispatch(new EmailAdminAboutNewUser($user));
  • event($event) :触发事件。示例如下:
event(new ContactAdded($contact));
  • factory($entityClass) :返回给定类的工厂构建器实例。示例如下:
$contact = factory(App\Contact::class)->make();
  • old($key, $default = null) :返回上一次表单提交中该表单键的值(如果存在)。示例如下:
<input name="name" value="{{ old('value', 'Your name here') }}"
  • redirect($path) :返回重定向到指定路径的响应,无参数时返回 Illuminate\Routing\Redirector 类的实例。示例如下:
Route::get('post', function () {
    ...
    return redirect('home');
});
  • response($body, $status, $headers) :根据参数返回预构建的响应实例,无参数时返回响应工厂的实例。示例如下:
return response('OK', 200, ['X-Header-Greatness' => 'Super great']);
return response()->json(['status' => 'success'], 200);
  • view($viewPath) :返回视图实例。示例如下:
Route::get('home', function () {
    return view('home'); // Gets /resources/views/home.blade.php
});
集合操作

Laravel 的集合是功能强大且常被低估的工具,本质上是增强版的数组。它将数组遍历方法封装为一致、简洁、可链式调用的方法,让代码更简洁。

下面通过一个示例对比传统的 foreach 循环、PHP 原生数组函数和 Laravel 集合的使用。

  • 传统 foreach 循环
$users = [...];
$admins = [];
foreach ($users as $user) {
    if ($user['status'] == 'admin') {
        $user['name'] = $user['first'] . ' ' . $user['last'];
        $admins[] = $user;
    }
}
return $admins;
  • 使用 PHP 原生数组函数
$users = [...];
return array_map(function ($user) {
    $user['name'] = $user['first'] . ' ' . $user['last'];
    return $user;
}, array_filter($users, function ($user) {
    return $user['status'] == 'admin';
}));
  • 使用 Laravel 集合
$users = collect([...]);
return $users->filter(function ($user) {
    return $user['status'] == 'admin';
})->map(function ($user) {
    $user['name'] = $user['first'] . ' ' . $user['last'];
    return $user;
});

可以看到,使用 Laravel 集合能将复杂的操作拆分为简单、离散、易懂的任务,减少代码行数,提高代码的可读性和可维护性。例如,统计管理员数量可以这样实现:

$users = [...]
$countAdmins = collect($users)->filter(function ($user) {
    return $user['status'] == 'admin'
})->count();

综上所述,Laravel 提供了丰富的功能和工具,合理运用这些功能可以大大提高开发效率和代码质量。无论是命令输出处理、测试,还是使用辅助函数和集合操作,都能让我们的开发工作更加轻松和高效。

Laravel 开发实用技巧:输出处理、测试与常用工具

集合操作进阶

Laravel 集合除了前面提到的基本操作外,还有许多其他强大的方法,下面介绍一些常用的集合方法。

常用集合方法
方法名 功能 示例
all() 返回集合的所有元素作为数组
$collection = collect([1, 2, 3]);
$array = $collection->all();
// $array 为 [1, 2, 3]

| count() | 返回集合中元素的数量 |

$collection = collect([1, 2, 3]);
$count = $collection->count();
// $count 为 3

| each() | 遍历集合中的每个元素,并对每个元素执行给定的闭包 |

$collection = collect([1, 2, 3]);
$collection->each(function ($item, $key) {
    echo $item;
});

| filter() | 根据给定的闭包过滤集合中的元素,返回满足条件的元素组成的新集合 |

$collection = collect([1, 2, 3, 4, 5]);
$evenNumbers = $collection->filter(function ($item) {
    return $item % 2 == 0;
});
// $evenNumbers 为 [2, 4]

| map() | 对集合中的每个元素应用给定的闭包,并返回一个新的集合,新集合中的元素是闭包的返回值 |

$collection = collect([1, 2, 3]);
$multiplied = $collection->map(function ($item) {
    return $item * 2;
});
// $multiplied 为 [2, 4, 6]

| reduce() | 通过迭代集合中的元素,将它们合并为一个单一的值 |

$collection = collect([1, 2, 3]);
$sum = $collection->reduce(function ($carry, $item) {
    return $carry + $item;
}, 0);
// $sum 为 6

| sort() | 对集合中的元素进行排序,返回一个新的排序后的集合 |

$collection = collect([3, 1, 2]);
$sorted = $collection->sort();
// $sorted 为 [1, 2, 3]
集合链式调用示例

集合的强大之处在于可以进行链式调用,将多个操作组合在一起。例如,我们可以先过滤出偶数,然后将这些偶数乘以 2,最后计算它们的总和。

$collection = collect([1, 2, 3, 4, 5]);
$sum = $collection->filter(function ($item) {
    return $item % 2 == 0;
})->map(function ($item) {
    return $item * 2;
})->reduce(function ($carry, $item) {
    return $carry + $item;
}, 0);
// $sum 为 12
实际应用场景

下面通过几个实际的应用场景,展示如何在 Laravel 开发中使用前面介绍的知识。

任务调度与监控

假设我们有一个定时任务,需要每天凌晨 2 点执行,并且要将任务的输出保存到文件中,同时在任务执行前后进行 HTTP “ping” 监控。

$schedule->command('do:thing')
    ->dailyAt('02:00')
    ->appendOutputTo($filePath)
    ->pingBefore($beforeUrl)
    ->thenPing($afterUrl);

在这个示例中, dailyAt('02:00') 表示任务每天凌晨 2 点执行, appendOutputTo($filePath) 用于将任务输出追加到指定文件, pingBefore($beforeUrl) thenPing($afterUrl) 用于在任务执行前后进行 HTTP “ping” 监控。

数据处理与展示

假设我们有一个用户列表,需要过滤出管理员用户,并将他们的姓名显示在页面上。

$users = collect([
    ['name' => 'John', 'status' => 'admin'],
    ['name' => 'Jane', 'status' => 'user'],
    ['name' => 'Bob', 'status' => 'admin']
]);

$adminNames = $users->filter(function ($user) {
    return $user['status'] == 'admin';
})->map(function ($user) {
    return $user['name'];
});

foreach ($adminNames as $name) {
    echo $name . '<br>';
}

在这个示例中,我们首先使用 filter() 方法过滤出管理员用户,然后使用 map() 方法提取他们的姓名,最后遍历显示这些姓名。

表单验证与 CSRF 保护

在 Laravel 中,表单验证和 CSRF 保护是非常重要的。下面是一个简单的表单示例,展示如何使用 csrf_field() old() 函数。

<form method="post" action="/submit">
    {{ csrf_field() }}
    <input type="text" name="name" value="{{ old('name', 'Your name here') }}">
    <input type="submit" value="Submit">
</form>

在这个示例中, {{ csrf_field() }} 用于在表单中添加 CSRF 令牌, old('name', 'Your name here') 用于在表单提交失败时,保留用户之前输入的值。

总结与展望

Laravel 作为一个强大的 PHP 框架,提供了丰富的功能和工具,无论是任务调度、测试,还是数据处理和表单验证,都能让开发者更加轻松地完成开发任务。通过合理运用 Laravel 的辅助函数和集合操作,我们可以提高代码的可读性、可维护性和开发效率。

在未来的开发中,我们可以进一步探索 Laravel 的其他功能,如数据库迁移、模型关联、中间件等,以构建更加复杂和强大的应用程序。同时,我们也可以结合前端框架,如 Vue.js 或 React.js,实现前后端分离的开发模式,提高应用的性能和用户体验。

总之,掌握 Laravel 的开发技巧,能够让我们在 PHP 开发领域更加游刃有余,为我们的职业生涯带来更多的机会和挑战。

流程图:集合操作流程
graph LR
    A[创建集合] --> B[过滤元素]
    B --> C[映射元素]
    C --> D[合并元素]
    D --> E[输出结果]

这个流程图展示了一个典型的集合操作流程,从创建集合开始,经过过滤、映射和合并等操作,最终输出处理后的结果。在实际开发中,我们可以根据具体的需求,灵活组合这些操作,实现复杂的数据处理任务。

通过本文的介绍,相信你对 Laravel 的命令输出处理、测试、辅助函数和集合操作有了更深入的了解。希望这些知识能够帮助你在 Laravel 开发中更加高效地完成任务,提升你的开发技能和水平。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值