zoukankan      html  css  js  c++  java
  • Laravel5.1-Eloquent ORM:起步

    概述

    有很多朋友问,MCV的M是在哪里介绍的,这里就是介绍M的地方了。
    Laravel有一个强大的数据库ORM Eloquent,它的原理是每张数据表对应一个Model,对Model的操作就对应数据库的操作,你只用管对model的操作,而数据库的操作是自动的(意味着你不用写SQL语句)。

    Eloquent采用了Active Record的模式,表映射到类,记录映射到对象。它的特点是简单直观,但解耦方面稍弱。还有一种叫做Data Mapping(以Doctrine为代表),它对象操作和数据操作是完全分离的,有兴趣可以google一下。

    使用Eloquent之前,先配置一下数据库连接。

    定义数据模型(Models)

    新装好的Laravel App目录下,你会发现已经有一个User.php的模型文件了,在这里会出现一个无数人问的问题:为什么把模型文件放在这里?没有一个Models的目录吗?

    我想Taylor可能是觉得组织Model的方式有很多种,把选择权交给大家吧;

    按照我自己的习惯,我是会把Model放进app目录下新建的Models文件夹里,怎么弄呢?

    你只需要改一下User Model命名空间即可:

    1. 1 namespace AppModels;
      2 use ...
      3 class User extends Model implements AuthenticatableContract, CanResetPasswordContract
      4 {
      5 ...
      6 }

    不过User 模型因为与Auth还有点关系,所以在config/auth.php里,你还需要修改一下命名空间:

    1. 'model' =>AppModelsUser::class,

    用artisan生成Model文件

    1. php artisan make:model User

    如果像刚才讲的一样,喜欢吧model放到models文件夹里,就这样写:

    1. php artisan make:model ModelsUser

    同时,比较好的习惯是后面再带一个migration,因为建立好模型后就要去建表了,所以生成Model的正确姿势是:

    1. php artisan make:model ModelsUser --migration
    2. php artisan make:model ModelsUser -m

    定义模型的内容

    用artisan自动生成model以后,典型的内容应该是这样的:

    1 <?php
    2 namespace App;
    3 use IlluminateDatabaseEloquentModel;
    4 class Flight extends Model
    5 {
    6 //
    7 }

    下面我们就来看可以在里面写点啥:

    表名

    首先第一个肯定是关联表了,否则怎么使用ORM呢?

    1. <?php
    2. namespace App;
    3. use IlluminateDatabaseEloquentModel;
    4. class Flight extends Model
    5. {
    6. protected $table = 'my_flights';
    7. }

    这样就把Flight这个模型和my_flights这张表以及表里的字段联系起来了,表的字段会自动成为Flight这个模型对象的属性。

    主关键字(Primary Keys)

    Laravel默认自增id字段为主Key,当然你也可以指定其他的字段:

    1. $primaryKey ='user_name';

    时间戳(Timestamps)

    一般情况下,Laravel默认你的表中自带created_atupdated_at这两个字段,并会在生成数据的时候自动填充。

    如果你的表中没有这两个字段或者不想自动管理它们,可以像下面这样关掉:

    1. <?php
    2. namespace App;
    3. use IlluminateDatabaseEloquentModel;
    4. class Flight extends Model
    5. {
    6. public $timestamps = false;
    7. }

    你还可以改时间戳在数据库中的存储格式(默认从年存到秒),还有从数据库中读出来的显示格式:

    1. <?php
    2. namespace App;
    3. use IlluminateDatabaseEloquentModel;
    4. class Flight extends Model
    5. {
    6. protected $dateFormat = 'U';
    7. }

    文档这里又是个啃爹货,你以为每个人都懂'U'是什么意思吗?写成'Y-m-d'这种大家都能理解的不好吗?关于'U'是什么意思,以及一共有多少种时间格式,请参阅:

    http://php.net/manual/en/datetime.createfromformat.php

    数据库连接

    一般不写的话是连接默认数据库,但是你也可以把某个模型关联到其他数据库(当然,这个数据库你必须事先在config里面配置过):

    1. <?php
    2. namespace App;
    3. use IlluminateDatabaseEloquentModel;
    4. class Flight extends Model
    5. {
    6. protected $connection = 'connection-name';
    7. }

    获取模型集合

    所谓获取模型集合就是获取多条数据库记录对应的模型。获取的方法和数据库查询器(DQB)的方法完全一样的,只不过指定的类名不是DB,而是相应的模型:

    1. namespace AppHttpControllers;
    2. use AppFlight;
    3. use AppHttpControllersController;
    4. class FlightController extends Controller
    5. {
    6. public function index()
    7. {
    8. $flights = Flight::all();
    9. return view('flight.index', ['flights' => $flights]);
    10. }
    11. }

    这个All()在这里就是把全部记录取出来,也就是自动把对应的模型对象集合全部取出来了。

    读取模型属性

    字段和字段值就是模型对象的属性和属性值:

    1. foreach ($flights as $flight) {
    2. echo $flight->name;
    3. }

    进一步筛选数据

    1. $flights = AppFlight::where('active', 1)
    2. ->orderBy('name', 'desc')
    3. ->take(10)
    4. ->get();

    看过DQB后,这些都是最基本的内容了。

    Collections

    像all()和get()这种会返回多条数据的方法,在Eloquent里面会返回一个collection对象集合(对象装在对象里),而不是像DQB的数组结果集合(对象装在数组里)。Collection其实前面已经讲过了,它是在数据查询出来后,提供了一系列处理数据的方法,非常强大实用。

    当然,collection结果集也是可以直接遍历的:

    1. foreach ($flights as $flight) {
    2. echo $flight->name;
    3. }

    切片化处理结果(chunk)

    处理海量数据的时候,你可以把数据切片化处理,这样节省内存:

    1. Flight::chunk(200, function ($flights) {
    2. foreach ($flights as $flight) {
    3. //
    4. }
    5. });

    方法和DQB一样,这里就不多说了。

    获取单个模型对象(单条数据)

    取单个对象,用find()first()方法:

    1. // 这个是通过ID查找
    2. $flight = AppFlight::find(1);
    3. // 这个是在结果集中取第一个记录
    4. $flight = AppFlight::where('active', 1)->first();

    find()方法还可以用于查询多条记录,用数组就行:

    1. $flights = AppFlight::find([1, 2, 3]);

    找不到记录怎么办

    很多情况下,你希望找不到记录的时候自动报错,这时候就使用findOrFail()firstOrFail()方法,这两个方法首先会去找第一条记录,找不到就会抛出一个IlluminateDatabaseEloquentModelNotFoundException类指定的错误,如果这个错误没有被指定抛出,就会自动抛出一个http 404错误:

    1. $model = AppFlight::findOrFail(1);
    2. $model = AppFlight::where('legs', '>', 100)->firstOrFail();

    结果计算(Aggregates)

    和DQB一样,用于结果计算的count,sum,max这些方法都可以用,不过这些方法返回是数字而不是对象哦:

    1. $count = AppFlight::where('active', 1)->count();
    2. $max = AppFlight::where('active', 1)->max('price');

    模型的增删改

    1. namespace AppHttpControllers;
    2. use AppFlight;
    3. use IlluminateHttpRequest;
    4. use AppHttpControllersController;
    5. class FlightController extends Controller
    6. {
    7. public function store(Request $request)
    8. {
    9. $flight = new Flight;
    10. $flight->name = $request->name;
    11. $flight->save();
    12. }
    13. }

    这个就是最简单的通过模型新增数据库记录的方法了,在save()之前,都是在操作模型数据对象,save()后,模型的属性就被写到数据库的字段里了,注意id,created_at,updated_at字段是默认生成的,不用手动指定;

    除了用save()方法,还有一个方法create(),这个方法可以批量更新属性,并直接写入数据库:

    1. public function store(Request $request)
    2. {
    3. $flight = Flight::create([
    4. 'name'=>'MH370',
    5. 'passengers'=>'227',
    6. 'from'=>'KL',
    7. 'to'=>'BJ',
    8. 'status'=>'missing'
    9. ]);
    10. }

    如果你的$request对象刚好就是下面那个数组(当然实际不是的,还需要处理一下,具体看请求那一章节),你可以这样写:

    1. public function store(Request $request)
    2. {
    3. $flight = Flight::create($request);
    4. }

    是不是变得非常简单了?

    但是,有一个问题:

    批量更新注入(Mass Assignment)

    这个$request是表单或者url参数提交过来的,所以用户可以自由设置的.万一你的模型中有这样一个属性'is_admin', 黑客直接把这个参数改为1,然后通过create方法写入数据库,那你不就傻X了么。

    所以,为了预防这个问题的发生,在写入数据库之前,还需要校验一下哪些字段是可以通过create()方法批量改的(也就是用户有权利自由改的),哪些是不能外部参数直接改的,必须手动指定属性,然后save()的;

    1. <?php
    2. namespace App;
    3. use IlluminateDatabaseEloquentModel;
    4. class Flight extends Model
    5. {
    6. protected $fillable = ['name','passengers'];
    7. }

    $fillable顾名思义就是可以填的字段,是个白名单;一般不能填写的字段占少数,为了填写方便,还有个黑名单$guarded:

    1. namespace App;
    2. use IlluminateDatabaseEloquentModel;
    3. class Flight extends Model
    4. {
    5. protected $guarded = ['status'];
    6. }

    也就是说,除了status不能批量更新,其他字段都能批量更新;

    注意了:如果你使用了create()方法,但又没有添加上面说的写保护属性,那么会报Mass Assignment的错误,这是无数人遇到过的问题。

    1. $flight = AppFlight::find(1);
    2. $flight->name = 'New Flight Name';
    3. $flight->save();

    原理都是很简单的,改属性,然后存数据库。

    批量更新

    还有一个update()方法,可以对多条数据同时更新:

    1. AppFlight::where('active', 1)
    2. ->where('destination', 'Shanghai')
    3. ->update(['delayed' => 1]);

    上面的意思是:所有在飞上海的航班,延误状态改为1;

    这个update()和 create()一样,也是对mass assignment写保护是有要求的;

    其他生成记录的方法

    还有一些常用的逻辑,laravel也封装好了:

    1. // 先找第一条记录,如果没有就新建一条记录(写数据库)
    2. $flight = AppFlight::firstOrCreate(['name' => 'Flight 10']);
    3. // 先找第一条记录,如果没有就新建一个对象(不写数据库)
    4. $flight = AppFlight::firstOrNew(['name' => 'Flight 10']);

    如果你一直在看这个文档攻略,现在你应该熟悉laravel的语义,一看到create这种词,就知道是拿数据库开刀的。

    1. $flight = AppFlight::find(1);
    2. $flight->delete();

    有save()就有delete(),道理很简单,delete()也是直接操作数据库。

    通过ID删除记录

    1. AppFlight::destroy(1);
    2. AppFlight::destroy([1, 2, 3]);
    3. AppFlight::destroy(1, 2, 3);

    上面的步骤是先找后删除,有个简写的方法,destroy(),你把一个id或多个id输入进去,就可以在数据库中直接删除数据了;

    按条件删除

    1. $deletedRows = AppFlight::where('active', 0)->delete();

    似乎是理所当然的事情,不解释。

    软删除

    软删除其实就是假删除,数据都还在,只不过是看不见了,这是非常常用的功能。

    要使用软删除,首先要做一下设置:

    1. <?php
    2. namespace App;
    3. use IlluminateDatabaseEloquentModel;
    4. use IlluminateDatabaseEloquentSoftDeletes;
    5. class Flight extends Model
    6. {
    7. use SoftDeletes;
    8. protected $dates = ['deleted_at'];
    9. }

    在模型中引入一个IlluminateDatabaseEloquentSoftDeletes PHP Trait,然后添加一个属性'deleted_at'

    当然,只是模型有'deleted_at'还不够,数据库也必有对应的字段,migration提供了一个方便的写法:

    1. Schema::table('flights', function ($table) {
    2. $table->softDeletes();
    3. });

    现在就可以使用软删除了,使用delete()方法时,不是真删,而是假删。

    如果你要判断某个模型是否处被软删除了,可以:

    1. if ($flight->trashed()) {
    2. //
    3. }

    显而易见,就是trashed()这个方法,也就是暂时丢进回收站,还可以回收的。

    查询被软删除(回收站里)的记录

    查全部数据

    默认情况下,被trashed的记录都不会被查询出来,如果要查全部数据,加个方法withTrashed()就行了:

    1. $flights = AppFlight::withTrashed()
    2. ->where('account_id', 1)
    3. ->get();

    只查回收站里的数据

    1. $flights = AppFlight::onlyTrashed()
    2. ->where('airline_id', 1)
    3. ->get();

    显而易见,onlyTrashed()。

    还原回收站数据

    1. $flight->restore();

    这个还原整个模型被软删除的数据;

    1. AppFlight::withTrashed()
    2. ->where('airline_id', 1)
    3. ->restore();

    这个是还原指定条件的数据;

    强制永久性删除

    设置了软删除后,如果要强制删除,可以:

    1. $flight->forceDelete();

    Query Scopes

    这个scope是范围的意思,Query Scopes讲的就是查询范围,是用来限制模型的读取范围的。
    软删除就是个很好的Query Scopes的例子,它默认只查出那些没被软删除的记录;

    我们可以自己来定义scope:

    1. namespace App;
    2. use IlluminateDatabaseEloquentModel;
    3. class User extends Model
    4. {
    5. public function scopePopular($query)
    6. {
    7. return $query->where('votes', '>', 100);
    8. }
    9. public function scopeActive($query)
    10. {
    11. return $query->where('active', 1);
    12. }
    13. }

    scopePopular是个魔术方法,这样就定义了一个名为Popular的Scope;

    使用scope

    1. $users = AppUser::popular()->active()->orderBy('created_at')->get();

    只要在模型后跟上那么scope的名字即可;

    动态Scope

    有时候你需要把数据筛选条件变成动态的,很简单,加个参数就好:

    1. namespace App;
    2. use IlluminateDatabaseEloquentModel;
    3. class User extends Model
    4. {
    5. public function scopeOfType($query, $type)
    6. {
    7. return $query->where('type', $type);
    8. }
    9. }

    用的时候这样用:

    1. $users = AppUser::ofType('admin')->get();

    Laravel 5.2 Query Scope 改进了很多,变得十分强大和方便

    模型事件

    模型事件就是模型在进行数据读写时候发生的事件,例如creating, created, updating, updated, saving, saved, deleting, deleted, restoring, restored.这些。你可以在发生这些行为的时候执行而外的代码逻辑。

    不像JS的事件是初始化就会自动侦听的,laravel的事件一般需要预置一个侦听器(listener),如果你要全局侦听,最好的地方就是放在Service Provider的boot()方法里:

    1. <?php
    2. namespace AppProviders;
    3. use AppUser;
    4. use IlluminateSupportServiceProvider;
    5. class AppServiceProvider extends ServiceProvider
    6. {
    7. public function boot()
    8. {
    9. User::creating(function ($user) {
    10. if ( ! $user->isValid()) {
    11. return false;
    12. }
    13. });
    14. }
    15. public function register()
    16. {
    17. //
    18. }
    19. }

    一旦有模型新建记录的事件发生,就会先运行闭包里的代码。

    creating, created, updating, updated, saving, saved, deleting, deleted, restoring, restored.至于这些事件的具体定义,就不多说了,看字面意思就ok了。

  • 相关阅读:
    gulp ( http://markpop.github.io/2014/09/17/Gulp入门教程 )
    less 官网讲解 ( http://www.bootcss.com/p/lesscss/ )
    js 闭包 弊端
    js 闭包 理解 copy
    js 中 的 if使用条件
    $ each() 小结
    文件自动加载
    (openssl_pkey_get_private 函数不存在)phpstudy开启openssl.dll 时提示httpd.exe 丢失libssl-1_1.dll
    form
    js字符串处理
  • 原文地址:https://www.cnblogs.com/isykw/p/6151649.html
Copyright © 2011-2022 走看看