框架改名成 phpegg 了,求 star,顺便介绍下其内置的 DB Query Builder 的联表查询。

2017-10-09 19:42:15 +08:00
 qiujin

之前叫 my_php_framework 被吐槽,决定改名为 phpegg,域名 phpegg.com 也一并拿下了。

项目新地址 https://github.com/qiu-jin/phpegg 目前星少的可怜,求 star.

前一帖发的 readme 重点介绍了几种应用模式,忽略 DB Query Builder 的介绍。虽然数据库只是在驱动层实现不算框架的核心功能,但是数据库是使用最频繁的,而且内置的 query builder 花费了我些功夫,特别是支持多种联表查询方法,平常使用很方便。

query builder 的一些常用的 where order limit 等方法这里就不介绍了(大部分框架和库这部分都大同小异)

query builder 的常用方法使用可以看这个临时文档 https://qiu-jin.gitbooks.io/docs-phpegg-com/shu-ju-ku/lian-biao-cha-xun.html

下面重点介绍下联表查询方法

在无需使用 ORM 无需定义数据库模型的情况下,用联表查询方法就可以连表查询多表数据。

支持一对一,多对一,一对多,多对多等多种数据表关联。

并且在默认设定和联表作用域下,查询语法更加简洁易懂,也不用像一些框架或库一样需要嵌杂原生的 SQL 的来实现( Query 语法和原生 SQL 混杂在一起不伦不类,不如直接用 SQL )。

示例:查询主题 1 及其发布者与评论信息

$db->post->join('user')->on('user_id', 'id')->select('name')->with('comment')->get(1)
// 生成并执行的 SQL
SELECT `post`.*,`user`.`name` AS `user_name` FROM `post` LEFT JOIN `user` ON `post`.`user_id` = `user`.`id` WHERE `post`.`id` = '1'
SELECT * FROM `comment` WHERE `post_id` = '1'

例中关联到了 post user comment 3 个表,其中 post 作为主表,user 是 join 从表,comment 是 with 从表。

post 表与 user 表是多对一关系,一个 post 只有一个 user 发表者,一个 user 能发表多个 post 主题。

post 表与 comment 表是一对多关系,一个 post 有多条 comment 评论,一条 comment 只属于一个 post 主题。

先主表 post join user 表查询数据,表之间使用 on 方法关联字段,得到查询数据后使用 with 逻辑联表方法查询 comment 数据,表之间关联使用默认设定,然后组合数据返回。

另一个示例:查找积分大于 0 的用户,及其在 2017-10-01 后发表的主题。

$db->user->select('id', 'name')// 查找用户 id name
   ->sub('account')->where('score', '>', 0)// 查找用户积分 score 大于 0 的记录
   ->with('post')->select('id', 'title')->where('time', '>', '2017-10-01') //查找主表用户 2017-10-01 后发布主题的 id 和标题
   ->find(); //执行查询
// 生成并执行的 SQL
SELECT `id`,`name` FROM `user` WHERE `id` IN (SELECT `user_id` FROM `account` WHERE `score` > '0') 
SELECT `id`,`title`,`user_id` FROM `post` WHERE `user_id` IN ('1','2','3','5') AND `time` > '2017-10-01'

例中关联到了 user account post 3 个表,其中 user 作为主表,account 是子查询从表,post 是 with 从表。

此连表查询示例的作用域:$db->usersub('account') 之间是主表user作用域,sub('account')with('post') 之间是从表account作用域,with('post')find() 之间是从表post作用域,在作用域内whereselect order limit等方法不需要显式的指定表名,因为作用域确定了这些方法是作用于那张表,并在生成 SQL 时自动处理。

此联表查询示例用到的默认设定:在例中 user 表和 account post 表是如何关联的?在我们并没有显式的指定的情况下,会默认使用主表的 id 字段与从表中字段名为主表名+id的字段关联,此例中恰好 account post 表中都有user_id字段,所以我们可以直接使用默认设定,如不用默认设定我们也可以使用 on 方法来指定关联字段。

1 join 方法

生成原生 SQL JOIN 语句联表查询多表数据.

通常用一对一和多对一表关系场合

一对一:查询用户信息与其帐号积分信息(一个用户只有一个积分帐号表)

$db->user->join('account')->select('score')->get($user_id);
SELECT `user`.*,`account`.`score` AS `account_score` FROM `user` LEFT JOIN `account` ON `user`.`id` = `account`.`user_id` WHERE `user`.`id` = '1'

多对一:查询最近 10 个主题以及发布者信息(一个用户可以发布多个主题)

$db->post->order('id', true)->limit(10)->join('user')->on('user_id', 'id')->select('id', 'name')->find();
SELECT `post`.*,`user`.`id` AS `user_id`,`user`.`name` AS `user_name` FROM `post` LEFT JOIN `user` ON `post`.`user_id` = `user`.`id` ORDER BY `post`.`id` DESC LIMIT 10
2 with 方法

使用逻辑联表查询多表数据。

通常用于一对多和多对多表关系场合

在默认优化条件下只需要 1+1 次 SQL 查询,先查主表数据,然后根据主表数据 where in 查询从表数据,最后逻辑组合 2 表数据。

查询一个用户及其最近 10 个主题

$db->user->with('post')->order('id', true)->limit(10)->get($user_id)
SELECT * FROM `user` WHERE `id` = '1' LIMIT 1
SELECT * FROM `post` WHERE `user_id` = '1' ORDER BY `id` DESC LIMIT 10

查询主题以及回复评论第 1 页

$db->post->with('comment')->page(1)->get($post_id)
SELECT * FROM `post` WHERE `id` = '1' LIMIT 1
SELECT * FROM `comment` WHERE `post_id` = '1' LIMIT 0,30
3 relate 方法

通常用于多对多表关系场合,并且有一个关系表存储 2 个表的对应关系。

在默认优化条件下只需要 1+1+1 次 SQL 查询,先查主表数据,在查关系表数据,然后根据关系表数据查询从表数据,最后逻辑组合 3 表数据。

查询一个用户及其最近收藏书签的主题,其中 bookmark 书签表是关系表保存 user 和 post 多对多的映射关系。

$db->user->relate('post')->on('bookmark')->get($user_id);
SELECT * FROM `user` WHERE `id` = '1' LIMIT 1
SELECT `user_id`, `post_id` FROM `bookmark` WHERE `user_id` IN ('1')
SELECT * FROM `post` WHERE `id` IN ('2','3','5')
4 sub 方法

生成原生 SQL 子查询语句联表查询主表数据。

子查询通常只做为主表的过滤条件,用户不需要其本身数据。

查询最新一个主题的作者的信息。

$db->user->sub('post')->order('id', ture)->get()
SELECT * FROM `user` WHERE `id` IN (SELECT `user_id` FROM `post` ORDER BY `id` DESC)  LIMIT 1
5 union 方法

使用原生 SQL union 语法联表查询主表数据。

union 用于表结构相同的多个表。

$db->table1->union('table2')->union('table3')->find()
3402 次点击
所在节点    PHP
23 条回复
tanszhe
2017-10-15 21:30:05 +08:00
sql 有没有防注入的功能呢?
$db->user->relate('post')->on('bookmark')->get(1 or 1);

建议采用预处理的方式
tanszhe
2017-10-15 21:34:25 +08:00
with 和 relate 功能确实很好 👍
qiujin
2017-10-16 10:03:59 +08:00
@tanszhe 嗯,是用预处理处理的参数,只是 LOG 打印 SQL 时采用拼接的方式,这样看起来更直观。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/396251

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX