最适合入门的Laravel初级教程(十一)模型Eloquent ORM

花了一篇文章的时间我们终于学会了使用 DB 查询数据;
燃鹅有件悲伤的事情我不知当讲不当讲;
咱以后是基本不使用 DB 的方式查询数据;
因为我们有更强大的模型 Model;
模型 ORM 就是把数据库的表映射到模型类;
既然是用面向对象的方式来操作数据库;
那面向对象的优点自然就继承了;
模型可以方便的复用;

laravel 的 Model 有一个很大的优点是使用了全球最先进的 Eloquent ORM;
她让数据库的操作变的简单安全且无懈可击;
laravel 的 Model 有一个很大的缺点是使用了全球最先进的 Eloquent ORM;
她让数据库的操作变的缓慢笨重且无药可救;
laravel 虽然也有一些东西被人诟病;
比如说路由;但是人家好歹可以缓存啊;
Eloquent ORM 你缓存一个看看;
吐槽归吐槽;
Eloquent ORM 作为 laravel 中的一个亮点部分;
也是耗费了 Taylor 相当大精力和时间的成果;
用起来真的相当的强大;

拧干了水分;
咱下面开始讲干货;
首先就是创建 Model ;
Model 其实就是一个类文件;
之前文章我们讲过;
laravel 中数据表是以复数形式下划线命名法;
但是 Model 文件则是使用单数形式帕斯卡命名法;
既首字母大写的驼峰命名法;
比如说 articles 表的 Model 文件名是 Article.php ;
image_types 表的 Model 文件名则是 ImageType.php ;
当然贴心的 laravel 并不需要我们手动创建模型文件;
直接运行命令行即可;

php artisan make:model Article

运行命令的话;
你会发现这个文件是建在跟目录下的 app 目录下的;
app/Article.php;

如果很多表那么模型都默认生成在这个目录下;
作为一个处女座的强迫症;
我非常之不喜欢这种不整齐不统一不按等级目录存放的方式;
同时参考了大量的 laravel 应用;
最后我决定把模型文件放在 app/Models 目录下;
建议童鞋们也这样来设计模型的目录;
否则;当创建了大量的模型文件后;app 目录下简直无法直视;
命令行也是支持目录的;
上面的命令行就可以改为:

php artisan make:model Models/Article

app/Models/Article.php;

现在我们有了 Model;
接着的任务就是调用 Model 了;
我们新建一个 ModelController 控制器;

php artisan make:controller ModelController

在 app/Http/Controllers/ModelController.php 中写一个 index 方法;

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Article;

class ModelController extends Controller
{
    /**
     * 调用模型
     */
    public function index(Article $articleModel)
    {
        $data = $articleModel->get();
        dump($data);
    }
}

在 routes/web.php 文件中创建路由;

Route::prefix('model')->group(function () {
    Route::get('index', 'ModelController@index');
});

这时候访问下 /model/index 链接;
这时候我们就通过模型把数据全取出来了;

和使用了 DB 取出来的数据一样的是都是一个 Collection 集合;
不一样的是 DB 取出来的是数组;
Model 取出来的是一个类;
需要一层一层的剥开点到 attributes 我们才能看到数据;
如果想更直观点查看的话;
我们去翻翻 Collection 集合的文档能找到一个 toArray 方法;

dump($data->toArray());

这样看着就清爽直观多了吧;

虽然照着做成功了;
但是一些童鞋可能不太明白这种类名跟一个变量直接当参数传给方法的;
public function index(Article $articleModel) ;
laravel 中大量使用了这种方式;
它的概念叫做 依赖注入;
我们上面说过模型其实就是一个类;
既然是类那我们就可以实例化它了;
我们用实例化 new 的方式来实现一遍;
这样会容易理解一些;

    /**
     * 调用模型
     */
    public function index(Article $articleModel)
    {
        $data = $articleModel->get();
        dump($data->toArray());
        $articleModel = new Article();
        $data = $articleModel->get();
        dump($data->toArray());
    }

我们还可以直接像调用静态方法一样直接使用模型;

    /**
     * 调用模型
     */
    public function index(Article $articleModel)
    {
        $data = $articleModel->get();
        dump($data->toArray());
        $articleModel = new Article();
        $data = $articleModel->get();
        dump($data->toArray());
        $data = Article::get();
        dump($data->toArray());
    }

这三种方式打印出来的数据都是一样的;

而且模型可以像 DB 那样链式调用;
DB 有的那些方法模型也都有;
我们用模型的方式来实现一遍之前用 DB 写的 get 方法;
接着在 app/Http/Controllers/ModelController.php 文件中创建一个 get 方法;

    /**
     * 查询数据
     */
    public function get()
    {
        $data = Article::select('category_id', 'title', 'content')
            ->where('title', '<>', '文章1')
            ->whereIn('id', [1, 2, 3])
            ->groupBy('category_id')
            ->orderBy('id', 'desc')
            ->get();
        dump($data->toArray());
    }

在 routes/web.php 文件中创建路由;

Route::prefix('model')->group(function () {
    Route::get('index', 'ModelController@index');
    Route::get('get', 'ModelController@get');
});

没有意外;
取出来的数据跟 app/Http/Controllers/DatabaseController.php 中的 get 方法一样;

像 get 里面这一长串方法一样;
我们在查询数据的时候经常会有略微复杂的查询;
我们可以把它们写成一个模型方法;
比如说在 app/Models/Article.php 文件中写一个 articleList 方法;

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Article extends Model
{
    /**
     * 获取文章列表
     *
     * @return mixed
     */
    public function articleList()
    {
        $data = $this->select('category_id', 'title', 'content')
            ->where('title', '<>', '文章1')
            ->whereIn('id', [1, 2, 3])
            ->groupBy('category_id')
            ->orderBy('id', 'desc')
            ->get();
        return $data;
    }
}

然后我们就可以在各个地方方便的复用了;

    /**
     * 查询数据
     */
    public function get(Article $articleModel)
    {
        $data = Article::select('category_id', 'title', 'content')
            ->where('title', '<>', '文章1')
            ->whereIn('id', [1, 2, 3])
            ->groupBy('category_id')
            ->orderBy('id', 'desc')
            ->get();
        dump($data->toArray());
        $data = $articleModel->articleList();
        dump($data->toArray());
    }

作为入门教程;
这里先不说 Repository 和模型关联;
后续中级和高级教程再深入讲解;

我们接着来讲模型新增数据;
一般是 html 提交一个表单;
把表单的数据存到数据库;
模型有一个 create 方法就是用来新增数据的;
我们新建一个 store 方法和路由;

    /**
     * 插入数据
     */
    public function store(Article $articleModel)
    {
        $data = [
            'category_id' => 6,
            'title' => '文章6',
            'content' => '内容6'
        ];
        $articleModel->create($data);
    }
Route::prefix('model')->group(function () {
    Route::get('index', 'ModelController@index');
    Route::get('get', 'ModelController@get');
    Route::get('store', 'ModelController@store');
});

如果我们直接访问 /model/store 这个方法是会报错的;
因为在模型中需要先定义允许 create 方法插入到数据库的字段;
就是给 $fillable 属性定义允许赋值的字段;
在app/Models/Article.php 模型中定义$fillable 属性;

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Article extends Model
{

    /**
     * 允许赋值的字段
     *
     * @var array
     */
    protected $fillable = ['category_id', 'title', 'content'];
}

这样再次访问 /model/store 就可以把数据插入到数据库中了;
但是如果我们的字段很多;
这样一个一个的定义字段也挺麻烦的;
还好有一个 $guarded 属性是用来定义不允许复制的字段的;
那我们灵机一动;
咱要是不需要保护表中的字段;
直接给 $guarded 赋个空数组;
那就等于说什么字段都不限制了;

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Article extends Model
{
    /**
     * 不允许赋值的字段
     *
     * @var array
     */
    protected $guarded = [];

需要注意的是不能自相矛盾;
$fillable 和 $guarded 只能定义其中的一个;

create 方法的返回值就是当前插入到数据库中的内容;
我们可以通过返回值判断成功或者失败;
而实际开发中我们经常需要返回新增数据的id;
那直接访问返回值的 id 属性即可;

    /**
     * 插入数据
     */
    public function store(Article $articleModel)
    {
        $data = [
            'category_id' => 2,
            'title' => '文章6',
            'content' => '内容6'
        ];
        $result = $articleModel->create($data);
        dump($result->id);
        $id = $articleModel->create($data)->id;
        dump($id);
    }

如果要编辑数据就要用到 update 方法了;
这个就比较简单了;
举个简单例子咱就过去了;
同样还是从表单获取的 $data 数据;
并且获取了要修改的数据的 id 为 6 ;

    /**
     * 修改数据
     */
    public function update(Article $articleModel)
    {
        $id = 6;
        $data = [
            'category_id' => 2,
            'title' => '文章6',
            'content' => '内容666'
        ];
        $result = $articleModel->where('id', $id)->update($data);
        dump($result);
    }

update 方法的返回值是被改动的数据的条数;

我们查看下插入的数据;

我们从茫茫数据中一眼就发现了第6条数据的不同;
我们之前用 DB 插入到数据库中的数据;
created_at 和 updated_at 字段都是空的;
而我们用模型插入和修改后;
created_at 和 updated_at 自动都变成了插入或者修改的时间了;
这就是使用模型的好处之一;
但是最后的这个 deleted_at 怎么是空的;
这个字段应该怎么用呢?
这就要讲下模型的删除了;
删除数据是一件很危险的行为;
而且很多时候我们希望能有一个恢复删除的功能;
比如说文章的回收站;
laravel 的模型为我们提供了很方便的软删除功能;
要启用软删除;
首先数据表需要有 deleted_at 字段;
之前讲迁移的时候已经简单的说了下;
创建迁移的时候调用 softDeletes 即可;

Schema::create('articles', function (Blueprint $table) {
    $table->increments('id');
    $table->integer('category_id')->unsigned()->default(0)->comment('分类id');
    $table->string('title')->comment('标题');
    $table->text('content')->comment('内容');
    $table->timestamps();
    $table->softDeletes();
});

模型默认是没有开启软删除功能的;
开启也很简单;
就是使用 SoftDeletes 这个 trait ;

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Article extends Model
{
    use SoftDeletes;
}

开启后创建个 delete 方法走一遍看看;
app/Http/Controllers/ModelController.php

    /**
     * 删除数据
     */
    public function delete(Article $articleModel)
    {
        $id = 6;
        $result = $articleModel->where('id', $id)->delete();
        dump($result);
    }

routes/web.php;

Route::prefix('model')->group(function () {
    Route::get('index', 'ModelController@index');
    Route::get('get', 'ModelController@get');
    Route::get('store', 'ModelController@store');
    Route::get('update', 'ModelController@update');
    Route::get('delete', 'ModelController@delete');
});

访问 /model/delete 方法;
如我们所料;
数据并没有真正被删除;
只是 deleted_at 不是 null 而是删除的时间了;

我们再访问 /model/index 方法;
发现已经查不到这条数据了;
如果想查询数据的时候带上被软删除的;
可以调用 withTrashed 方法;

Article::withTrashed()->get();

如果只查已经被删除的则是 onlyTrashed 方法;

Article::onlyTrashed()->get();

要实现回收站功能;
还需要能恢复删除的方法;
很简单调用 restore 方法即可;

$articleModel->where('id', $id)->restore();

要是想彻底删除还可以用 forceDelete ;

$articleModel->where('id', $id)->forceDelete();

至此;
增删改查我们就全过了一遍了;
过年耽误了;
终于把这篇文章完成了;

白俊遥博客
请先登录后发表评论
  • 最新评论
  • 总共7条评论
白俊遥博客

着迷.:模型调用的时候会报错呢白俊遥博客

2018-08-13 14:09:30 回复

白俊遥博客
  • LJ 回复 着迷.:你没在控制器中 use model吧
  • 2018-12-03 14:34:24 回复
白俊遥博客

YOU:function store(Article $articleModel){   $articleModel->***;}function store(){   Article::***;}这两个写法有区别吗,或者性能上的区别

2018-04-10 12:00:08 回复

白俊遥博客 白俊遥博客
  • 云淡风晴 回复 YOU:没;
  • 2018-04-22 21:33:39 回复
白俊遥博客

2014:博主,我想扒你博客的模板可以不

2018-03-11 12:51:48 回复

白俊遥博客
  • (ˇˍˇ)~ 回复 2014:博客是开源的,不用费劲了噢
  • 2018-03-11 21:02:08 回复
白俊遥博客 白俊遥博客
  • 云淡风晴 回复 2014:免费开源;不设版权;随意使用;
  • 2018-03-31 20:14:39 回复
白俊遥博客

hekaizxb:博主 这样要在控制器操作多个表,比如要操作article,user两个表,要在控制器加上use App\Models\Article;use App\Models\User;方法public function index(Article $articleModel,User $userModel){}这样写,有没有什么简化的方法

2018-03-08 15:27:41 回复

白俊遥博客

快乐远〖航〗:弱弱的问一句,就是定义复用articleList()的时候,程序是怎么知道我在查询Articles表的?

2018-03-06 21:51:50 回复

白俊遥博客
  • AesopL 回复 快乐远〖航〗:认真看文章啊,大兄弟
  • 2018-03-08 10:28:04 回复
白俊遥博客

Only Strive:来顶

2018-03-06 15:07:22 回复

白俊遥博客

cantinzing:希望博主出个收藏功能哈

2018-03-05 08:38:03 回复

白俊遥博客
  • cantinzing 回复 cantinzing:还有 这些教程文章标题可不可以是概括文章内容的标题  有时想找某个功能找大半天
  • 2018-03-05 08:39:29 回复
白俊遥博客 白俊遥博客
  • 云淡风晴 回复 cantinzing:准了;多谢反馈;
  • 2018-03-06 09:08:52 回复
白俊遥博客
  • 回复 cantinzing: 啊
  • 2018-03-27 22:03:52 回复