原文地址:http://blog.onlywan.cc/14843810761202.html

Laravel Eloquent使用小记

今天由于开发数据库业务中间层须要。開始研究Laravel Eloquent,由于刚開始使用laravel框架的时候,都是使用query,查询构建器来写sql相似于

DB::connection('mydb')->table('mylove')
->where( 'name', 'guowan' )
->get();

复杂一点的sql使用db::raw

DB::connection('mydb')->table('mylove')->select( DB::RAW( 'count("name") as mylovecount' ) )
->where( 'name', 'guowan' )
->get();

本着在工作中学习的态度開始研究Eloquent,对着laravel中文文档。開始设计Eloquent Model。这里给出表大概字段(因兼容老系统要求。表字段设计与当前业务不相符,这里不与讨论~)

表结构

CREATE TABLE `user_ext` (
`user_id` int(10) NOT NULL,
`realname` varchar(255) DEFAULT NULL,
`gender` int(11) NOT NULL DEFAULT '0',
`birthday` datetime DEFAULT NULL,
`comefrom` varchar(255) DEFAULT NULL,
`qq` varchar(255) DEFAULT NULL,
`weibo` varchar(255) DEFAULT NULL,
`blog` varchar(255) DEFAULT NULL,
`mobile` varchar(255) DEFAULT NULL,
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 CREATE TABLE `user` (
`user_id` int(10) NOT NULL AUTO_INCREMENT,
`username` varchar(100) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
`user_img` varchar(255) DEFAULT NULL,
) ENGINE=InnoDB DEFAULT CHARSET=utf8

创建Eloqueue Model

  • user
<?php

namespace App\Http\Models\Eloquent;

use Illuminate\Database\Eloquent\Model;

class CUser extends Model
{
/**
* 与模型关联的数据表。
*
* @var string
*/
protected $table = 'user'; /*
* 数据库表主键
*
* @var string
*/
protected $primaryKey = 'user_id'; /*
* 取消自己主动维护create_at,update_at字段
*
* @var string
*/
public $timestamps = false; /*
* 获取与指定用户相关联的扩展信息记录
*/
public function hasOneExt()
{
return $this->hasOne( 'App\Http\Models\Eloquent\CUserExt', 'user_id', 'user_id' );
}
}
  • user_ext
<?php

namespace App\Http\Models\Eloquent;

use Illuminate\Database\Eloquent\Model;

class CUserExt extends Model
{ /**
* 与模型关联的数据表。
*
* @var string
*/
protected $table = 'ac_user_ext'; /*
* 数据库表主键
*
* @var string
*/
protected $primaryKey = 'user_id'; /*
* 取消自己主动维护create_at,update_at字段
*
* @var string
*/
public $timestamps = false; public function acUser()
{
return $this->belongsTo( 'App\Http\Models\Eloquent\CUser' );
}
}

user与user_ext表为1对1关系。

注意

user model中的hasOneExt方法。之所以使用hasOneExt方法名,是通过方法命名,在调用方法的时候就能够知道和userExt表的关系。

hasOne函数,第一个參数是类路径;第二个參数外键。也就是userExt表的主键;第三个參数才是user表主键。自己使用的时候,没有指定第二个和第三个參数,会出现错误

问题

以下才是今天记录的主要内容,在使用过程中,出现一些问题。以及问题对应的解决方法,可能有些问题还没有解决或者解决的不好,这里记录一下,添加一下印象,也能够和其它同学一块讨论一下

1. 依赖方法hasOneExt

调用以下方法

$oUser = CUser::find( $sUMId )->hasOneExt();

结果居然返回UserExt表中数据。

我的本意本来想做对应的关联查询。查出两个表的数据。然后在网上各种搜索Eloquent两表联查。返回两表字段。

最终解决方式例如以下:

$oUser = CAcUser::with( 'hasOneExt' )->find( $sUMId );

查询结果:

Array
(
[user_id] => 1
[username] => admin
[email] => wanguowan521@163.com
[user_img] => 201303/26132122j2lg.jpg
[has_one_ext] => Array
(
[user_id] => 1
[realname] => 瞌睡
[gender] => 1
[birthday] =>
[comefrom] => **,不限
[qq] =>
[weibo] =>
[blog] =>
[mobile] =>
) )

这里依赖表数据用法名作为key成为返回结果的一部分,这个对于业务接口,须要一维数组的时候还得须要翻译。

幸好对于业务层来说。希望屏蔽底层数据层字段细节。本来就须要做一次翻译。所以这里也就不是什么大问题。

这里with语法。是Eloquent中所谓的预载入语法,主要是为了解决ORM(Object Relation Mapping) n+1次查询问题–具体说明。在网上查询过程中,这里尽管是1对1关系,可是假设这样解决,会将一次join查询,变成两次查询。对于将来高并发场景来说,有点不能接受。

可是不这样解决又没有找到其它解决方法。

无奈,尝试打印Eloquent运行的sql,查看具体的sql语句(打印laravel运行sql方法比較多,能够參考资料),代码例如以下:

DB::enableQueryLog();

$oUser = CUser::with( 'hasOneExt' )->find( $sUMId );

print_r(
DB::getQueryLog()
);

打印结果例如以下:

Array
(
[0] => Array
(
[query] => select * from `user` where `user`.`user_id` = ? limit 1
[bindings] => Array
(
[0] => 1
) [time] => 0.56
) [1] => Array
(
[query] => select * from `user_ext` where `user_ext`.`user_id` in (?)
[bindings] => Array
(
[0] => 1
) [time] => 0.32
) )

能够看出。sql先依据user_id查询到主标数据,然后在去依赖表中做in查询,这样确实攻克了ORM n+1次查询的问题,可是对于直接使用sql,还是多出一次查询。

这里发现一个比較有趣的事情。log里有一个time值,难道这个是sql运行时间。假设这个是运行时间的话。那就能够简单的验证一下sql运行效率问题了,然后開始查询资料。最终在源代码中找到了答案,源代码例如以下:具体链接

    /**
* Run a SQL statement and log its execution context.
*
* @param string $query
* @param array $bindings
* @param Closure $callback
* @return mixed
*
* @throws QueryException
*/
protected function run($query, $bindings, Closure $callback)
{
$start = microtime(true);
// To execute the statement, we'll simply call the callback, which will actually
// run the SQL against the PDO connection. Then we can calculate the time it
// took to execute and log the query SQL, bindings and time in our memory.
try
{
$result = $callback($this, $query, $bindings);
}
// If an exception occurs when attempting to run a query, we'll format the error
// message to include the bindings with SQL, which will make this exception a
// lot more helpful to the developer instead of just the database's errors.
catch (\Exception $e)
{
throw new QueryException($query, $bindings, $e);
}
// Once we have run the query we will calculate the time that it took to run and
// then log the query, bindings, and execution time so we will report them on
// the event that the developer needs them. We'll log time in milliseconds.
$time = $this->getElapsedTime($start);
$this->logQuery($query, $bindings, $time);
return $result;
} /**
* Get the elapsed time since a given starting point.
*
* @param int $start
* @return float
*/
protected function getElapsedTime($start)
{
return round((microtime(true) - $start) * 1000, 2);
}

这里能够看出time就是sql运行时间,并且单位是毫秒.

这里就能够測试单条join和使用eloquent with查询效率对照,代码例如以下:

DB::enableQueryLog();

DB::table(  'user' )
->leftJoin( 'user_ext as ext', 'user.user_id', '=', 'ext.user_id' )
->where( 'user.user_id', 1 )
->get(); $oUser = CUser::with( 'hasOneExt' )->find( $sUMId ); print_r(
DB::getQueryLog()
);

结果例如以下:

Array
(
[0] => Array
(
[query] => select * from `user` as `user` left join `user_ext` as `ext` on `user`.`user_id` = `ext`.`user_id` where `user`.`user_id` = ?
[bindings] => Array
(
[0] => 1
) [time] => 0.65
) [1] => Array
(
[query] => select * from `user` where `user`.`user_id` = ? limit 1
[bindings] => Array
(
[0] => 1
) [time] => 0.35
) [2] => Array
(
[query] => select * from `user_ext` where `user_ext`.`user_id` in (?)
[bindings] => Array
(
[0] => 1
) [time] => 0.35
) )

从结果能够看出,运行一条时间相比运行两条时间,差距不是非常大,可是客观来说,这说明不了什么问题;首先,測试基于本地数据库,一次请求和两次请求的网络影响及延迟会比线上差距要小非常多;其次。本地測试数据库。两个表数据量都在1k。数据量太小,无法反应真实线上数据查询效率。所以这里查询结果仅供參考,后期具体结果,会在本地伪造100w左右数据量进行測试观察。并咨询公司dba。对于大数据量对连表查询效率影响情况。

总结

对于今天解决这个问题的过程,尽管感觉没有得到完美的答案,可是在查询过程中也学习到不少东西。在这里做一下记录。

以备后期温故学习。

这里记录一下几个小细节:

数据库查询过程中。为了节省应用server与数据库server之间网络流量及数据库server的IO,数据库查询原则是仅仅查询返回实用字段,对于没用的大字段。特别是text等,不须要时。尽量不查询。

数据库查询尽量不要使用selec *

Eloquent 联合查询指定字段

  • 方法1
$oUser = CUser::with( [ 'hasOneExt' => function( $query ) {
$query->select( 'user_id', 'realname', 'gender', 'birthday' );
} ] )->find( $sUMId, [ 'user_id', 'username', 'email', 'user_img' ] );

当中query−>select(′userid′,′realname′,′gender′,′birthday′)为查询扩展表字段。find(sUMId, [ ‘user_id’, ‘username’, ‘email’, ‘user_img’ ] )为查询主表字段

  • 方法2
public function hasOneExt() {
return $this->hasOne( 'App\Http\Models\Eloquent\CUserExt', 'user_id', 'user_id' )
->select( 'user_id', 'realname', 'gender', 'birthday' );
} $oUser = CUser::with( 'hasOneExt' )->find( $sUMId, [ 'user_id', 'username', 'email', 'user_img' ] );

运行sql结果:

Array
(
[0] => Array
(
[query] => select `user_id`, `username`, `email`, `user_img` from `user` where `user`.`user_id` = ? limit 1
[bindings] => Array
(
[0] => 1
) [time] => 0.5
) [1] => Array
(
[query] => select `user_id`, `realname`, `gender`, `birthday` from `user_ext` where `user_ext`.`user_id` in (?)
[bindings] => Array
(
[0] => 1
) [time] => 0.33
) )

欢迎加入公众号:


Laravel Eloquent使用小记的更多相关文章

  1. 判断Laravel Eloquent获取数据结果集是否为空

    在使用Laravel Eloquent模型时,我们可能要判断取出的结果集是否为空,但我们发现直接使用is_null或empty是无法判段它结果集是否为空的. var_dump之后我们很容易发现,即使取 ...

  2. 深入理解 Laravel Eloquent(三)——模型间关系(关联)

    Eloquent是什么 Eloquent 是一个 ORM,全称为 Object Relational Mapping,翻译为 "对象关系映射"(如果只把它当成 Database A ...

  3. Laravel Eloquent 判断取出的结果集是否为空

    在使用Laravel Eloquent模型时,我们可能要判断取出的结果集是否为空,但我们发现直接使用is_null或empty是无法判段它结果集是否为空的. var_dump之后我们很容易发现,即使取 ...

  4. Laravel Eloquent 自定义返回字段

    返回指定字段 Book::select("price", "name")->all(); 返回关系字段关联的属性 Book::select("p ...

  5. laravel Eloquent 查询数据库判断获取的内容是否为空

    原文地址:https://www.cnblogs.com/love-snow/articles/7205338.html 在使用 Laravel Eloquent 模型时,我们要判断取出的结果集是否为 ...

  6. Laravel Eloquent ORM

    Eloquent ORM 简介 基本用法 集体赋值 插入.更新.删除 软删除 时间戳 查询范围 关系 查询关系 预先加载 插入相关模型 触发父模型时间戳 与数据透视表工作 集合 访问器和调整器 日期调 ...

  7. Laravel Eloquent get获取空的数据问题

    在用laravel框架来获取数据,若数据不存在时,以为会返回空,其实不是的,其实是一个 collection 值,会返回如下: object(Illuminate\Database\Eloquent\ ...

  8. 20 个 Laravel Eloquent 必备的实用技巧

    Eloquent ORM 看起来是一个简单的机制,但是在底层,有很多半隐藏的函数和鲜为人知的方式来实现更多功能.在这篇文章中,我将演示几个小技巧. 1. 递增和递减 要代替以下实现: $article ...

  9. Laravel Eloquent ORM 时如何查询表中指定的字段

    导读:在使用Laravel ORM的Model方法find, get, first方法获取数据对象时返回的数据对象的attributes属性数组里会包含数据表中所有的字段对应...原文地址:http: ...

随机推荐

  1. php mysql procedure获取多个结果集

    protected function getRs($id) { $db = new mysqli(C("DB_HOST"), C("DB_USER"), C(& ...

  2. Installation error: INSTALL_PARSE_FAILED_MANIFEST_MALFORMED

    一般为AndroidManifest.xml中的activity的问题 这次主要是因为activity在另外一个package里面,但是package名称第一次字母是大写,所以出错 把package名 ...

  3. MySQL -- Fast Index Creation

    1.fast index creation简介 MySQL5.5之后,对innodb表创建或删除辅助索引的效率提升了很多,即增加了新的功能fast index creation.因为MySQL5.5之 ...

  4. Set up development environment for apps for SharePoint 2013

    SharePoint 2013 support app development pattern.An app for SharePoint is small and isolate applicati ...

  5. ldconfig命令

    ldconfig是一个动态链接库管理命令 为了让动态链接库为系统所共享,还需运行动态链接库的管理命令--ldconfigldconfig 命令的用途,主要是在默认搜寻目录(/lib和/usr/lib) ...

  6. js 什么是深拷贝问题?

    一.什么是值类型? 二.什么是引用类型? 三.使用ES Next新特性带来的 Object.assign 方法 和 扩展运算符: 四.Object.assign 方法 和 扩展运算符的 “深入浅出” ...

  7. 常用的代码之一:用StopWatch计算代码运行花费的时间。

    先引用Diagnostics using System.Diagnostics; 然后: Stopwatch stopWatch = new Stopwatch(); stopWatch.Start( ...

  8. mysql 中 delete 子查询的限制

    1 DELETE FROM tablename 中的 tablename 不能起别名 delete ; [Err] - You have an error in your SQL syntax; 2 ...

  9. 怎么安装预装的win8三星笔记本改win7再装Ubuntu问题[zz]

    随着科技的高速发展,人们对电脑的要求越来越高,对电脑系统的要求亦是,那些电脑自带的系统,很多时候已经无法满足人们的需求了,而为了满足自己的需求,人们往往会为电脑改装新系统,而本文要和大家一起分享的话题 ...

  10. C/C++/Objective-C经典书籍推荐

    C语言要从大而全,从基础開始.它属于最好的.别被它误导.它也有非常多错误,不适合标准软件开发人员使用.变量声明,定义,编程规范全然不合规范,可是从语言学习方面做到极致,有大量不同的样例和试题.标准的教 ...