PHP数据库表操作的封装类及用法实例详解

本文实例讲述了PHP数据库表操作的封装类及用法。分享给大家供大家参考,具体如下:

数据库表结构:

设置字符编码:

引入Table类:

设置数据库参数:

创建实体对象:

Table类有三个参数: $table,$pk,$pdo=null

$table: 表名称. $pk: 主键名称. 不支持联合主键 $pdo: 独立的PDO对象. 一般不需要传 Notice: Table类是表操作的封装,不是Model层的基类,所以不支持表前缀,表前缀应该在Model层实现

插入数据:

"admin1",'password' => "admin1",'nickname' => "管理员1",'r' => mt_rand(0,5),); echo $userTable->insert($user)->rowCount(),"\n"; echo $userTable->lastInsertId(),"\n";

批量插入数据:

batchInsert($fields,$rows);

查询所有数据:

select方法返回一个PDOStatement对象,fetchAll返回多行,fetch返回单行

select()->fetchAll());

field自定义:

select('id,nickname')->fetchAll());

where查询:

where('id > ?',50)->select()->fetchAll());

where and条件:

where('id > ?',6)->where('id in (?)',array(5,7,9)) ->select()->fetchAll());

where or条件:

where('id = ? OR id = ?',6,8)->select()->fetchAll());

group分组 having过滤:

group('r')->having('c between ? and ?',10,20) ->select('*,r,count(*) as c')->fetchAll());

order排序:

order('r desc,id')->select()->fetchAll());

limit 行数:

跳过30行 返回10行

limitOffset(10,30)->select()->fetchAll());

查询单行:

where('id = ?',6)->select()->fetch());

根据主键查询数据:

find(4));

update更新数据:

'admin4-1','nickname' => '管理员4-1',); echo $userTable->where('id = ?',4)->update($user)->rowCount(),"\n";

replace替换数据:

使用了MySQL的REPLACE语句

4,'username' => 'admin4','password' => 'admin4','nickname' => '管理员4',); echo $userTable->replace($user)->rowCount(),"\n";

删除数据:

where('id = ?',4)->delete()->rowCount(),"\n";

分页查询

第2页,每页10行数据:

page(2,10)->select()->fetchAll());

分页查询的总行数:

where('r=?',3)->order('id desc')->page(2,10) ->select()->fetchAll(); echo $userTable->count(),"\n";

复杂查询:

where('id > ?',1)->where('id < ?',100) ->group('r')->having('c between ? and ?',1,100)->having('c > ?',1) ->order('c desc')->page(2,3)->select('*,count(*) as c')->fetchAll());

自增:

where('id = ?',$id)->plus('r')->find($id)); // 减一 var_dump($userTable->where('id = ?',$id)->plus('r',-1)->find($id)); // 多列 var_dump($userTable->where('id = ?','r',-1)->find($id));

自增,并获得自增后的值:

where('id = ?',$id)->incr('r'),"\n"; // 减一 echo $userTable->where('id = ?',$id)->incr('r',-1),"\n";

save 保存修改:

判断数据中是否存在主键字段,如果存在主键字段就update数据,反之insert数据

3,'nickname' => '管理员3-3',); echo $userTable->save($user)->rowCount(),"\n"; var_dump($userTable->find(3)); // 添加 $user = array( 'username' => 'admin11','password' => 'admin11','nickname' => '管理员11',"\n"; $id = $userTable->lastInsertId(); var_dump($userTable->find($id));

生成外表测试数据:

select('id')->fetchAll(); $id = 0; foreach ($users as $user) { for ($i=0; $i<10; $i++) { $id++; $blog = array( 'user_id' => $user['id'],'title' => "blog$id",); $blogTable->insert($blog); } }

Table类不支持JOIN查询

需要的朋友可以手写sql语句,使用query方法来执行.或者自己修改Table类来支持JOIN

获取外表数据:

where('id in (?)',array(1,12,23,34,56,67,78,89,90,101)) ->select()->fetchAll(); // 获取外表数据 key为外表id value为外表行数据 var_dump($userTable->foreignKey($blogs,'user_id') ->fetchAll(PDO::FETCH_UNIQUE)); var_dump($userTable->foreignKey($blogs,'user_id','*,id') ->fetchAll(PDO::FETCH_UNIQUE)); var_dump($userTable->foreignKey($blogs,'id,username,nickanem,id') ->fetchAll(PDO::FETCH_UNIQUE)); // 获取外表数据 返回键值对数组 key为id value为username var_dump($userTable->foreignKey($blogs,username') ->fetchAll(PDO::FETCH_KEY_PAIR));

PDOStatement::fetchAll 示例:

select('*,id')->fetchAll(PDO::FETCH_UNIQUE)); // 获取数组 var_dump($userTable->select('nickname')->fetchAll(PDO::FETCH_COLUMN)); // 获取键值对 var_dump($userTable->select('id,nickname')->fetchAll(PDO::FETCH_KEY_PAIR)); // 获取数据分组 var_dump($userTable->select('r,id,nickname')->fetchAll(PDO::FETCH_GROUP)); // 获取数据分组 var_dump($userTable->select('r,id')->fetchAll(PDO::FETCH_GROUP|PDO::FETCH_COLUMN)); // 获取数据分组 var_dump($userTable->select('r,nickname')->fetchAll(PDO::FETCH_GROUP|PDO::FETCH_KEY_PAIR)); // 获取对象 指定获取方式,将结果集中的每一行作为一个属性名对应列名的对象返回。 var_dump($userTable->select()->fetchAll(PDO::FETCH_OBJ)); // 获取对象 指定获取方式,返回一个所请求类的新实例,映射列到类中对应的属性名。 // Note: 如果所请求的类中不存在该属性,则调用 __set() 魔术方法 var_dump($userTable->select()->fetchAll(PDO::FETCH_CLASS)); // 获取对象 指定获取方式,更新一个请求类的现有实例,映射列到类中对应的属性名。 var_dump($userTable->select()->fetchAll(PDO::FETCH_INTO)); // 获取自定义行 var_dump($userTable->select()->fetchAll(PDO::FETCH_FUNC,function($id,$username,$password,$r){ return array('id'=>$id,'name'=>"$username - $password - $r"); })); // 获取单一值 var_dump($userTable->select()->fetchAll(PDO::FETCH_FUNC,$r){ return "$id - $username - $password - $r"; }));

Table类源代码:

*/ /** * 模型 */ class Table { /** * @var PDO */ public static $__pdo = null; // 默认PDO对象 public static $__host = '127.0.0.1'; // 默认主机 public static $__user = 'root'; // 默认账户 public static $__pass = '123456'; // 默认密码 public static $__name = 'test'; // 默认数据库名称 public static $__charset = 'utf8'; // 默认字符集 /** * @var PDO */ public $_pdo = null; // PDO对象 public $_table = null; // 表名 public $_pk = 'id'; // paramry public $_where = array(); // where public $_where_params = array(); // where params public $_count_where = array(); // count where public $_count_where_params = array(); // count where params public $_group = ''; // group public $_having = array(); // having public $_having_params = array(); // having params public $_order = null; // order public $_limit = null; // limit public $_offset = null; // offset public $_for_update = ''; // read lock public $_lock_in_share_model = ''; // write lock /** * Table Construct * @param string $table_name * @param string $pk * @param string $prefix * @param PDO $pdo */ function __construct($table=null,$pk=null,PDO $pdo=null) { $this->_table = isset($table) ? $table : $this->_table; $this->_pk = isset($pk) ? $pk : $this->_pk; $this->_pdo = $pdo; } /** * @return PDO */ public function getPDO() { if (isset($this->_pdo)) { return $this->_pdo; } if (isset(self::$__pdo)) { return self::$__pdo; } $dsn = sprintf("mysql:host=%s;dbname=%s;charset=%s;",self::$__host,self::$__name,self::$__charset); $options = array( PDO::ATTR_PERSISTENT => true,PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC ); return self::$__pdo = new PDO($dsn,self::$__user,self::$__pass,$options); } /** * 执行语句 * @param string $sql * @return PDOStatement */ public function query($sql) { $params = func_get_args(); array_shift($params); return $this->queryParams($sql,$params); } /** * 执行语句 * @param string $sql * @return PDOStatement */ public function queryParams($sql,array $params) { $sqls = explode('?',$sql); $sql_new = array_shift($sqls); $params_new = array(); foreach ($sqls as $i => $sql_item) { if (is_array($params[$i])) { $sql_new .= str_repeat('?,',count($params[$i])-1).'?'.$sql_item; $params_new = array_merge($params_new,$params[$i]); } else { $sql_new .= '?'.$sql_item; $params_new[] = $params[$i]; } } $stmt = $this->getPDO()->prepare($sql_new); foreach ($params_new as $i => $param) { switch (gettype($param)) { case 'integer': $stmt->bindValue($i+1,$param,PDO::PARAM_INT); break; case 'NULL': $stmt->bindValue($i+1,PDO::PARAM_NULL); break; default : $stmt->bindValue($i+1,$param); } } // echo $sql_new,"\n"; var_dump($params_new); // exit(); $stmt->executeResult = $stmt->execute(); $this->reset(); return $stmt; } /** * 查询数据 * @param string $field * @return PDOStatement */ public function select($columns='*') { $params = array_merge($this->_where_params,$this->_having_params); $sql = "SELECT $columns FROM `{$this->_table}`"; $sql .= empty($this->_where) ? '' : ' WHERE '. implode(' AND ',$this->_where); $sql .= empty($this->_group) ? '' : ' GROUP BY '. $this->_group; $sql .= empty($this->_having) ? '' : ' HAVING '. implode(' AND ',$this->_having); $sql .= empty($this->_order) ? '' : ' ORDER BY '. $this->_order; if (isset($this->_limit)) { $sql .= ' LIMIT ?'; $params[] = $this->_limit; if (isset($this->_offset)) { $sql .= ' OFFSET ?'; $params[] = $this->_offset; } } $sql .= $this->_for_update; $sql .= $this->_lock_in_share_model; $this->_count_where = $this->_where; $this->_count_where_params = $this->_where_params; return $this->queryParams($sql,$params); } /** * 添加数据 * @param array $data * @return PDOStatement */ public function insert(array $data) { $sql = "INSERT `{$this->_table}` SET"; $params = array(); foreach ($data as $col=>$val) { $sql .= " `$col` = ?,"; $params[] = $val; } $sql{strlen($sql)-1} = ' '; return $this->queryParams($sql,$params); } /** * 批量插入数据 * @param array $names * @param array $rows * @param number $batch * @return Table */ public function batchInsert(array $fields,array $rows,$batch=1000) { $i = 0; $sql = "INSERT `{$this->_table}` (`".implode('`,`',$fields)."`) VALUES "; foreach ($rows as $row) { $i++; $sql .= "('".implode("','",array_map('addslashes',$row))."'),"; if ($i >= $batch) { $sql{strlen($sql)-1} = ' '; $this->query($sql); $i = 0; $sql = "INSERT `{$this->_table}` (`".implode('`,$fields)."`) VALUES "; } } if ($i > 0) { $sql{strlen($sql)-1} = ' '; $this->query($sql); } return $this; } /** * 更新数据 * @param array $data * @return PDOStatement */ public function update(array $data) { $sql = "UPDATE `{$this->_table}` SET"; $params = array(); foreach ($data as $col=>$val) { $sql .= " `$col` = ?,"; $params[] = $val; } $sql{strlen($sql)-1} = ' '; $sql .= empty($this->_where) ? '' : 'WHERE '. implode(' AND ',$this->_where); $params = array_merge($params,$this->_where_params); return $this->queryParams($sql,$params); } /** * 替换数据 * @param array $data * @return PDOStatement */ public function replace(array $data) { $sql = "REPLACE `{$this->_table}` SET"; $params = array(); foreach ($data as $col=>$val) { $sql .= " `$col` = ?,$params); } /** * 删除数据 * @return PDOStatement */ public function delete() { $sql = "DELETE FROM `{$this->_table}`"; $sql .= empty($this->_where) ? '' : ' WHERE '. implode(' AND ',$this->_where); return $this->queryParams($sql,$this->_where_params); } /** * 重置所有 * @return Table */ public function reset() { $this->_where = array(); $this->_where_params = array(); $this->_group = null; $this->_having = array(); $this->_having_params = array(); $this->_order = null; $this->_limit = null; $this->_offset = null; $this->_for_update = ''; $this->_lock_in_share_model = ''; return $this; } /** * where查询条件 * @param string $format * @return Table */ public function where($format) { $args = func_get_args(); array_shift($args); $this->_where[] = $format; $this->_where_params = array_merge($this->_where_params,$args); return $this; } /** * group分组 * @param string $columns * @return Table */ public function group($columns) { $this->_group = $columns; return $this; } /** * having过滤条件 * @param string $format * @return Table */ public function having($format) { $args = func_get_args(); array_shift($args); $this->_having[] = $format; $this->_having_params = array_merge($this->_having_params,$args); return $this; } /** * order排序 * @param string $columns * @return Table */ public function order($order) { $this->_order = $order; return $this; } /** * limit数据偏移 * @param number $offset * @param number $limit * @return Table */ public function limitOffset($limit,$offset=null) { $this->_limit = $limit; $this->_offset = $offset; return $this; } /** * 独占锁,不可读不可写 * @return Table */ public function forUpdate() { $this->forUpdate = ' FOR UPDATE'; return $this; } /** * 共享锁,可读不可写 * @return Table */ public function lockInShareMode() { $this->_lock_in_share_model = ' LOCK IN SHARE MODE'; return $this; } /** * 事务开始 * @return bool */ public function begin() { return $this->getPDO()->beginTransaction(); } /** * 事务提交 * @return bool */ public function commit() { return $this->getPDO()->commit(); } /** * 事务回滚 * @return bool */ public function rollBack() { return $this->getPDO()->rollBack(); } /** * page分页 * @param number $page * @param number $pagesize * @return Table */ public function page($page,$pagesize = 15) { $this->_limit = $pagesize; $this->_offset = ($page - 1) * $pagesize; return $this; } /** * 获取自增ID * @return int */ public function lastInsertId() { return $this->getPDO()->lastInsertId(); } /** * 获取符合条件的行数 * @return int */ public function count() { $sql = "SELECT count(*) FROM `{$this->_table}`"; $sql .= empty($this->_count_where) ? '' : ' WHERE '. implode(' AND ',$this->_count_where); return $this->queryParams($sql,$this->_count_where_params)->fetchColumn(); } /** * 将选中行的指定字段加一 * @param string $col * @param number $val * @return Table */ public function plus($col,$val = 1) { $sets = array("`$col` = `$col` + $val"); $args = array_slice(func_get_args(),2); while (count($args) > 1) { $col = array_shift($args); $val = array_shift($args); $sets[] = "`$col` = `$col` + $val"; } $sql = "UPDATE `{$this->_table}` SET ".implode(',$sets); $sql .= empty($this->_where) ? '' : ' WHERE '. implode(' AND ',$this->_where); $params = array_merge(array($val),$this->_where_params); $this->queryParams($sql,$params); return $this; } /** * 将选中行的指定字段加一 * @param string $col * @param number $val * @return int */ public function incr($col,$val = 1) { $sql = "UPDATE `{$this->_table}` SET `$col` = last_insert_id(`$col` + ?)"; $sql .= empty($this->_where) ? '' : ' WHERE '. implode(' AND ',$params); return $this->getPDO()->lastInsertId(); } /** * 根据主键查找行 * @param number $id * @return array */ public function find($id) { return $this->where("`{$this->_pk}` = ?",$id)->select()->fetch(); } /** * 保存数据,自动判断是新增还是更新 * @param array $data * @return PDOStatement */ public function save(array $data) { if (array_key_exists($this->_pk,$data)) { $pk_val = $data[$this->_pk]; unset($data[$this->_pk]); return $this->where("`{$this->_pk}` = ?",$pk_val)->update($data); } else { return $this->insert($data); } } /** * 获取外键数据 * @param array $rows * @param string $fkey * @param string $field * @param string $key * @return PDOStatement */ public function foreignKey(array $rows,$fkey,$field='*') { $ids = array(); foreach($rows as $row) { $ids[] = $row[$fkey]; } // $ids = array_column($rows,$fkey); if (empty($ids)) { return new PDOStatement(); } return $this->where("`{$this->_pk}` in (?)",$ids)->select($field); } }

github地址:

https://github.com/dotcoo/php/blob/master/Table/Table.php

更多关于PHP相关内容感兴趣的读者可查看本站专题:《》、《》、《》、《》、《》及《

希望本文所述对大家PHP程序设计有所帮助。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


文章浏览阅读8.4k次,点赞8次,收藏7次。SourceCodester Online Tours & Travels Management System pay.php sql injectionLine 16 of pay.php invokes a SQL query built using unvalidated input. This call could allow an attacker to modify the statement’s meaning or to execute arbitrary SQL commands.SQL
文章浏览阅读3.4k次,点赞46次,收藏51次。本文为大家介绍在windwos系统搭建typecho博客+cpolar内网穿透工具将博客发布到公共网络环境,实现远程也可以访问和操作。_windows搭建typecho
文章浏览阅读1.1k次。- php是最优秀, 最原生的模板语言, 替代语法,让php更加的优雅的与html生活在一起 -->请放心, 最终生成的,或者说用户最终看到的,仍然是一个html文档, php代码中的内容不会被泄漏的。-- 将php与html代码混编的时候,大括号很容易造成配对错误,最好杜绝它 -->php标签内部代码由php.exe解释, php标签之外的代码原样输出,仍由web服务器解析。-- 所以php的流程控制语句, 都提供了替代语法,用冒号代替大括号 -->php echo '百变鹏仔'?_利用php将静态页面修改为动态页面
文章浏览阅读1.1k次,点赞18次,收藏15次。整理K8s网络相关笔记博文内容涉及 Linux network namespace 认知以及彼此通信Demo,实际中的应用理解不足小伙伴帮忙指正不必太纠结于当下,也不必太忧虑未来,当你经历过一些事情的时候,眼前的风景已经和从前不一样了。——村上春树。_linux network namespace 多端通信 模式认知
文章浏览阅读1.2k次,点赞22次,收藏19次。此网络模型提供了一个逻辑二层(L2)网络,该网络封装在跨 Kubernetes 集群节点的现有三层(L3)网络拓扑上。使用此模型,可以为容器提供一个隔离的 L2 网络,而无需分发路由。封装网络带来了少量的处理开销以及由于覆盖封装生成 IP header 造成的 IP 包大小增加。封装信息由 Kubernetes worker 之间的 UDP 端口分发,交换如何访问 MAC 地址的网络控制平面信息。此类网络模型中常用的封装是 VXLAN、Internet 协议安全性 (IPSec) 和 IP-in-IP。_k8s网络组件对比
文章浏览阅读1.1k次,点赞14次,收藏19次。当我们谈论网络安全时,我们正在讨论的是保护我们的在线空间,这是我们所有人的共享责任。网络安全涉及保护我们的信息,防止被未经授权的人访问、披露、破坏或修改。
文章浏览阅读1.3w次,点赞3次,收藏7次。尽管您可以通过 ping 命令解析出网站的 IP 地址,但是可能在浏览器中访问时仍然遇到问题,这可能是因为浏览器使用的 DNS 解析结果不同于 ping 命令使用的解析结果。可能是因为您的网络或设备上设置了防火墙,阻止了对特定网站的访问。有些国家或组织可能会对特定的域名进行屏蔽,从而阻止访问相关网站。如果您的网络使用代理服务器进行访问控制,可能会由于代理服务器的配置问题导致无法访问某些网站。即使您的网络和设备一切正常,目标网站本身可能也存在问题,例如服务器故障、维护或过载,导致无法访问。_能ping通打不开网页
文章浏览阅读839次,点赞22次,收藏19次。本系统带文档lw万字以上文末可领取本课题的JAVA源码参考。
文章浏览阅读2.1k次,点赞31次,收藏22次。基于微信小程序奶茶点餐外卖系统设计与实现(PHP后台+Mysql)可行性分析毕设源代码毕业设计,数据安全和系统稳定性以及团队能力和资源配备方面都具备较好的条件。因此,该项目的可行性较高。:黄菊华老师《Vue.js入门与商城开发实战》《微信小程序商城开发》图书作者,CSDN博客专家,在线教育专家,CSDN钻石讲师;微信小程序作为一种快捷、方便的移动应用形式,成为很多用户点餐外卖的首选。项目的界面和功能都可以定制,包安装运行!项目配有对应开发文档、开题报告、任务书、PPT、论文模版等。
文章浏览阅读1.8k次,点赞52次,收藏38次。本文主要通过对系统的前台系统和后台管理系统进行了功能性需求分析,对系统的安全性和可扩展性进行了非功能性需求分析。在详细的需求分析的基础上,根据系统的功能设计确定了数据库结构,实现完整的代码编写。Lucky+Baby母婴用品网站使用 Dreamweaver、HBuilder代码编辑器、Apache服务器等开发工具,完成了系统的主要模块的页面设计和功能实现。本文展示了首页页面的实现效果图,并通过代码和页面介绍了用户注册功能、商品搜索功能、生成订单和查看我的订单功能、在线付款功能功能的实现过程。
文章浏览阅读1.5k次,点赞45次,收藏40次。本设计主要实现集人性化、高效率、便捷等优点于一身的人事信息管理系统,完成首页、系统用户、通知公告、部门信息、员工薪资、考勤签到、员工请假、招聘信息、应聘信息等功能模块。
文章浏览阅读1k次。该错误通常出现在数据库读取结果集数据时,比如当我们写好SQL语句从数据库读取数据时,本身应该返回结果集,再给结果集中读取数据。解决思路:这种错误一般是因为echo后面输出了一个数组导致的,或者是数组作为字符串进行拼接运算时导致的。该错误直译为:警告:mysqli_fetch_assoc函数期望参数1是mysqli的结果集,但是给了一个布尔值。这种错误是PHP解析器在解析时遇到了语法错误,直译为:解析错误:语法错误,意料之外的...该错误直译为:提示:未定义的索引:username。_array to string conversion in
文章浏览阅读2.7w次。解决http请求报错context deadline exceeded (Client.Timeout exceeded while awaiting headers)_context deadline exceeded (client.timeout exceeded while awaiting headers)
文章浏览阅读1.3k次,点赞26次,收藏24次。复杂网络是一种由大量相互连接的元素(节点或顶点)组成的网络结构,这些连接通常是非常复杂和动态的。这些网络可以在各种领域中发现,包括社交网络、生物学系统、信息技术和交通系统等。_代理建模
文章浏览阅读2.6k次,点赞76次,收藏71次。epoll详解,事件模型,ET/LT模式,并通过三个示例进行代码实现。
文章浏览阅读3.3k次。罗拉ROLA-IP是一家来自纽约的代理IP提供商,由李嘉诚先生投资建设,韩国人工智能、自动驾驶、虚拟现实方面的领军企业World IT Show投资入股,由美国纽约大学IT管理教授团队研究开发,进入中国市场6年多,全世界设有多个分子公司。接下来,我们要检查代理和防火墙的设置,因为在绝大多数情况下,它们是导致这个错误的原因,尤其是当用户使用免费代理时。对网站的访问受阻实际上是一个非常常见的错误,它既可能是由于物理原因(硬件问题)造成的,也可能是由于软件错误引起的。检查代理设置,并确保其正确配置。_无法访问此网站,检查代理服务器和防火墙
文章浏览阅读1.1k次,点赞14次,收藏20次。本系统带文档lw万字以上文末可领取本课题的JAVA源码参考。_php洗车服务预约管理系统php源码
文章浏览阅读1.1k次。桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。同时,对于桶中元素的排序,选择何种比较排序算法对于性能的影响至关重要。
文章浏览阅读936次,点赞22次,收藏17次。本系统带文档lw万字以上文末可领取本课题的JAVA源码参考。
文章浏览阅读822次,点赞15次,收藏14次。在整个设计过程中,要确定可能的具体解决方案,以实现每一个小的最终目标,对于每一个小目标,我们首先必须了解一些相关的需求分析信息。除了以上作品下面是2023-2024年最新100套计算机专业原创的毕业设计源码+数据库,是近期作品,如果你的题目刚好在下面可以文末领取java源码参考。springboot基于springboot的在线考试系统。springboot基于springboot的商城购物系统。springboot基于微信小程序的智慧校园设计与实现。springboot基于用户的协同过滤算法的话题推荐。