orm 系列 之 Eloquent使用2

上一篇介绍了Eloquent的migrations和Scheme Builder功能,本文介绍Eloquent最重要的Model。

本文会按照下面的顺序介绍:

  • Model的创建
  • CRUD操作
  • 查询和聚合操作
  • Hydrate
  • 组合查询

Model创建

在laravel中,创建一个model非常简单,我们可以通过下面的命令创建

php artisan make:model Book

默认在app目录下生成一个Book.php文件,打开后看到:

class Book extends Model
{
    //
}

就这么简单的几行,我们就可以使用了,从这个model出发,我们可能想要知道Book是在哪个表中的,这个时候,我们可以设置$table属性,同时,我们想要设置主键及其类型,我们就设置$primaryKey,$keyType,同时,我们可能还想要指定数据库连接,当然也行,通过设置$connection即可,于是我们的Book就变为:

class Book extends Model
{
    protected $connection = 'mysql';
    protected $table = 'books';
    protected $primaryKey = 'id';
    protected $keyType = 'int';
}

当然,上面这些属性不设置也行,就使用这些值的默认值就可以。

CRUD操作

有了模型,让我们来完成基本的CRUD操作,先是创建动作,

Route::get( 'book_create', function () {
    $book              = new Book;
    $book->title       = 'My First Book!';
    $book->pages_count = 230;
    $book->price       = 10.5;
    $book->description = 'A very original lorem ipsum dolor sit amet...';
    $book->save();
    echo 'Book: ' . $book->id;
} );

我们通过save就可以完成insert操作,此处有几个tricky方法,首先是我们的Model中不存在title这些字段,那是怎么设置的呢?然后就是save操作的时候,怎么知道是insert Or update呢?

先回答第一个属性问题

// class model
public function __set($key, $value)
{
    $this->setAttribute($key, $value);
}
public function __get($key)
{
    return $this->getAttribute($key);
}

model中通过magic method,可以正确的设置和获取属性。接着回答第二个问题,save怎么知道是insert还是update呢?

if ($this->exists) {
    $saved = $this->isDirty() ?
    $this->performUpdate($query, $options) : true;
}
else {
    $saved = $this->performInsert($query);
}

我们可以看到Model中有个属性exists表明记录是否在数据库中,此处还有个有意思的函数isDirty,看下实现

public function getDirty()
{
    $dirty = [];

    foreach ($this->attributes as $key => $value) {
      if (! array_key_exists($key, $this->original)) {
        $dirty[$key] = $value;
      } elseif ($value !== $this->original[$key] &&
                ! $this->originalIsNumericallyEquivalent($key)) {
        $dirty[$key] = $value;
      }
    }

    return $dirty;
}

此处会存储一个从数据库中加载出来的数据original,然后通过现在的attributesoriginal比较来知道哪些字段需要更新,在执行更新操作的时候,自然就是下面的操作了,只会跟新dirty字段。

$dirty = $this->getDirty();

if (count($dirty) > 0) {
    $this->setKeysForSaveQuery($query)->update($dirty);

    $this->fireModelEvent('updated', false);
}

下面是读取操作,我们可以执行在命令行中执行php artisan tinker进入命令中,然后通过下面的方法,返回一些数据:

>>> App\Book::all()
=> Illuminate\Database\Eloquent\Collection {#711
     all: [
       App\Book {#713
         id: 1,
         title: "My First Book!",
         pages_count: 230,
         price: "10.50",
         description: "A very original lorem ipsum dolor sit amet...",
         created_at: "2016-12-01 15:13:35",
         updated_at: "2016-12-01 15:13:35",
       },
     ],
   }

查询方法有很多,我们不做过多的介绍,我们此处来看下,all方法的执行,all在Model中是不存在的,那怎么调用的呢?这也是php的magic method实现的,

public function __call($method, $parameters)
{
    if (in_array($method, ['increment', 'decrement'])) {
      return call_user_func_array([$this, $method], $parameters);
    }

    $query = $this->newQuery();

    return call_user_func_array([$query, $method], $parameters);
}
public static function __callStatic($method, $parameters)
{
    $instance = new static;

    return call_user_func_array([$instance, $method], $parameters);
}

其实现都是一致的,都是通过调用Eloquent/Builder来实现的。

下面我们来看更新操作怎么做的

>>> $book1 = App\Book::find(1);
=> App\Book {#709
     id: 1,
     title: "My First Book!",
     pages_count: 230,
     price: "10.50",
     description: "A very original lorem ipsum dolor sit amet...",
     created_at: "2016-12-01 15:13:35",
     updated_at: "2016-12-01 15:13:35",
   }
>>> $book1->price = 12;
=> 12
>>> $book1->save();

上面的步骤就好,save通过之前的分析,会自动更新price。

查询和聚合

Eloquent提供了丰富的查询方式,通过前面的__call方法分析,我们知道,这些最终调用的都是Eloquent/Builder方法,where方法也不例外,where其最简单的形式如下:

where('field_name', 'operator', 'term')

然后通过连贯操作,我们可以任意的组合where条件,此处term的含义就是我们怎么讲where条件组装,是condition1 AND condition2 还是 condition1 OR condition2.

聚合操作如'count', 'min', 'max', 'avg', 'sum',这些操作在实现上都是通过下面一段类似的代码实现:

public function max($column)
{
    return $this->aggregate(__FUNCTION__, [$column]);
}

通过_FUNCTION_来闯入方法名。

Hydrate

这个功能其实我们在之前The Clean Architecture in PHP 读书笔记(十)就提到过这个问题,如何能在数据库中记录和我们的Model之间进行转换,此处有转换有两个方向,先看数据记录怎么变为Model,此处有几个方法

  1. Model::create新建,
  2. Model::hydrate将数据库数据转换为Model

当时遇到的问题有字段对应,另一个是安全问题,具体来说就是怎么知道数组中的key和Model的attribute对应,Eloquent的方法是直接将属性存储为一个$attributes数组,然后由用户自己根据字段名进行获取,但是,其实这也会有个问题,就是一旦字段名更改了,我们必须要去更改所有直接使用字段名的地方。

当然这个问题存在了,Eloquent就尝试着去解决了,这就用到了Accessors and mutators,直接看代码:

public function getPriceAttribute($value)
{
    return '$ ' . $value;
}

public function setTitleAttribute( $value )
{
    $this->attributes['title'] = strtolower($value);
}

通过将值截断,我们可以在里面做我们想做的任何操作,实现属性的转换。

另一个是安全问题,我们怎么知道哪些字段是Model的属性,这是通过可以被赋值属性的“白名单”$fillable和可以被赋值属性的$guarded“黑名单”完成的,在白名单里面的就是可以设置的属性,黑名单里面的是不能的。

组合查询

组合查询也是非常酷的一个功能,我们在开发中时常会写出各种各样的名字查询,如getLongBook,getCheapBook,getLongAndCheapBook等等,每次这种查询我们都必须要去写个方法,苦不堪言,那有什么好的方法呢?这个时候Eloquent提供了一个很酷的功能。

public function scopeLong(  Query $query, $long = 700)
{
    return $query->where( 'pages_count', '>', $long );
}
public function scopeCheap(  Query $query, $price = 500)
{
    return $query->where( 'price', '<', $price );
}
>>> App\Book::cheap()->long()->toSql()
=> "select * from `books` where `price` < ? and `pages_count` > ?"
>>>

我们通过Model的scope功能,我们就能快速实现组合查询,以后再也不用烦恼的写查询了。

以上就是Eloquent的Model操作的基本介绍,下节将会介绍Eloquent的关系。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,012评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,589评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,819评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,652评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,954评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,381评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,687评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,404评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,082评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,355评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,880评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,249评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,864评论 3 232
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,007评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,760评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,394评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,281评论 2 259

推荐阅读更多精彩内容