概况
Yii2 一个高效安全的高性能PHP框架。mongodb 一个高性能分布式文档存储NOSQL数据库。
关于mongodb与mysql的优缺点,应该都了解过。
mysql传统关系数据库,安全稳定、数据完整、资源文档完善、使用群体多、支持事物,V5.7后支持原生Json速度不逊mongodb、分布式主从集群刚刚的……
mongodb新兴NOSQL数据库,Bson文档存储,支持原生Javascript、性能优异百千万数据不在话下、内置GridFS Sharding海量存储、热数据持久化、分片部署……
参考:http://www.wtoutiao.com/p/19an83a.html
那到底mysql与mongodb性能对比如何呢,什么时候选择mysql什么时候选择mongodb呢,网上有很多自己search吧,这里我们讲的是,将mongodb加入Yii2的扩展中,并使用经典的ActiveRecord操作数据。
等等,再说下什么是ActiveRecord
说ActiveRecord之前呐,先要知道ORM(Object Relational Mapping 对象关系映射)用于实现面向对象编程语言里不同类型系统的数据之间的转换。从效果上说,它其实是创建了一个可在编程语言里使用的"虚拟对象数据库"。ActiveRecord就是一个典型的ORM。Yii2 提供了DAO (Data Access Objects 数据访问对象) ,可以方便的用$db->createCommand()完成任何数据库相关的任务,包括复杂的业务SQL语句。甚至执行速度比ActiveRecord快很多,但是在这个金(敏)钱(捷)社(开)会(发)时代,开发速度才是我们解决的最主要问题。Yii2的ActiveRecord中,每个AR类代表一个数据表,其字段作为AR类的属性,一个AR实例代表在表中的一行。常见的CRUD操作被作为AR类的方法执行。 我们使用更面向对象的方法处理我们的数据,so,开发速度就开到5挡了!
一.mongodb安装
1.Windows安装
下载最新的mongodb 地址:https://www.mongodb.com/download-center 本人使用版本V3.2
一路"下一步"完成安装。mongd为服务端,mongo为客户端。打开终端切换到bin目录
终端中输入:
mongod --logpath C:\data\dbConf\mongodb.log --logappend --dbpath C:\data\db --journal --bind_ip 127.0.0.1 --serviceName MongoDB --port 9898 --install
参数说明:
-logpath C:\data\dbConf\mongodb.log #设置日志path --dbpath C:\data\db #设置db path --journal #设置数据存储格式 --bind_ip 127.0.0.1 #防止其他ip访问 --port 9888 #27017修改端口 --install #install Windows service
其中bind_ip与port很重要,记得一定要修改,嗯!秘密噢!
当使用了--install后会安装到Windows的系统服务中,如果要卸载mongodb服务,使用
sc delete mongodb
在Windows服务中开启mongodb服务
在终端中输入
mongo --port 9898
出现如下表示成功
2.Linux安装
同理下载Linux版mongodb
wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-3.4.3.tgz
解压
tar -zxvf mongodb-linux-x86_64-3.4.3.tgz
移动目录到/usr/local/mongodb
mv mongodb-linux-x86_64-3.4.3/ /usr/local/mongodb
新建db,与log文件路径,因为mongodb数据存储在data的db目录,但安装启动不会自动生成。我们把data新建在mongodb的同级目录(放哪你自己决定)
mkdir -p data/db mkdir -p data/dbconf
启动服务
cd bin/
./mongod --logpath /usr/local/mongodb/data/dbconf/mongodb.log --logappend --dbpath /usr/local/mongodb/data/db --bind_ip 127.0.0.1,201.104.104.104 --port 9898
查看data/dbconf/mongodb.log 显示失败
2017-03-30T09:45:14.324+0800 I CONTROL [initandlisten] MongoDB starting : pid=513 port=9898 dbpath=/usr/local/mongodb/data/db 64-bit host=u23dk88fzrZ 2017-03-30T09:45:14.324+0800 I CONTROL [initandlisten] db version v3.4.3 2017-03-30T09:45:14.324+0800 I CONTROL [initandlisten] git version: f07437fb5a6cca07c10bafa78365456eb1d6d5e1 2017-03-30T09:45:14.324+0800 I CONTROL [initandlisten] allocator: tcmalloc 2017-03-30T09:45:14.324+0800 I CONTROL [initandlisten] modules: none 2017-03-30T09:45:14.324+0800 I CONTROL [initandlisten] build environment: 2017-03-30T09:45:14.324+0800 I CONTROL [initandlisten] distarch: x86_64 2017-03-30T09:45:14.324+0800 I CONTROL [initandlisten] target_arch: x86_64 2017-03-30T09:45:14.324+0800 I CONTROL [initandlisten] options: { net: { bindIp: "127.0.0.1,201.104.104.104", port: 9898 }, storage: { dbPath: "/usr/local/mongodb/data/db" }, systemLog: { destination: "file", path: "/usr/local/mongodb/data/dbconf/mongodb.log" } } 2017-03-30T09:45:14.347+0800 E NETWORK [initandlisten] listen(): bind() failed Cannot assign requested address for socket: 201.104.104.104:9898 2017-03-30T09:45:14.347+0800 E NETWORK [initandlisten] Failed to set up sockets during startup. 2017-03-30T09:45:14.347+0800 E STORAGE [initandlisten] Failed to set up listener: InternalError: Failed to set up sockets 2017-03-30T09:45:14.347+0800 I NETWORK [initandlisten] shutdown: going to close listening sockets... 2017-03-30T09:45:14.347+0800 I NETWORK [initandlisten] removing socket file: /tmp/mongodb-9898.sock 2017-03-30T09:45:14.347+0800 I NETWORK [initandlisten] shutdown: going to flush diaglog... 2017-03-30T09:45:14.347+0800 I CONTROL [initandlisten] now exiting 2017-03-30T09:45:14.347+0800 I CONTROL [initandlisten] shutting down with code:48
因为没有让防火墙放行端口9898,开放端口
iptables -A INPUT -p tcp -m tcp --dport 9898 -j ACCEPT
插曲:linux用的是Centos7,默认使用的firewall防火墙必须启用iptables 链接:http://www.linuxidc.com/Linux/2015-05/117473.htm 、 http://blog.csdn.net/smstong/article/details/39317277
然后我们使用客户端mongo连接
./mongo --port 9898
已经链接成功。
要以守护进程的形式运行mongodb,要添加fork参数:
./mongod --logpath /usr/local/mongodb/data/dbconf/mongodb.log --logappend --dbpath /usr/local/mongodb/data/db --bind_ip 127.0.0.1,201.104.104.104 --port 9898 --fork
3.安装php mongodb扩展
下载地址:http://pecl.php.net/package/mongodb
*注: pecl提供了两个package 一个是 http://pecl.php.net/package/mongodb 另外一个是 http://pecl.php.net/package/mongo, 目前后者不在更新已经提示:This package has been superseded, but is still maintained for bugs and security fixes.
Windows版的扩展就不多说了,找到对应的php版本.dll就可以了,我用的是php7.0.8
修改php.ini 加入ext=php_mongodb.dll
说说Linux版本扩展的安装
这个是mongodb官网安装插件的说明:https://github.com/mongodb/mongo-php-driver-legacy
下载最新版: 2017-03-20
wget http://pecl.php.net/get/mongodb-1.2.8.tgz
解压:
tar vxzf mongodb-1.2.8.tgz
进入目录,此时就要注意了如果php是自己编译安装,在编译mongo之前的要带上php的配置path
使用phpize命令添加外挂模块, 什么是phpize? 链接: http://www.2cto.com/kf/201111/110140.html
/usr/local/php/bin/phpize
./configure --with-php-config=/usr/local/php/bin/php-config make make install
貌似php7.0.8下编译一直提示:/usr/local/php/bin/php: symbol lookup error: /home/wwwroot/tools/mongodb-1.2.8/modules/mongodb.so: undefined symbol: OPENSSL_init_ssl
解决php安装mongodb.so 扩展提示 :mongodb.so: undefined symbol: OPENSSL_init_ssl
非php7.0.8版本编译报错,找了好久问题是安装openssl版本的问题
首先我们查看phpinfo,看php -i 显示的openssl支持的多少版本
OpenSSL support | enabled |
OpenSSL Library Version | OpenSSL 1.0.1e-fips 11 Feb 2013 |
OpenSSL Header Version | OpenSSL 1.0.1e-fips 11 Feb 2013 |
Openssl default config | /etc/pki/tls/openssl.cnf |
然后在终端查看openssl版本
OpenSSL 1.1.0 25 Aug 2016
问题就在这,因为我yum update过openssl,导致php编译版本与系统现有版本不一致,使mongodb make的时候提示openssl保存有问题。
解决办法:
恢复之前的openssl版本在编译mongodb
通过find / -name openssl
/usr/bin/openssl OpenSSL 1.0.1e-fips 11 Feb 2013 /usr/local/bin/openssl OpenSSL 1.1.0 25 Aug 2016 /usr/local/include/openssl ------- /usr/local/ssl/bin/openssl OpenSSL 1.0.2c 12 Jun 2015 /usr/local/ssl/include/openssl -------- /usr/local/share/doc/openssl -------- /usr/include/openssl -------- /usr/lib64/openssl -------- /etc/pki/ca-trust/extracted/openssl--------
要让终端默认使用1.0.1e版本,(Linux终端bash默认/usr/local/bin)
/usr/local/bin/ mv openssl openssl1.1.0 cp /usr/bin/openssl ./
修改/usr/local/include/openssl目录中的.h文件为1.0.1e版本的文件,约72个*.h文件 (可查看opensslv.h文件获取版本)
终端访问:
# openssl version OpenSSL 1.0.1e-fips 11 Feb 2013
修改php.ini
extension=/usr/local/php/lib/php/extensions/no-debug-non-zts-20151012/mongodb.so
因php由php-fpm控制,因此需要重启php-fpm,使php.ini配置生效。重启php-fpm
参考 Linux 安装基于(PHP5.5)memcache扩展
service php-fpm stop
./php-fpm -c /usr/local/php/etc/php2.ini
二. Yii2 配置mongodb-extensions
可通过composer安装或手动安装
1.通过composer安装
官网安装说明:http://www.yiiframework.com/doc-2.0/ext-mongodb-index.html
命令行切换到项目vendor同级目录执行
php composer.phar require –prefer-dist yiisoft/yii2-mongodb “^2.1”
漫长等待…… 执行完成后composer.json文件和在\vendor\yiisoft下有对应的目录生成
"yiisoft/yii2-mongodb": "~2.1.0"
2.通过手动安装
下载地址:https://github.com/yiisoft/yii2-mongodb
下载后添加到 /vendor/yiisoft/yii2-mongodb
打开 vendor\yiisoft\extensions.php 在最底部添加:
'yiisoft/yii2-mongodb' => array ( 'name' => 'yiisoft/yii2-mongodb', 'version' => '2.1.0', 'alias' => array ( '@yii/mongodb' => $vendorDir . '/yiisoft/yii2-mongodb', ), ),
打开 vendor\composer\autoload_psr4.php 在最底部添加:
'yii\\mongodb\\' => array($vendorDir . '/yiisoft/yii2-mongodb'),
3.配置main文件
main-local.php 配置连接mongodb
'mongodb' => [ 'class' => 'yii\mongodb\Connection', //'class' => 'backend\models\core\mongodb\Mconnection', # 有账户的配置 //'dsn' => 'mongodb://demofancyecommerce:fdaVBDFS#fdfdtyg423DF23#$@localhost:27017/demofancyecommerce', # 无账户的配置 'dsn' => 'mongodb://127.0.0.1:9898/mongo_test', //'dsn' => 'mongodb://10.10.10.252:10001/erp,mongodb://10.10.10.252:10002/erp,mongodb://10.10.10.252:10004/erp?replicaSet=terry&readPreference=primaryPreferred', ],
4. 设置gii
设置gii生成器配置
$config['bootstrap'][] = 'mongogii'; $config['modules']['mongogii'] ['class']= 'yii\mongodb\model\Generator'; $config['modules']['mongogii']['allowedIPs'] =['127.0.0.1','201.104.104.104',"::1"];
三. ActiveRecord操作数据
这步我们要将用ActiveRecord进行数据的CURD操作
1.导入测试数据
如果你是在Windows下操作,强烈建议你用MongoVUE管理工具,很NB的一个mongodb管理工具。 好,将mysql中的数据入mongodb。
如果服务器Linux设置仅127.0.0.1可连接mongod服务端,可以参考SecureCRT端口映射转发,转发到本地对应的端口,使用 localhost:port 连接
查看插入的集合数据
2.生成models
在上一步,我们已经配置了mian文件和gii,同时也给测试库的customer集合插入测试数据,现在用gii来生成models
直接访问项目域+mongogii
生成的路径在models/Customer.php 为了区分与comm里面的,我们将模型改名为MCustomer.php,查看代码
namespace app\models; use Yii; /** * This is the model class for collection "customer". * * @property \MongoDB\BSON\ObjectID|string $_id * @property mixed $id * @property mixed $name * @property mixed $province * @property mixed $city * @property mixed $town * @property mixed $address * @property mixed $lng * @property mixed $lat * @property mixed $create_time */ class MCustomer extends \yii\mongodb\ActiveRecord { /** * @inheritdoc */ public static function collectionName() { return ['mongo_test', 'customer']; } /** * @inheritdoc */ public function attributes() { return [ '_id', 'id', 'name', 'province', 'city', 'town', 'address', 'lng', 'lat', 'create_time', 'status', ]; } /** * @inheritdoc * 参考 YII2,rules规则 */ public function rules() { return [ [['name', 'province', 'city', 'town', 'address'], 'required'], [['id', 'name', 'province', 'city', 'town', 'address', 'lng', 'lat', 'create_time', 'status'], 'safe'], [['province', 'city', 'town'], 'integer'], [['name'], 'string', 'max' => 20], ]; } /** * @inheritdoc */ public function attributeLabels() { return [ '_id' => 'ID', 'id' => 'Id', 'name' => 'Name', 'province' => 'Province', 'city' => 'City', 'town' => 'Town', 'address' => 'Address', 'lng' => 'Lng', 'lat' => 'Lat', 'create_time' => 'Create Time', ]; } }
其中rules规则是自己加的,因为mongogii仅生成‘safe’规则,rules验证规则可参考: http://www.phpxs.com/post/3443/
3.控制器方法
MVC中Models已经有了,就剩Controller与View了,而最主要的就是Controller了,因为逻辑都放在控制器中了(按照大神的建议,逻辑代码应该放到Models里面)。
其他什么都别说 然后我们吃着火锅一起唱这歌!!!上代码!!!
MongoController.php
/** * mongo controller */ namespace app\controllers; use Yii; class MongoController extends BaseController { public $pageSize = 10; /** * Index 列表首页 */ public function actionIndex() { $search = $this->post('search'); $orderField = $this->post('orderField'); $orderDirection = $this->post('orderDirection'); $where = []; $orderby = 'id desc'; ##################### search ############################## if ($search['name']) { # 模糊搜索 $where['name'] = ['$regex' => $search['name']]; } if ($search['id']) { # int 类型匹配 $where['id'] = intval($search['id']); } ###################### orderby #################################### if ($orderField && $orderDirection) { $orderby = $orderField . " {$orderDirection}"; } ############### page ##################### if ($this->post('pageSize')) { $this->pageSize = $this->post('pageSize'); } $_GET['page'] = ''; if ($this->post('pageCurrent')) { $_GET['page'] = $this->post('pageCurrent'); } ####################### data #################### $data = \app\models\MCustomer::find()->where($where)->orderBy($orderby); $count = $data->count(); $pagesize = $this->pageSize; // 分页大小 $pages = new \yii\data\Pagination(['totalCount' => $count, 'pageSize' => $pagesize]); $model = $data->offset($pages->offset)->limit($pages->limit)->all(); $pagge = $this->render('//' . 'default/page', ['pageSize' => $pagesize, 'pageTotal' => $count, 'page' => $_GET['page']]); return $this->display([ 'alist' => $model, 'pages' => $pages, 'search' => $search, 'pagge' => $pagge, ]); } /** * 删除操作 */ public function actionDel() { $id = self::post('id'); # field id 与字段类型必须对应 (int) $_id = self::post('_id'); # mongoid # by _id $model = \app\models\MCustomer::findOne($_id); if ($model) { $model->delete(); # 物理删除 } # by id if ($id) { $model = \app\models\MCustomer::find()->where(["id" => intval($id)])->one(); if ($model) { $model->status = 3; # 状态删除 $model->save(); } } if (!$model->errors) { self::UICallback(200, '删除成功'); } } /** * 编辑、 新增 操作 */ public function actionEdit() { $id = $this->query('id'); $model = \app\models\MCustomer::find()->where(["id" => intval($id)])->one(); if (!$model) { $model = new \app\models\MCustomer(); } return $this->display('edit', [ 'model' => $model, ]); } /** * 编辑新增 保存save 操作 */ public function actionDataSave() { $_id = self::post('_id'); $post = \Yii::$app->request->post(); # 获取省市区县及address, 通过百度解析为lng、lat经纬度保存-- 演示暂无 $post['create_time'] = strtotime($post['create_time']); if ($_id) { # 编辑 $model = \app\models\MCustomer::findOne($_id); $model->attributes = $post; $model->save(); } else { # 新增 $model = new \app\models\MCustomer(); $model->attributes = $post; $model->save(); #$id= $model->attributes['id']; # 获取插入数据的mongoid } if ($model->errors) { self::UICallback(300, '保存失败', ['node' => $model->errors]); } else { self::UICallback(200, '保存成功', ['closeCurrent' => TRUE, 'tabid' => 'monnn']); } } }
views/mongo里面代码:
index.php
<?php use yii\bootstrap\ActiveForm; ?> <div class="bjui-pageHeader"> <?php $form = ActiveForm::begin([ 'id' => 'pagerForm', 'method' => 'post', 'options' => ['data-toggle' => 'ajaxsearch'] ]); ?> <input type="hidden" name="pageSize" value="${model.pageSize}" placeholder=""> <input type="hidden" name="pageCurrent" value="${model.pageCurrent}"> <input type="hidden" name="orderField" value=""> <input type="hidden" name="orderDirection" value=""> <div class="bjui-searchBar"> <label>客户姓名:</label> <?php echo yii\helpers\Html::input('text', 'search[name]', $search['name'], ['size' => 12, 'placeholder' => '客户姓名']) ?> <label>客户id:</label> <?php echo yii\helpers\Html::input('text', 'search[id]', $search['id'], ['size' => 12, 'placeholder' => '客户ID信息']) ?> <button type="submit" class="btn-default" data-icon="search">查询</button> <a href="/mongo/edit" class="btn btn-green" data-id="mongo_data" data-toggle="navtab" data-title="新增客户" data-icon="plus">客户</a> </div> <?php ActiveForm::end(); ?> </div> <div class="bjui-pageContent tableContent"> <table data-toggle="tablefixed" data-width="100%" data-nowrap="true"> <thead> <tr> <th data-order-field="id" >ID</th> <th data-order-field="name">姓名</th> <th>位置</th> <th>经纬度</th> <th width="370">操作</th> </tr> </thead> <tbody> <?php if (!empty($alist)) { foreach ($alist as $key => $value) { ?> <tr data-id=""> <td><?php echo $value['id']; ?></td> <td><?php echo $value['name']; ?></td> <td><?php echo $value['city']; ?><?php echo $value['address']; ?></td> <td><?php echo $value['lng']; ?>,<?php echo $value['lat']; ?></td> <td> <a href="/mongo/edit?id=<?php echo $value->id;?>" class="btn btn-orange" data-id="mongo_data" data-toggle="navtab" data-reload-warn="本页已有打开的内容,确定将刷新本页内容,是否继续?" data-title="查看客户信息" data-icon="edit">查看</a> <a href="/mongo/del" data-toggle="doajax" data-data="_id=<?php echo $value->{'_id'}?>&id=<?php echo $value['id'] ?>&_csrf=<?php echo Yii::$app->request->csrfToken; ?>" class="btn btn-red row-del" data-confirm-msg="确定要删除该行信息吗?" data-icon="remove">删</a> </td> </tr> <?php } } ?> </tbody> </table> </div> <?php echo $pagge;
edit.php
<?php use yii\helpers\Html; use yii\bootstrap\ActiveForm; ?> <div class="bjui-pageContent"> <?php $form = ActiveForm::begin([ 'action' => '/mongo/data-save', 'options' => ['data-alertmsg' => 'false', 'data-toggle' => 'validate']]); ?> <?php echo Html::hiddenInput('_id', $model->{'_id'}); ?> <table class="table table-condensed table-hover" width="100%"> <tbody> <tr> <td colspan="2"> <label for="name" class="control-label x85">姓名:</label> <?php echo Html::input('text', 'name', $model->name, ['data-rule' => "required", 'placeholder' => '姓名']); ?> </td> </tr> <tr> <td> <label for="j_custom_birthday" class="control-label x85">现居地址:</label> <?php echo $this->AreaSelect(['name' => 'province', 'chkval' => $model['province'], 'area' => 'province', 'data-rule' => 'required', 'doption' => '请选择省份', 'data-nextselect' => '#employee_j_form_city', 'data-refurl' => '/comm/area?id={value}']); ?>- <?php echo $this->AreaSelect(['name' => 'city', 'chkval' => $model['city'], 'area' => 'city', 'data-rule' => 'required', 'doption' => '请选择城市', 'parentid' => $model['province'], 'id' => 'employee_j_form_city', 'data-nextselect' => '#employee_j_form_town', 'data-refurl' => '/comm/area?id={value}']); ?>- <?php echo $this->AreaSelect(['name' => 'town', 'chkval' => $model['town'], 'area' => 'town', 'data-rule' => 'required', 'doption' => '请选择区县', 'data-emptytxt' => '请选择区县', 'parentid' => $model['city'], 'id' => 'employee_j_form_town']); ?> </td> <td > <label for="address" class="control-label x85">详细地址:</label> <?php echo Html::input('text', 'address', $model->address, ['data-rule' => "required", 'id' => 'address', 'size' => 25, 'placeholder' => '详细地址']); ?> </td> </tr> <tr> <td colspan="2"> <label for="url" class="control-label x85">登记时间:</label> <?php echo Html::input('text', 'create_time', $model->create_time ? date("Y-m-d", $model->create_time) : '', ['data-rule' => "required", 'data-toggle' => 'datepicker', 'data-pattern' => 'yyyy-MM-dd H:m', 'placeholder' => '登记时间', 'data-min-date' => date('Y-m-d')]); ?> </td> </tr> </tbody> </table> <?php ActiveForm::end(); ?> </div> <div class="bjui-pageFooter"> <ul> <li><button type="button" class="btn-close" data-icon="close">取消</button></li> <li><button type="submit" class="btn-default" data-icon="save">保存</button></li> </ul> </div>
效果: