Eloquent ORM 概述、模型定义及基本查询详解
1、引子
在正式进入本节的之前,让我们先来看看什么是ORM。
ORM,即 Object-Relational Mapping(对象关系映射),它的作用是在关系型数据库和业务实体对象之间作一个映射,这样,我们在操作具体的 业务对象时,就不需要再去和复杂的SQL语句打交道,只需简单的操作对象的属性和方法即可。
ORM 两种最常见的实现方式是 ActiveRecord 和 DataMapper,ActiveRecord 尤其流行,在很多框架中都能看到它的身影。两者的区别主要在于 ActiveRecord 中模型与数据表一一对应,而 DataMapper 中模型与数据表是完全分离的。
Laravel 中的 Eloquent ORM 使用的也是 ActiveRecord 实现方式,每一个 Eloquent 模型类对应着数据库中的一张表,我们通过调用模型类的相应方法实现对数据库的增删改查。
2、定义模型
2.1 创建模型
我们使用Artisan命令make:model生成模型类,模型类默认位于app目录下,我们也可以在创建时指定生成目录:
php artisan make:model Models/Post
这样就会在app目录下生成一个Models目录,并且在Models目录下生成一个Post模型类。Laravel 中所有模型类继承自Illuminate\Database\Eloquent\Model类。
2.2 指定表名
如果不手动指定,默认Post对应的数据表为posts,以此类推。也可以通过设置$table属性自定义表名:
public $table = 'posts';
2.3 指定主键
Eloquent默认数据表主键为id,当然也可以通过设置$primaryKey属性来自定义主键:
public $primaryKey = 'id';
2.4 时间戳设置
默认情况下,Eloquent模型类会自动管理时间戳列create_at和update_at(如果定义迁移时设置了这两列的话),如果要取消自动管理,可以设置$timestamps属性为false:
public $timestamps = false;
还有,如果你想要设置时间戳的格式,可以使用$dateFormat属性,该属性决定了日期时间以何种格式存入数据库,以及以何种格式显示:
//设置日期时间格式为Unix时间戳
protected $dateFormat = 'U';
更多关于日期时间格式设置,请参考php官方函数date中format部分。
3、查询数据
3.1 获取多个模型
我们可以使用Eloquent模型上的all方法获取所有模型实例,比如我们通过如下方法获取所有文章:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use App\Models\Post;
class TestController extends Controller
{
/**
* Display a listing of the resource.
*
* @return Response
*/
public function index()
{
//获取多个Eloquent模型
$posts = Post::all();
dd($posts);
}
}
可见输出结果是模型数组集合,每一个$items元素对应一个Post模型实例。
此外,需要了解的是每一个Eloquent模型本身都是一个查询构建器,所有我们可以调用所有查询构建器上的方法,只不过第一个方法调用都要使用静态方法调用:
$posts = Post::where('id','<',3)->orderBy('id','desc')->take(1)->get();
dd($posts);
也许你已经注意到了,模型查询返回结果都是Illuminate\Database\Eloquent\Collection的一个实例,该类实现了ArrayAccess接口,所以我们可以像访问数组一样访问该实例,此外,该Collection类还提供了很多其它有用的方法对查询结果进行处理,详见源码。
既然Eloquent模型是查询构建器,自然也支持分组块获取数据:
Post::chunk(2,function($posts){
foreach ($posts as $post) {
echo $post->title.'<br>';
}
});
输出结果如下:
test 1
test 2
test 3
3.2 获取单个模型
可以使用查询构建器方法获取单个模型实例:
$post = Post::where('id',1)->first();
dd($post);
当然也可以通过Eloquent模型类提供的快捷方法find:
$post = Post::find(1);
如果没有找到对应的表记录,会输出null,如果我们想要捕获查询结果为空的异常并进行处理,比如跳转到404页面,可以使用findOrFail或者firstOrFail方法,如果表记录存在,两者返回获取到的第一条记录,否则抛出Illuminate\Database\Eloquent\ModelNotFoundException异常。
3.3 聚合函数查询
如果要对查询结果进行计数、统计、最大值/最小值、平均数等聚合运算,可以使用查询构建器上的对应方法,我们我们查询文章总数:
$count = Post::where('id','>',0)->count();
echo $count;
输出结果为3,又或者我们想要获取文章最大阅读数:
$views = Post::where('id','>',0)->max('views');
echo $views;
输出结果为800。
1、连接查询(join)
连接查询指的是将两张表或多张表关联到一起进行查询,获取一个表的行与另一个表的行匹配的数据。常见的连接查询包括内连接(等值连接)、左(外)连接、右(外)连接和交叉连接(完全连接)等。下面这张图形象的展示了这几种连接查询所获取的结果集:
SQL连接查询
下面我们简单演示下内连接和左连接。我们将用户表users和文章表posts关联到一起进行查询,在此之前,我们先创建posts表,其字段及初始值如下:
文章表posts
其中user_id对应users表中的用户id。
1.1 内连接
内连接用于获取两张表结果集的交集部分,我们可以使用查询构建器的join方法进行内连接查询:
$users = DB::table('users')->join('posts','users.id','=','posts.user_id')->get();
dd($users);
1.2 左连接
左连接的结果集指定的左表的所有行,如果左表的某行在右表中没有匹配行,则在相关联的结果集行中右表的所有选择列表列均为空值(null)。我们使用查询构建器的leftJoin方法进行左连接查询:
$users = DB::table('users')->leftJoin('posts','users.id','=','posts.user_id')->get();
dd($users);
对应的输出结果为:
left join 查询结果
1.3 更加复杂的连接查询
很多时候,连接查询的查询条件往往更为复杂,并不是一个简单的join/leftJoin方法就能搞定的,那么我们如何添加更加复杂的查询条件呢?使用JoinClause代替条件参数:
$users = DB::table('users')->join('posts',function($join){
$join->on('users.id','=','posts.user_id')
->where('posts.id','>',1);
})->get();
dd($users);
2、联合查询(union)
联合查询用于将两个或更多查询的结果集组合为单个结果集,该结果集包含联合查询中所有查询的全部行。UNION的结果集列名与UNION运算符中第一个Select语句的结果集的列名相同,另一个Select语句的结果集列名将被忽略,且其他查询字段数必须和第一个相同。Laravel查询构建器中我们使用union方法进行联合查询:
$users = DB::table('users')->where('id','<',3);
$users = DB::table('users')->where('id','>',2)->union($users)->get();
dd($users);
3、where子句
使用查询构建器上的where方法可以添加自定义查询条件,调用该方法需要传入三个参数:第一个列名,第二个是操作符,第三个是比较值:
$user = DB::table('users')->where('name','=','Laravel')->get();
dd($user);
如果操作符为“=”,该语句也可简化为:
$user = DB::table('users')->where('name','Laravel')->get();
需要注意的是查询构建器支持方法链,这意味着如果有多个查询条件且这个多个条件是AND连接,可以在get之前使用多个where方法。如果多个条件使用OR连接,可以使用orWhere方法:
$user = DB::table('users')->where('name','Laravel')->orWhere('name','Academy')->get();
更多where子句查询条件可查看Illuminate\Database\Query\Builder源码。
4、排序
查询构建器使用orderBy方法对查询结果进行排序:
$users = DB::table('users')->orderBy('id','desc')->get();
dd($users);
根据代码可以看到orderBy方法需要传入两个参数,第一个是排序字段,第二个是排序方向,asc代表升序,desc代表倒序,上述代码输出为:
5、分组
为了更好地演示分组,我们给数据表posts新增两个字段:cat_id和views,代表分类ID和浏览数:
posts表新增分类ID和浏览数
分组一般用于聚合查询,接下来我们使用groupBy方法对查询结果进行分组,比如我们可以统计每个分类下有几篇文章:
$posts = DB::table('posts')->select('cat_id',DB::raw('COUNT(id) as num'))->groupBy('cat_id')->get();
dd($posts);
我们还可以使用having方法为分组加上条件,比如我们可以统计总浏览数大于500的分类:
$posts = DB::table('posts')->select('cat_id',DB::raw('SUM(views) as views'))->groupBy('cat_id')->having('views','>',500)->get();
dd($posts);
输出结果为:
加条件的分组查询结果
注意:having中的条件字段必须出现在select查询字段中,否则会报错。
6、分页
查询构建器中使用skip和take对查询结果进行分页,相当于SQL语句中的limit语句:
$posts = DB::table('posts')->skip(0)->take(2)->get();
dd($posts);
获取查询构建器很简单,还是要依赖DB门面,我们使用DB门面的table方法,传入表名,即可获取该表的查询构建器:
$users = DB::table('users');
这样我们就获取到了$users表的查询构建器,实际上,底层返回的是Illuminate\Database\Query\Builder的实例,我们对查询构建器的所有操作都是调用该实例对应类上的方法。下面我们就列举查询构建器的一些常用方法,我们还是沿用上一节创建的$users表做演示说明 。
1、新增数据
使用查询构建器的insert方法即可插入一条/多条数据:
DB::table('users')->insert([
['id'=>1,'name'=>'Laravel','email'=>'laravel@test.com','password'=>'123'],
['id'=>2,'name'=>'Academy','email'=>'academy@test.com','password'=>'123'],
['id'=>3,'name'=>'LaravelAcademy','email'=>'laravel-academy@test.com','password'=>'123']
]);
执行成功后即可在数据表$users中插入三条记录。有时候,我们需要插入记录后获取自增ID,可以使用insertGetId方法:
$insertId = DB::table('users')->insertGetId(
['name'=>'Laravel-Academy','email'=>'laravelacademy@test.com','password'=>'456']
);
2、更新数据
更新表记录很简单,使用查询构建器的update方法即可:
$affected = DB::table('users')->where('name','Laravel-Academy')->update(['password'=>'123']);
该方法返回受影响的函数。
3、删除数据
使用delete方法删除表记录,删除方法和更新方法类似,返回被删除的行数:
$deleted = DB::table('users')->where('id', '>', 3)->delete();
如果我们是要删除整个数据表数据,则略去where条件,如果是要清空数据表还要将自增ID置为0,可以使用truncate方法:
DB::table('users')->truncate();
4、基本查询
4.1 获取所有表记录
使用get方法即可获取一张表的所有记录:
$users = DB::table('users')->get();
dd($users);
如果是获取指定列的数据,则需要加上select条件:
$users = DB::table('users')->select('name','email')->get();
dd($users);
4.2 获取单条记录
获取单条记录需要在查询基础上加上first方法:
$user = DB::table('users')->where('name','Laravel')->first();
dd($user);
4.3 分组块获取数据
如果数据库包含多条数据,则一次性获取会极大影响性能,对应地,我们可以调用chunk方法分组块获取数据:
DB::table('users')->chunk(2,function($users){
foreach($users as $user){
// if($user->name=='LaravelAcademy')
// return false;
echo $user->name.'<br>';
}
});
这里我们指定每次取两条记录。注释部分的意思是我们可以设定查询退出条件,当达到该条件时,查询退出,不再往下执行。
4.4 获取单列的值
上述方法获取的查询结果都是对象实例/对象实例数组,有时候,我们只是想简单获取单列的值,遍历数组获取指定列的值太麻烦,可以使用lists方法获取列值数组:
$users = DB::table('users')->lists('name');
dd($users);
这样我们就可以免去遍历对象数组的麻烦。
4.5 原生表达式
此外,查询构建器还支持原生表达式,我们需要调用DB门面的raw方法来实现:
$users = DB::table('users')->select(DB::raw('name,email'))->where('id','<',3)->get();
dd($users);
如何使用查询构建器实现对数据库的高级查询?比如join、union、添加自定义where条件、排序、分组、分页后面会有介绍。
Laravel操作mysql数据库包括了对数据库的增加,查询,删除及修改的例子了,同时Laravel多种数据库,包括MySQL、Postgres、SQLite和SQL Server了,我们下面介绍mysql数据库操作例子。1、连接数据库
Laravel中数据库配置文件为config/database.php,打开该文件,默认内容如下:
<?php
return [
//默认返回结果集为PHP对象实例
'fetch' => PDO::FETCH_CLASS,
//默认数据库连接为mysql,可以在.env文件中修改DB_CONNECTION的值
'default' => env('DB_CONNECTION', 'mysql'),
'connections' => [
//sqlite数据库相关配置
'sqlite' => [
'driver' => 'sqlite',
'database' => storage_path('database.sqlite'),
'prefix' => '',
],
//mysql数据库相关配置
'mysql' => [
'driver' => 'mysql',
'host' => env('DB_HOST', 'localhost'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => false,
],
//Postgres数据库相关配置
'pgsql' => [
'driver' => 'pgsql',
'host' => env('DB_HOST', 'localhost'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'prefix' => '',
'schema' => 'public',
],
//SQL Server数据库相关配置
'sqlsrv' => [
'driver' => 'sqlsrv',
'host' => env('DB_HOST', 'localhost'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'prefix' => '',
],
],
//迁移表名称
'migrations' => 'migrations',
//Redis数据库相关配置
'redis' => [
'cluster' => false,
'default' => [
'host' => '127.0.0.1',
'port' => 6379,
'database' => 0,
],
],
];
如果要修改数据库配置信息,去修改.env对应值即可。我们实例教程使用的Homestead开发环境默认配置,不做修改。如果你没有使用Homestead,则需要根据本地配置修改相应配置值。
我们在讲Windows上安装Homestead的时候,已经演示过数据库连接测试,以及如何在本地使用Navicat连接Homestead的数据库,这里不再赘述,下面直接进入如何使用数据库进行增删改查。
2、数据库初始化
我们在项目根目录使用Artisan命令运行Laravel自带的迁移生成users表和password_reset表:
对应在数据库中生成三张表:
3、使用DB门面进行增删改查
3.1 插入数据
我们使用DB门面执行原生SQL语句,插入操作使用DB门面的insert方法,代码如下:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use DB;
class TestController extends Controller
{
/**
* Display a listing of the resource.
*
* @return Response
*/
public function index()
{
DB::insert('insert into users (id, name, email, password) values (?, ?, ? , ? )',
[1, 'Laravel','laravel@test.com','123']);
DB::insert('insert into users (id, name, email, password) values (?, ?, ?, ? )',
[2, 'Academy','academy@test.com','123']);
}
}
在浏览器中输入http://laravel.app:8000/test,执行成功后在数据库中插入两条记录:
db-insert-data
3.2 查询语句
查询操作使用DB门面的select方法,代码如下:
$user = DB::select('select * from users where id = ?', [1]);
dd($user);
在浏览器地址栏输入http://laravel.app:8000/test,输出内容如下:
可以看到select查询返回的结果是数组。而数组中的每一个元素都是PHP对象。
我们还 可以看到在执行查询的时候使用了参数绑定,以避免SQL注入。除此之外还可以使用命名绑定:
$user = DB::select('select * from users where id = :id', [':id'=>1]);
效果一样。
3.3 更新语句
更新表记录可以使用DB门面的update方法,该方法返回受影响的行数:
$affected = DB::update('update users set name="LaravelAcademy" where name = ?', ['Academy']);
echo $affected;
打印结果为1,如果没有更新任何记录则返回0。
3.4 删除语句
删除表记录使用DB门面的delete方法,和update类似,该方法返回被删除的行数:
$deleted = DB::delete('delete from users');
echo $deleted;
打印结果为2,表数据都被我们删除了。
3.5 通用语句
除了上述这些DML(insert/update/delete)和DQL(select)语句,SQL语句还包括DCL(create/drop等)和DDL(grant等),要运行后者,我们可以调用DB门面的statement方法:
DB::statement('drop table users');
执行完该语句后,数据表users会被删除。
4、监听查询事件
除此之外,我们还可以通过DB门面的listen方法监听查询事件,比如我们在记录日志和调试的时候这会给我们确定问题提供便利,可以在服务提供者的boot方法中注册该监听器,例如我们在AppServiceProvider的boot方法中定义监听器如下:
/**
* 启动所有应用服务
*
* @return void
*/
public function boot()
{
DB::listen(function($sql, $bindings, $time) {
echo 'SQL语句执行:'.$sql.',参数:'.json_encode($bindings).',耗时:'.$time.'ms';
});
}
这样我们运行如下SQL语句:
DB::insert('insert into users (id, name, email, password) values (?, ?, ? , ? )',
[3, 'LaravelAcademy','laravel-academy@test.com','123']);
则浏览器会输出如下内容:
SQL语句执行:insert into users (id, name, email, password) values (?, ?, ? , ? ),参数:[3,"LaravelAcademy","laravel-academy@test.com","123"],耗时:1.26ms
5、数据库事务
很多时候,我们需要执行一连串操作,而其中任何一个操作出错则整个流程失败,需要回退重来,这个时候我们就要用到数据库事务。
DB门面提供两种方式支持数据库事务,一种是调用transaction方法然后传入闭包作为参数,我们将需要进行事务操作的逻辑放到闭包函数内:
DB::transaction(function () {
DB::table('users')->update(['id' => 1]);
DB::table('posts')->delete();
});
另一种是 beginTransaction、 rollBack和 commit三个方法一起使用从而构建一个完整的事务操作:
DB::beginTransaction();
if($somethingIsFailed){
DB::rollback();
return false;
}
DB::commit();
此外,使用DB门面提供的事务还支持查询构建器和Eloquent ORM数据库操作。
相关文章
- 这篇文章主要介绍了Mybatis Plus select 实现只查询部分字段的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-09-01
- 这篇文章主要给大家介绍了关于C#创建自定义控件及添加自定义属性和事件使用的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用C#具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧...2020-06-25
- 本文实例讲述了JS实现自定义简单网页软键盘效果。分享给大家供大家参考,具体如下:这是一款自定义的简单点的网页软键盘,没有使用任何控件,仅是为了练习JavaScript编写水平,安全性方面没有过多考虑,有顾虑的可以不用,目的是学...2015-11-08
MyBatisPlus-QueryWrapper多条件查询及修改方式
这篇文章主要介绍了MyBatisPlus-QueryWrapper多条件查询及修改方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2022-06-27- 这篇文章主要介绍了Oracle使用like查询时对下划线的处理方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-03-16
- 为了增强android应用的用户体验,我们可以在一些Button按钮上自定义动态的设置一些样式,比如交互时改变字体、颜色、背景图等。 今天来看一个通过重写Button来动态实...2016-09-20
- 这篇文章主要介绍了解决mybatis-plus 查询耗时慢的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-07-04
- 下面我们来看一篇关于Android自定义WebView网络视频播放控件开发例子,这个文章写得非常的不错下面给各位共享一下吧。 因为业务需要,以下代码均以Youtube网站在线视...2016-10-02
- 自定义一个jquery模态窗口插件,将它集成到现有平台框架中时,它只能在mainFrame窗口中显示,无法在顶层窗口显示. 解决这个问题的办法: 通过以下代码就可能实现在顶层窗口弹窗 复制代码 代码如下: $(window.top.documen...2014-05-31
- 这篇文章主要介绍了自定义feignClient的常见坑及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-10-20
- 今天小编就为大家分享一篇pytorch 自定义卷积核进行卷积操作方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-05-06
PHP YII框架开发小技巧之模型(models)中rules自定义验证规则
YII的models中的rules部分是一些表单的验证规则,对于表单验证十分有用,在相应的视图(views)里面添加了表单,在表单被提交之前程序都会自动先来这里面的规则里验证,只有通过对其有效的限制规则后才能被提交,可以很有效地保证...2015-11-24- 这篇文章主要介绍了jquery自定义插件开发之window的实现过程的相关资料,需要的朋友可以参考下...2016-05-09
- 如果需要在查询语句返回的列中包含一列表示该条记录在整个结果集中的行号, ISO SQL:2003 标准提出的方法是提供 ROW_NUMBER() / RANK() 函数。 Oracle 中可以使用标准方法(8i版本以上),也可以使用非标准的 ROWNUM ; MS SQL...2015-03-15
- 这篇文章主要为大家详细介绍了Node实现搜索框进行模糊查询,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-06-28
- 这篇文章主要介绍了Mybatis用注解写in查询的实现方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-07-13
PHP+Mysql+jQuery查询和列表框选择操作实例讲解
本文讲解如何通过ajax查询mysql数据,并将返回的数据显示在待选列表中,再通过选择最终将选项加入到已选区,可以用在许多后台管理系统中。本文列表框的操作依赖jquery插件。HTML <form id="sel_form" action="post.php" me...2015-10-23Element-ui 自带的两种远程搜索(模糊查询)用法讲解
这篇文章主要介绍了Element-ui 自带的两种远程搜索(模糊查询)用法讲解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-01-29- 这篇文章主要介绍了Vue 组件复用多次自定义参数操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-07-27
- 这篇文章主要介绍了Mybatis和Mybatis-Plus时间范围查询方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-08-06