使用 Macro 让你的代码更简洁,更具有可读性

Macro 是 Laravel 框架的一个强大功能。 它可以向 Laravel 的内部组件添加自定义功能。

你可以把它理解成为 trait 中的一个方法,还有点和我们开发中常用助手文件中 helpers 中的方法类似,其目的是将 Laravel 的内部组件进行横向扩展以全局通用。

下面我们来看一个例子:


User 表中有一个字段是 is_vip,用来记录用户是否是 VIP,当我们在控制器中进行判断时,常规的写法是:

// UserController.php

public function getVip(Request $request)
{
    if($request->user()->is_vip)
    {
        return true;
    }
    return false;
}

当多个控制器都有对于 is_vip 字段进行判断的分支时,代码就会变得冗余,假如 is_vip 为真时,后续还有其他判断,那么嵌套的 if 语句就会让代码逻辑变得更复杂。

if($request->user()->is_vip)
{
    if(...)
    {
        ...
    }
}

如果我们能在 Illuminate\Http\Request 类中添加一个方法来判断请求用户 is_vip 的值就好了,Macro 就是用来做这个的,我们再来改写一下之前的方法:

public function getVip(Request $request)
{
    // 先把它暂时写在控制器中。
    Request::macro('isVip', function()
    {
        if(auth()->check()) // 先做授权判断,增加代码健壮性
        {
            return $this->user()->is_vip;  // 等同于 $request->user()->is_vip, $this 指向的是 Request 的实例。
        }
        return false;
    }

    // 这样的话控制器只有一行代码了。
    return $request->isVip(); 
}

Macro 中的 $this 指向的是被扩展方法的实例,上文中的 $this 代表的是控制器中注入的 Request 实例。


再来个例子,项目中经常需要判断字符串长度是否大于等于指定长度,通常是这样:

use Illuminate\Support\Str;
public function str()
{  
    $length = 3;
    $str1 = 'cbd';
    if(Str::length($str1) >= $length)
    {
        return true;
    }
    return false;
}

使用 Macro 改写后如下:

use Illuminate\Support\Str;
public function str()
{ 
    // 先把它暂时写在控制器中。
    Str::macro('lengthCheck', function(string $str1, int $length)
    {
        if(self::length($str1) >= $length)
        {
            return true;
        }
        return false;
    });

    // 这样的话控制器只有一行代码了。  
    return Str::lengthCheck($str1, $length);
}

好了,到这里已经对 Macro 有一个基本的概念了,鼠标宏都用过吧?它不就是鼠标功能的一个扩展?鼠标本身没有连点功能,但是它提供了一个接口来让我们自定义编程,从而实现我们想要的功能。


下面我们还需要让 Macro 在框架加载时一同载入,从而实现全局调用,你可以将它写在 AppServiceProvider.php 中,如果数量不多的话,这个视情况决定。

本例中我们来创建一个 Provider,来统一将这些 Macro 放在一起。

php artisan make:provider MacrosServiceProvider

把上面两个 Macro 放入到 MacrosServiceProvider

<?php

namespace App\Providers;

use Illuminate\Http\Request;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Str;

class MacrosServiceProvider extends ServiceProvider
{  
    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        // 判断用户是否是 VIP
        Request::macro('isVip', function(){
            if(auth()->check())
            {
                return $this->user()->is_vip;
            }
            return false;
        });

        // 判断字符串是否大于等于给定长度
        Str::macro('lengthCheck', function(string $str1, int $length)
        {
            if(static::length($str1) >= $length)
            {
                return true;
            }
            return false;
        });

    }
}

app.php 中加载它

'providers' => [
    ...
    \App\Providers\MacrosServiceProvider::class,
];

在开发中多考虑使用 Macro 来让你的代码更具有可读性和复用性,希望本文对你有帮助。

最后说一下,哪些 Laravel 的组件可以使用 Macro 扩展:

  • Illuminate\Auth\RequestGuard
  • Illuminate\Auth\SessionGuard
  • Illuminate\Cache\Repository
  • Illuminate\Console\Command
  • Illuminate\Console\Scheduling\Event
  • Illuminate\Cookie\CookieJar
  • Illuminate\Database\Eloquent\FactoryBuilder
  • Illuminate\Database\Eloquent\Relations\Relation
  • Illuminate\Database\Grammar
  • Illuminate\Database\Query\Builder
  • Illuminate\Database\Schema\Blueprint
  • Illuminate\Filesystem\Filesystem
  • Illuminate\Foundation\Testing\TestResponse
  • Illuminate\Http\JsonResponse
  • Illuminate\Http\RedirectResponse
  • Illuminate\Http\Request
  • Illuminate\Http\Response
  • Illuminate\Http\UploadedFile
  • Illuminate\Mail\Mailer
  • Illuminate\Routing\PendingResourceRegistration
  • Illuminate\Routing\Redirector
  • Illuminate\Routing\ResponseFactory
  • Illuminate\Routing\Route
  • Illuminate\Routing\Router
  • Illuminate\Routing\UrlGenerator
  • Illuminate\Support\Arr
  • Illuminate\Support\Collection
  • Illuminate\Support\LazyCollection
  • Illuminate\Support\Str
  • Illuminate\Support\Testing\Fakes\NotificationFake
  • Illuminate\Translation\Translator
  • Illuminate\Validation\Rule
  • Illuminate\View\Factory
  • Illuminate\View\View

更多参考 :
教程:如何利用 macro 方法来扩展 Laravel 的基础类的功能

翻译:神奇的 Laravel 宏指令(Macro)


附言:
关于 PHPSTORM IDE 没有 Macro 提示的问题解决方法:

安装 barryvdh/laravel-ide-helper 组件

composer require --dev barryvdh/laravel-ide-helper

跟着文档一步一步走就行了,注意文档中有一句话,如果要使用代码提示的话,必须根据文档中的要求来写:

为 Macro 和 Mixin 自动生成 PHPDoc

这个包可以为 Macro 和 Mixin 生成 PHPDocs,它们将被添加到 _ide_helper.php 文件中。

但这仅在您在声明宏时使用类型提示时才有效。

Str::macro('concat', function(string $str1, string $str2) : string {
    return $str1 . $str2;
});
本作品采用《CC 协议》,转载必须注明作者和本文链接
我从未见过一个早起、勤奋、谨慎,诚实的人抱怨命运。
附言 1  ·  1个月前

添加了对 IDE 的支持。

本帖由系统于 1个月前 自动加精
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 21

项目中使用宏指令做统一响应

2个月前 评论


ide 对宏命令不友好,跳转不过去啊,我装完还是没有提示

2个月前 评论
MArtian (楼主) 2个月前
风吹过有夏天的味道 (作者) 2个月前
MArtian (楼主) 2个月前
mengdodo 1个月前
MArtian (楼主) 1个月前

用来管理方法是一个不错的想法,不过如果可以自己使用容器和接口逻辑开发的话, 这个包无所谓

1个月前 评论

感觉用处不大,这些可以自定义函数解决 比这个要节省代码

1个月前 评论
还不出来 3周前
gema 3周前
chenBJ

我还以为python中flask框架中的宏咋跑这来了 :joy:

1个月前 评论

不错,$request->isVip() 相比 (bool)$request->user()?->is_vip,简洁了些。

4周前 评论
MArtian (楼主) 4周前
小李世界 (作者) 4周前
liaosp 4周前
MArtian (楼主) 4周前
小李世界 (作者) 4周前
// 判断用户是否是 VIP
        Request::macro('isVip', function(){
            return auth()->check() && $this->user()->is_vip;
        });

        // 判断字符串是否大于等于给定长度
        Str::macro('lengthCheck', function(string $str1, int $length)
        {
            return static::length($str1) >= $length;
        });

可以这样吗?

3周前 评论
MArtian (楼主) 3周前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!