Laravel中的模型(Eloquent ORM)的使用

黎小强
2021-06-22 / 0 评论 / 557 阅读 / 正在检测是否收录...

1. 模型的定义

一. 默认设置

  1. 框架可以使用 Eloquent ORM 进行数据库交互,也就是关系对象模型
  2. 在数据库入门阶段,我们已经创建了一个 User.php 模型,如下:

    php artisan make:model Http/Models/User //默认在 app 目录
  3. 而调用的时候,我们也知道表名要遵循它默认规则,修改为复数,或 特定

    class User extends Model
    {
        protected $table = 'user';
    }
  4. 系统假定你的主键为 id,如果你要修改默认主键,可以特定

    class User extends Model
    {
        protected $primaryKey = 'xid';    //主键
    }
    
  5. 系统假定你的主键 id 为自增性,意味着是主键会自动转换 int 类型;
  6. 如果你希望不是非自增,非数值类型主键,可以设置 $incrementing 取消;

    public $incrementing = false;    //非数字整型主键
  7. 如果你主键不是一个整数,那么需要$keyType 设置为 string;

    protected $keyType = 'string'
  8. 系统默认情况下会接管 created_atupdated_at 两个时间戳列;
  9. 如果不想让系统干涉这两个列,可以设置 false 取消;

    public $timestamps = false;        //取消写入时间戳
  10. 如果你想自定义时间戳的格式,可以设置;

    protected $dateFormat = 'U';
  11. 可以更改创建时间 created_at 和更新时间 updated_at 字段名

    const CREATED_AT = 'create_time';
    const UPDATED_AT = 'update_time';
  12. 默认读取 database.php 配置的数据库连接,也可以在模型端局部更改

    protected $connection = 'mysql';

二. 模型定义

  1. 之前在查询构造器部分,把常用的数据库操作基本讲完,模型大体相同
  2. 比如,我们要查询所有数据,直接使用模型::all()即可;

    //查询所有记录
    $users = User::get(); //或 all()
    return [$users];
  3. 也可以像查询构造器一样,添加各种各样的条件,写法一样;

    //查询性别为男,价格大于 90,限制显示 2 条
    $users = User::where([
        ['gender', '=', '男'],
        ['price', '>', 95]
    ])->limit(2)->get();
  4. 虽然安装了插件,但模型还是没有代码提示,可以通过安装插件解决;

    composer require barryvdh/laravel-ide-helper
    
    php artisan ide-helper:generate    // – 为 Facades 生成注释
    php artisan ide-helper:models     //– 为数据模型生成注释
    php artisan ide-helper:meta     //– 生成 PhpStorm Meta file
  5. 其它查询方法基本和查询构造器一样,如果有不一样,参考错误提示;
  6. 这里列出官网给出示例的方法,对照实验(结合详细文档,重复较多);

    关于更多 Eloquent ORM 操作, 看手册 ( https://learnku.com/docs/laravel/8.x/eloquent/9406

    (1) .find(1) //通过主键查找
    (2) .first() //查找第一个
    (3) .firstWhere() //找到查询中的首个
    (4) .find([1,2,3]) //通过数组查找
    (5) .firstOr() //查找首个返回,支持闭包
    (6) .firstOrFail() //找不到时返回异常
    (7) .count()、max()等集合 //集合操作
    
    //PS:还有很多在查询构造器中的方法,比如排序、分组子查询等等都可以使用(并未一一验证)。  

2. 模型的增删改

一. 增操作

  1. 新增方法如下,注意:默认模型接管 created_atupdated_at

    $users = new User();
    $users->username = '辉夜';
    $users->password = '123';
    $users->email = 'huiye@163.com';
    $users->details = '123';
    $users->save();
  2. 使用 create() 方法实现新增,但需要在模型端设置批量赋值的许可;
User::create([
    'username' => '辉夜',
    'password' => '123',
    'email' => 'huiye@163.com',
    'details' => '123',
]);


//许可批量赋值,默认不可
protected $fillable = [
    'username',
    'password',
    'email',
    'details'
];

//不许可的批量赋值,不可和$fillable 同时使用
//protected $guarded = ['uid'];
//如果取消批量赋值限制,直接如下
protected $guarded = [];

//PS:必须在模型中定义批量赋值的可填充字段,否则无法生效;防止用户不小心设置新值;

二. 改(更新)操作

  1. 更新,只要是查找到一条数据的情况下使用 save()就是更新

    $users = User::find(321);    //先查出,返回对象
    $users->username = '夜辉';
    $users->save();
  2. 使用 update() 方法实现批量更新

    User::where('username', '夜辉')
        ->update([
            'username' => '辉夜'
        ]);

三. 删操作

  1. 使用 delete() 方法,可以删除数据;

    $users = User::find(332);
    $users->delete();
    
    //批量删除
    $users = User::where('username', '夜辉');     //名字带 夜辉都删除掉
    $users->delete();
  2. 如果你是通过主键 id 删除,那使用 destroy(id) 方法,免去查询操作

    //通过主键删除
    User::destroy(328);

3. 批量赋值和软删除

一. 批量赋值

  1. 上一节增删改中,新增中我们发现需要进行批量赋值的许可;
  2. 一般情况下,是为了防止提交过来的字段在部分场景中不需要或不能;
  3. 所以,我们需要通过黑白名单机制进行过滤掉必要的字段;

    //通过提交过来的数据一次性新增
    User::create(\Request::all());

二. 软删除

  1. 什么叫软删除?它相对于真实的删除,而并非真正的删除,只是隐藏了;
  2. 首先,需要在数据库创建一个字段 deleted_at(默认),用于判断是否被软删除;
  3. 默认设置这个字段为空(null),如果写入数据,成为非空状态,则说明被删除;
  4. 开启软删除的功能,需要在模型端设置一下:

    //开启软删除功能
    use SoftDeletes;
  5. 当开启了软删除功能,之前的删除操作,都会变成更新操作,给 deleted_at 赋值;

    //删除一
    $users = User::find(82);
    $users->delete();
    
    //删除二
    User::destroy(81);
  6. 而当我们开启了软删除的功能后,此时通过正常的数据获取列表,会自动隐藏

    //软删除的数据不可见
    $users = User::get();
    return [$users];
    
    //单独获取被软删除的数据不行
    $users = User::find(82);
    return [$users];

3. 模型的作用域

一. 本地作用域(scope)

  1. 很多情况下,我们在数据查找时有一部分条件会被重复且大量使用
  2. 而这个条件,可能只是在这个模型对应的数据表使用,别的表并不使用;
  3. 那么这种情况,可以使用本地作用域的方式,将常用的 SQL 封装起来
  4. 参数用法格式: scope + 自定义方法名
  5. 比如:用户模块中,我们大量查询需要查询性别为男,且其它条件的 SQL;

    $users = User::where('gender', '男')
            ->where('price', '>', 90)
            ->get();
    
    //PS:我们可以将性别为男这个片段,封装成一个单独的方法,然后统一在这个模型下调用;

//AppHttpModels;
//本地作用域,搜索自动添加为“男”的条件
//语法:scope 开头,后面名称尽可能包含语义
public function scopeGenderMale($query)
{

   return $query->where('gender', '男');

}

//当然,如果赶紧单词太长,直接 gm()也行
$users = User::genderMale()

           ->where('price', '>', 90)
           ->get();
6. **上面的方法比较死板,适合简单粗暴**,如果想要灵活多变,**支持传递参数**;

//参数可以是 1 个或多个
$users = User::gender('女', -3)

       ->where('price', '>', 90)
       ->get();

//参数 2 和 3,接受控制器传递过来的 1,2
public function scopeGender($query, $value, $value2)
{

   return $query->where('gender', $value)->where('status', $value2);

}


### 二. 全局作用域

1. 全局作用域,顾名思义就是在任意地方都可以有效的封装条件;
2. 比如有个需求,不管在哪里操作,总是显示 status 为 1 的用户;
3. 首先在 app 目录下**创建一个用于全局作用域的目录**:`Scopes` ;
4. 创建一个用于设置 status 为 1 的全局作用域的类,它需要实现  `scope 接口`

namespace AppScopes;
//这里引用代码自动生成
use IlluminateDatabaseEloquentBuilder;
use IlluminateDatabaseEloquentModel;
use IlluminateDatabaseEloquentScope;

class StatusScope implements Scope
{

   public function apply(Builder $builder, Model $model)
   {
       // TODO: Implement apply() method.
       return $builder->where('status', 1);
   }

}

5. 此时,还不能实现全局,因为**需要在模型设置个开关**,让其富有灵活性;

//启用全局作用域
protected static function booted()
{

   parent::booted(); // TODO: Change the autogenerated stub
   static::addGlobalScope(new StatusScope());

}

//PS:而在控制器端,并不需要做任何设置,即可自动添加 status=1 的条件;

6. 当然,如果这个全局只是针对某个模块,并不需要创建一个全局类,直接闭包即可;

static::addGlobalScope('status', function (Builder $builder) {

   return $builder->where('status', 1);

});

//PS:注意 Builder 引入的文件和全局类引入的文件一致,如果引入别的同名类会错;

7. 如果某个查询,并**不需要这个全局条件**,可以**单独移出掉**;

//取消名称为 status 的全局
$users = User::withoutGlobalScope('status')->get();

//取消全局类的条件
$users = User::withoutGlobalScope(StatusScope::class)->get();

//PS:还有 withoutGlobalScopes([])方法,传递参数取消多个全局;


---

## 4. 模型的访问和修改器

### 一. 访问器

1. 访问器:就是在**获取数据列表时,拦截属性并对属性进行修改的过程**;
2. 用法参数格式: `get`+ **字段名** + `Attribute`
3. 比如,我们在输出性别时,在性别左右加上括号,或给邮件转换为大写;

//访问器,前固定 get,后固定 Attribute,Gender 是字段名
//参数$value 是源字段值,可修改返回
public function getGenderAttribute($value)
{

   return '【'.$value.'】';

}

//PS:如果字段名是两个单词中间是下划线:user_name,那么方法名:getUserNameAttribute()

4. 我们也可以创建一个虚拟字段,**用已有的数据字段进行整合**,不过要进行数据追加;

> 单条数据加上虚拟字段
>

**model层**

class User extends Model
{

   $protected $table = 'user';


   //创建一个虚拟字段
   public function getInfoAttribute()
   {
       return $this->username.'-'.$this->gender;
   }

}



**controller层**

$info = User::find(19)->info; //->info

dump($info);//打印 名字-性别



> 列表数据加上info虚拟字段
>

**注意:**一定要把虚拟字段加到`$appends`

**model层**

class User extends Model
{

     $protected $table = 'user';
   //将虚拟字段追加到数据对象列表里去

   protected $appends = ['info'];
   //创建一个虚拟字段
   public function getInfoAttribute()
   {
   return $this->username.'-'.$this->gender;
   }

}



**controller层**

$list = User::all()->toArray();


![1.png](https://blog.97it.net/usr/uploads/2021/06/1624502300.png)
**注意2** :如果 gender **之前已经有访问器修改过**,上面的方法会**得到修改过的结果**;

5. 如果要使用**源字段进行创建虚拟字段**,需要使用下面这种:`$this->attributes[xxxx]`

class User extends Model
{

     $protected $table = 'user';
   //将虚拟字段追加到数据对象列表里去

   protected $appends = ['info'];

   //创建一个虚拟字段
   public function getInfoAttribute()
   {
       return $this->attributes['username'].'-'.$this->attributes['gender'];
   }

}



![image-20210514100243622](/PHP/php框架/Laravel/Laravel学习/readme/image-20210514100243622.png)

### 二. 修改器

1. 修改器,相对于访问器,**是在写入的时候拦截,进行修改再写入**;
2. 用法格式: `set` +  **字段名** + `Attribute`

//修改器,写入数据时,将邮箱转换为大写
public function setEmailAttribute($value)
{

   $this->attributes['email'] = strtoupper($value);

}

3. 可以添加默认的日期列,默认 created_at 和 updated_at;

//设置可以自动写入日期的列
protected $dates = [

   'details'    //详情字段-转换日期格式

];

4. 可以设置字段输出的类型,比如设置一个布尔型,输出时就是 true 和 false;

//设置字段类型
protected $casts = [

   'details' => 'boolean'    //详情字段转换布尔类型

];

5. 注意的是。模型用`create()`方法写入走修改器, 用`insert()`方法不走修改器。
0

评论 (0)

取消