使用 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  ·  2年前

添加了对 IDE 的支持。

本帖由系统于 2年前 自动加精
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 21

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

2年前 评论


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

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

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

2年前 评论
chenBJ

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

2年前 评论

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

2年前 评论
MArtian (楼主) 2年前
小李世界 (作者) 2年前
liaosp 2年前
MArtian (楼主) 2年前
小李世界 (作者) 2年前
// 判断用户是否是 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;
        });

可以这样吗?

2年前 评论
MArtian (楼主) 2年前

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