Laravel 中 Trait 的使用

本文探讨了如何通过使用PHP Trait来减少在员工和客户模型中重复的user_id设置代码,实现代码复用,并介绍了Trait的基本概念、使用方法、冲突解决及在Laravel模型关联中的应用实例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

准备

我们来查看迁移中三张表的关系

public function up()
{
    // 用户表
    Schema::create('users', function (Blueprint $table) {
        $table->increments('id');
        $table->string('name');
        $table->string('email')->unique();
        $table->string('password');
        $table->timestamps();
    });
    // 员工表
    Schema::create('employees', function (Blueprint $table) {
        $table->increments('id');
        $table->integer('user_id');
        $table->string('employee_code');
        $table->string('phone');
        $table->timestamps();
    });
    // 客户表
    Schema::create('clients', function (Blueprint $table) {
        $table->increments('id');
        $table->integer('user_id');
        $table->string('company_name');
        $table->string('phone');
        $table->timestamps();
    });
}

其中,员工和客户都关联了 users 表,都具有 user_id 字段。

前言

大家可能经常看到以下几种情况:

class Employee extends Model
{
    protected static function boot()
    {
        parent::boot();

        static::saving(function ($employee){
            $employee->user_id = $employee->user_id ?? \auth()->id();
        });
    }
} 

// 或者

class Client extends Model
{
    protected static function boot()
    {
        parent::boot();

        static::saving(function ($client){
            $client->user_id = $client->user_id ?? \auth()->id();
        });
    }
}

// 或者直接在控制器中指定 user_id

可以看到以上代码明显有重复的,那么如何将重复的代码分离出去达到复用的效果呢?

可能我们会想到将它封装到公共方法中,在模型或者在控制器中调用。

public function hasUser($model)
{
    $model->user_id = $model->user_id ?? \auth()->id();
}

从上面的示例中发现不是很好,不够优雅,这时我们可以使用Trait

trait HasUser
{
    public static function booHasUser()
    {
        static::saving(function ($model){
            $model->user_id = $model->user_id ?? \auth()->id();
        });
    }
}
// 调用
class Employee extends Model
{
    use HasUser;
}

接下来我们来讲解一下Trait的使用:

Trait 是什么

Trait [treɪt] 翻译过来是 "特性"、"特点" 、"特质",是一种在 PHP 中复用代码的形式。

官方解释:

Trait 是为类似 PHP 的单继承语言而准备的一种代码复用机制。Trait 为了减少单继承语言的限制,使开发人员能够自由地在不同层次结构内独立的类中复用 method。 Trait 和 Class 组合的语义定义了一种减少复杂性的方式,避免传统多继承和 Mixin 类相关典型问题。

Trait 使用

1. 使用关键字Trait来定义


namespace App\Traits;

Trait HasUser
{
    public static function booHasUser()
    {
        static::saving(function ($model){
            $model->user_id = $model->user_id ?? \auth()->id();
        });
    }
}

2. 在类中使用use将该trait引入

class Employee extends Model
{
    use HasUser;
}

3. trait 有优先级顺序,即:调用类 > Trait > 父类

trait HasUser
{
    public function myTrait()
    {
        echo "user trait" . PHP_EOL;
    }
}

class Employee
{
    use HasUser;
    public function myTrait()
    {
        echo "employee trait" . PHP_EOL;
    }
}

$employee = new Employee();
$employee->myTrait();

结果:

Traits ✗ php TestTrait.php
employee trait

以上例子我们可以看出调用的是类中的myTrait()方法

4. 多个Trait使用逗号分隔

trait HasUser
{
    public function myTrait()
    {
        echo "user trait" . PHP_EOL;
    }
}

trait Address
{
    public function getCity()
    {
        echo "南京" . PHP_EOL;
    }
}

class Employee
{
    use HasUser, Address;
}

$employee = new Employee();
$employee->myTrait();
$employee->getCity();

5. 冲突的解决

如果两个 trait 都插入了一个同名的方法,如果没有明确解决冲突将会产生一个致命错误。

trait HasUser
{
    public function myTrait()
    {
        echo "user trait" . PHP_EOL;
    }
    public function sayHello()
    {
        echo "user say hello!" . PHP_EOL;
    }
}

trait Address
{
    public function getCity()
    {
        echo "南京" . PHP_EOL;
    }
    public function sayHello()
    {
        echo "address say hello!" . PHP_EOL;
    }
}

class Employee
{
    use HasUser, Address;
}

$employee = new Employee();
$employee->myTrait();
$employee->getCity();
$employee->sayHello();

结果:

Traits ✗ php TestTrait.php
PHP Fatal error:  Trait method sayHello has not been applied, because there are collisions with other trait methods on Employee in /app/Traits/TestTrait.php on line 27

Fatal error: Trait method sayHello has not been applied, because there are collisions with other trait methods on Employee in /app/Traits/TestTrait.php on line 27

从执行的结果我们可以看出两个trait方法存在冲突。

为了解决多个 trait 在同一个类中的命名冲突,需要使用 insteadof 操作符来明确指定使用冲突方法中的哪一个。还可以使用as操作符为某个方法别名。

trait HasUser
{
    public function myTrait()
    {
        echo "user trait" . PHP_EOL;
    }
    public function sayHello()
    {
        echo "user say hello!" . PHP_EOL;
    }
}

trait Address
{
    public function getCity()
    {
        echo "南京" . PHP_EOL;
    }
    public function sayHello()
    {
        echo "address say hello!" . PHP_EOL;
    }
}

class Employee
{
    use HasUser, Address {
        HasUser::sayHello insteadOf Address;
        Address::sayHello as sayHelloAddr;
    }
}

$employee = new Employee();
$employee->myTrait();
$employee->getCity();
$employee->sayHello();
$employee->sayHelloAddr();

结果:

Traits git:(release/2.1.6) ✗ php TestTrait.php
user trait
南京
user say hello!
address say hello!

6. laravel 模型关系使用Trait

很多模型中都有如下代码

public function user()
{
    return $this->belongsTo(User::class);
}

如何复用这段代码呢?

下面我们创建一个trait用来存放用户相关的代码

namespace App\Traits;

use App\Models\User;

trait BelongsToUser
{
    public static function bootBelongsToUser()
    {
        static::creating(function ($model) {
            if (!$model->user_id) {
                $model->user_id = \auth()->id();
            }
        });
    }

    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

员工模型中使用Trait

namespace App\Models;

use App\Traits\BelongsToUser;
use Illuminate\Database\Eloquent\Model;

class Employee extends Model
{
    use BelongsToUser;
}

客户模型中使用Trait

namespace App\Models;

use App\Traits\BelongsToUser;
use Illuminate\Database\Eloquent\Model;

class Client extends Model
{
    use BelongsToUser;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值