Yii2 Log 特性

文件Log的配置:

'log' =>[  
      # 追踪级别  
      # 消息跟踪级别  
      # 在开发的时候,通常希望看到每个日志消息来自哪里。这个是能够被实现的,通过配置 log 组件的 yii\log\Dispatcher::traceLevel 属性, 就像下面这样:  
      'traceLevel' => 3,  
        
      # 通过 yii\log\Logger 对象,日志消息被保存在一个数组里。为了这个数组的内存消耗, 当数组积累了一定数量的日志消息,日志对象每次都将刷新被记录的消息到 log targets 中。 你可以通过配置 log 组件的 yii\log\Dispatcher::flushInterval 属性来自定义数量  
      'flushInterval' => 1,  
        
      'targets' => [  
        'file' =>[  
          //'levels' => ['trace'],  
          'categories' => ['fecshop_debug'],  
          'class' => 'yii\log\FileTarget',  
          # 当 yii\log\Logger 对象刷新日志消息到 log targets 的时候,它们并 不能立即获取导出的消息。相反,消息导出仅仅在一个日志目标累积了一定数量的过滤消息的时候才会发生。你可以通过配置 个别的 log targets 的 yii\log\Target::exportInterval 属性来 自定义这个数量,就像下面这样:  
          'exportInterval' => 1,  
          # 输出文件  
          'logFile' => '@appfront/runtime/fecshop_logs/fecshop_debug.log',  
          # 你可以通过配置 yii\log\Target::prefix 的属性来自定义格式,这个属性是一个PHP可调用体返回的自定义消息前缀  
          'prefix' => function ($message) {  
            return $message;  
          },  
          # 除了消息前缀以外,日志目标也可以追加一些上下文信息到每组日志消息中。 默认情况下,这些全局的PHP变量的值被包含在:$_GET, $_POST, $_FILES, $_COOKIE,$_SESSION 和 $_SERVER 中。 你可以通过配置 yii\log\Target::logVars 属性适应这个行为,这个属性是你想要通过日志目标包含的全局变量名称。 举个例子,下面的日志目标配置指明了只有 $_SERVER 变量的值将被追加到日志消息中。  
          # 你可以将 logVars 配置成一个空数组来完全禁止上下文信息包含。或者假如你想要实现你自己提供上下文信息的方式, 你可以重写 yii\log\Target::getContextMessage() 方法。  
           'logVars' => [],  
        ],  
      ],  
    ],

然后新建文件:@appfront/runtime/fecshop_logs/fecshop_debug.log 并设置可写就行。

调用:

\Yii::info($post_log,'fecshop_debug');

$post_log 就是要输出的log内容。

如果log的配置或者文件有问题会不会报错呢?

1.去掉上面的配置,然后执行调用:

\Yii::info($post_log,'fecshop_debug');

经过测试,程序不会被卡住,会继续执行的

2.配置保留,去掉配置中对应的真实文件:@appfront/runtime/fecshop_logs/fecshop_debug.log 删除掉,经过测试,发现程序不会被卡住,还是会继续执行的。

总结:log出现问题,只要不是语法文件,不会卡住,还是会继续跑下去的。

Yii2 controller 传值给layout

在yii2中,我们通过下面的方法,将controller的数组传递给view

public function actionIndex()
    {
        $data = ['xx' => 'yy'];
        return $this->render($this->action->id,$data);
    }

在view文件中就可以使用$xx变量了,这个变量的值是’yy’.

现在我们想给layout里面传递,怎么办呢?下面是原理:

在yii/base/Controller.php中可以看到如下代码:

public function render($view, $params = [])
    {
        $content = $this->getView()->render($view, $params, $this);
        return $this->renderContent($content);
    }

查找renderContent()方法

public function renderContent($content)
   {
       $layoutFile = $this->findLayoutFile($this->getView());
       if ($layoutFile !== false) {
           return $this->getView()->renderFile($layoutFile, ['content' => $content], $this);
       }
       return $content;
   }

可以看到,我们只要重写renderContent()方法,在这个方法的内容部分:

[‘content’ => $content]

在这个数组中,添加上我们的想要的其他的数组,譬如:

[‘content’ => $content, ‘tt’ => ‘terry’]

我们就可以在layout里面使用$tt变量了。也就是将controller中的变量传递给layout。

 

 

yii2 配置加速 – N个配置文件生成一个配置文件

yii2的配置方式是N个配置文件,最终通过merge函数合并成一个php数组,如果只有几个配置文件,不会占用太多资源,如果像fecshop这类电商系统,有大量的配置文件,因此需要合并N个配置文件成一个配置文件,具体实现如下:

首先新建文件@app/merge_config.php 设置可写权限, 然后新建文件 @app/web/index-merge-config.php,将index.php的内容复制过来,在 代码:$config = yii\helpers\ArrayHelper::merge函数后面,添加下面的代码,这些代码的用处是将$config数组的内容重新写成php代码保存到文件@app/merge_config.php中

$str = '<?php '.PHP_EOL;
$str .= 'return '.PHP_EOL;

function toPhpCode($arr,$i=0){
  $i++;
  $tb_i = $i*4;
  $tb = ($i-1)*4;
  $tb1_str = '';
  $tb2_str = '';
  for($j=0;$j<$tb;$j++){
    $tb1_str .= ' ';
  }
  for($jj=0;$jj<$tb_i;$jj++){
    $tb2_str .= ' ';
  }
  $mystr = '';
  if(is_array($arr) && !empty($arr)){
    $mystr .= PHP_EOL .$tb1_str.'['.PHP_EOL;
    $t_arr = [];
    foreach($arr as $k => $v){
      $key 	= '';
      $value 	= '';
      if(is_string($k)){
        $key = "'".$k."'";
      }else{
        $key = $k;
      }
      if(is_array($v) && !empty($v)){
        $value = toPhpCode($v,$i);
      }else if(is_array($v) && empty($v)){
        $value = '[]';
      
      }else{
        if(is_string($v)){
          $value = "'".$v."'";
        }else if(is_bool($v)){
          if($v){
            $value = 'true';
          }else{
            $value = 'false';
          }
        }else{
          if(is_null($v)){
            $v = "''";
          }
          $value = $v;
        }
        
      }
      $t_arr[] = $tb2_str.$key."=>".$value;
    }
    $mystr .= implode(','.PHP_EOL,$t_arr);
    $mystr .= PHP_EOL .$tb1_str.']'. PHP_EOL;
  }
  return $mystr;
}


$str .= toPhpCode($config);
$str .= ';';

echo 'generate merge config file success';
file_put_contents('../merge_config.php', $str);
echo 'generate merge config file success';

执行这个文件后,就会将$config数组的内容,以php代码的方式写入到merge_config.php,这样就生成了单文件配置。

在index.php中加载单文件配置

$use_merge_config_file = true;

if($use_merge_config_file){
  $config = require('../merge_config.php');
}else{
  $config = yii\helpers\ArrayHelper::merge(
    require(__DIR__ . '/../../common/config/main.php'),
    require(__DIR__ . '/../../common/config/main-local.php'),
    require(__DIR__ . '/../config/main.php'),
    require(__DIR__ . '/../config/main-local.php'),
    # fecshop services config
    require(__DIR__ . '/../../vendor/fancyecommerce/fecshop/config/fecshop.php'),
    # fecshop module config
    require(__DIR__ . '/../../vendor/fancyecommerce/fecshop/app/appfront/config/appfront.php'),
    
    # thrid part confing
    
    # common modules and services.
    require(__DIR__ . '/../../common/config/fecshop_local.php'),
     
    # appadmin local modules and services.
    require(__DIR__ . '/../config/fecshop_local.php')
    
  );
}

这样就完成了。

 

下面fecshop的代码我粘贴一下:(供参考)

index.php:

<?php
error_reporting(E_ALL || ~E_NOTICE); //除去 E_NOTICE 之外的所有错误信息
ini_set('session.cookie_domain', '.fancyecommerce.com'); //初始化域名,
$http = ($_SERVER['SERVER_PORT'] == 443) ? 'https' : 'http';
$homeUrl = $http.'://'.$_SERVER['HTTP_HOST'].rtrim(dirname($_SERVER['SCRIPT_NAME']), '\\/');
/**
 * fecshop 使用合并配置(config)数组进行加速,true 代表打开。
 * 打开配置加速开关前,您需要执行 http://domain/index-merge-config.php 进行生成单文件配置数组。
 * 注意:打开后,当您修改了配置,都需要访问一次上面的链接,重新生成单文件配置数组,否则修改的配置不会生效
 * 建议:本地开发环境关闭,开发环境如果访问量不大,关闭也行,如果访问量大,建议打开
 * 
 */
$use_merge_config_file = false; 
 
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');

require(__DIR__ . '/../../vendor/autoload.php');
require(__DIR__ . '/../../vendor/fancyecommerce/fecshop/yii/Yii.php');

require(__DIR__ . '/../../common/config/bootstrap.php');

require(__DIR__ . '/../config/bootstrap.php');

if($use_merge_config_file){
  $config = require('../merge_config.php');
}else{
  $config = yii\helpers\ArrayHelper::merge(
    require(__DIR__ . '/../../common/config/main.php'),
    require(__DIR__ . '/../../common/config/main-local.php'),
    require(__DIR__ . '/../config/main.php'),
    require(__DIR__ . '/../config/main-local.php'),
    # fecshop services config
    require(__DIR__ . '/../../vendor/fancyecommerce/fecshop/config/fecshop.php'),
    # fecshop module config
    require(__DIR__ . '/../../vendor/fancyecommerce/fecshop/app/appfront/config/appfront.php'),
    
    # thrid part confing
    
    # common modules and services.
    require(__DIR__ . '/../../common/config/fecshop_local.php'),
     
    # appadmin local modules and services.
    require(__DIR__ . '/../config/fecshop_local.php')
    
  );
}

$config['homeUrl'] = $homeUrl;
/**
 * yii class Map Custom
 * 
 */ 
$yiiClassMap = require(__DIR__ . '/../config/YiiClassMap.php');
if(is_array($yiiClassMap) && !empty($yiiClassMap)){
  foreach($yiiClassMap as $namespace => $filePath){
    Yii::$classMap[$namespace] = $filePath;
  }
}

/**
 * 添加fecshop的服务 ,Yii::$service  ,  将services的配置添加到这个对象。
 * 使用方法:Yii::$service->cms->article;
 * 上面的例子就是获取cms服务的子服务article。
 */
new fecshop\services\Application($config['services']);
unset($config['services']);

$application = new yii\web\Application($config);
$application->run();

index-merge-config.php

<?php
error_reporting(E_ALL ); //除去 E_NOTICE 之外的所有错误信息

ini_set('session.cookie_domain', '.fancyecommerce.com'); //初始化域名,

$http = ($_SERVER['SERVER_PORT'] == 443) ? 'https' : 'http';
defined('YII_DEBUG') or define('YII_DEBUG', true);

defined('YII_ENV') or define('YII_ENV', 'dev');

require(__DIR__ . '/../../vendor/autoload.php');
require(__DIR__ . '/../../vendor/fancyecommerce/fecshop/yii/Yii.php');

require(__DIR__ . '/../../common/config/bootstrap.php');

require(__DIR__ . '/../config/bootstrap.php');

$config = yii\helpers\ArrayHelper::merge(
    require(__DIR__ . '/../../common/config/main.php'),
    require(__DIR__ . '/../../common/config/main-local.php'),
    require(__DIR__ . '/../config/main.php'),
    require(__DIR__ . '/../config/main-local.php'),
  # fecshop services config
  require(__DIR__ . '/../../vendor/fancyecommerce/fecshop/config/fecshop.php'),
  # fecshop module config
  require(__DIR__ . '/../../vendor/fancyecommerce/fecshop/app/appfront/config/appfront.php'),
  
  # thrid part confing
  
  # common modules and services.
  require(__DIR__ . '/../../common/config/fecshop_local.php'),
   
  # appadmin local modules and services.
  require(__DIR__ . '/../config/fecshop_local.php')
    
);

$str = '<?php '.PHP_EOL;
$str .= 'return '.PHP_EOL;

function toPhpCode($arr,$i=0){
  $i++;
  $tb_i = $i*4;
  $tb = ($i-1)*4;
  $tb1_str = '';
  $tb2_str = '';
  for($j=0;$j<$tb;$j++){
    $tb1_str .= ' ';
  }
  for($jj=0;$jj<$tb_i;$jj++){
    $tb2_str .= ' ';
  }
  $mystr = '';
  if(is_array($arr) && !empty($arr)){
    $mystr .= PHP_EOL .$tb1_str.'['.PHP_EOL;
    $t_arr = [];
    foreach($arr as $k => $v){
      $key 	= '';
      $value 	= '';
      if(is_string($k)){
        $key = "'".$k."'";
      }else{
        $key = $k;
      }
      if(is_array($v) && !empty($v)){
        $value = toPhpCode($v,$i);
      }else if(is_array($v) && empty($v)){
        $value = '[]';
      
      }else{
        if(is_string($v)){
          $value = "'".$v."'";
        }else if(is_bool($v)){
          if($v){
            $value = 'true';
          }else{
            $value = 'false';
          }
        }else{
          if(is_null($v)){
            $v = "''";
          }
          $value = $v;
        }
        
      }
      $t_arr[] = $tb2_str.$key."=>".$value;
    }
    $mystr .= implode(','.PHP_EOL,$t_arr);
    $mystr .= PHP_EOL .$tb1_str.']'. PHP_EOL;
  }
  return $mystr;
}


$str .= toPhpCode($config);
$str .= ';';

echo 'generate merge config file success';
file_put_contents('../merge_config.php', $str);
echo 'generate merge config file success';

 

 

Yii2 账号登录需要注意的地方 – 生成密码函数generatePasswordHash很慢

Yii2默认的用户组件,在登录用户,都需要先把密码加密,然后去数据查询核对,

密码加密的代码如下:

\Yii::$app->security->generatePasswordHash($password);

Yii2/base/Security.php

public function generatePasswordHash($password, $cost = null)
    {
        if ($cost === null) {
            $cost = $this->passwordHashCost;
        }

        if (function_exists('password_hash')) {
            /** @noinspection PhpUndefinedConstantInspection */
            return password_hash($password, PASSWORD_DEFAULT, ['cost' => $cost]);
        }

        $salt = $this->generateSalt($cost);
        $hash = crypt($password, $salt);
        // strlen() is safe since crypt() returns only ascii
        if (!is_string($hash) || strlen($hash) !== 60) {
            throw new Exception('Unknown error occurred while generating hash.');
        }

        return $hash;
    }

最后,发现代码耗时卡在   $hash = crypt($password, $salt);

也即是php的crypt函数。在我的linux上面耗费了520ms

现在的系统验证码,有很多包,是可以破的,玩爬虫的,都有付费的机器学习类的破验证码,普通的文字数字验证码,很轻松能破,验证码一破,账号注册登录被爬虫搞起来,很快就把php进程堵死了。

如果你的系统,登录注册很频繁,建议重写一下这个函数,不过是否在某些方面存在安全性,这个我说不好,自己取舍一下。

上面是我测试的结果,大家有兴趣可以都测试试试。

Yii2 – elasticSearch 新建mapping操作

在一些需要完全匹配,或者其他的一些情况,需要建立mapping,这个有点类似mysql的表定义

ActiveRecord的定义:

<?php

namespace appadmin\code\Ta\models\elasticSearch;

use yii\elasticsearch\ActiveRecord;

class TraceData extends ActiveRecord
{
  public static $currentIndex;
  
  # 定义db链接
  public static function getDb()
  {
    return \Yii::$app->get('elasticsearch_TA');
  }
  
  # 不同的website 使用的是不同的db ,使用前需要先初始化
  # db的名字
  public static function initDb($website_id){
    if($website_id){
      self::$currentIndex = 'ta'."_".$website_id;
    }
  }
  
  
  
  # db
  public static function index()
  {
    return self::$currentIndex;
  }
  # table
  public static function type()
  {
    return 'trace_data';
  }
  
   public function attributes()
    {
        $mapConfig = self::mapConfig();
    return array_keys($mapConfig['properties']);
    }
  
  public static function mapConfig(){
    return [
      'properties' => [
        'id'				=> ['type' => 'string',"index" => "not_analyzed"],
        'ip'				=> ['type' => 'string',"index" => "not_analyzed"],
        'service_date_str'	=> ['type' => 'string',"index" => "not_analyzed"],
        'service_datetime'	=> ['type' => 'string',"index" => "not_analyzed"],
        'service_timestamp'	=> ['type' => 'integer',"index" => "not_analyzed"],
        'devide' 			=> ['type' => 'string',"index" => "not_analyzed"],
        'user_agent' 		=> ['type' => 'string',"index" => "not_analyzed"],
        'browser_name' 		=> ['type' => 'string',"index" => "not_analyzed"],
        'browser_version'	=> ['type' => 'string',"index" => "not_analyzed"],
        'browser_date'		=> ['type' => 'string',"index" => "not_analyzed"],
        'browser_lang'		=> ['type' => 'string',"index" => "not_analyzed"],
        'operate' 			=> ['type' => 'string',"index" => "not_analyzed"],
        'operate_relase'	=> ['type' => 'string',"index" => "not_analyzed"],
        'domain' 			=> ['type' => 'string',"index" => "not_analyzed"],
        'url'				=> ['type' => 'string',"index" => "not_analyzed"],
        'title'				=> ['type' => 'string',"index" => "not_analyzed"],
        'refer_url'			=> ['type' => 'string',"index" => "not_analyzed"],
        'first_referrer_domain'	=> ['type' => 'string',"index" => "not_analyzed"],
        'is_return'			=> ['type' => 'integer',"index" => "not_analyzed"],
        'uuid'				=> ['type' => 'string',"index" => "not_analyzed"],
        'device_pixel_ratio'=> ['type' => 'string',"index" => "not_analyzed"],
        'resolution'		=> ['type' => 'string',"index" => "not_analyzed"],
        'color_depth'		=> ['type' => 'string',"index" => "not_analyzed"],
        'website_id'		=> ['type' => 'integer',"index" => "not_analyzed"],
        'sku'				=> ['type' => 'string',"index" => "not_analyzed"],
        'country_code'		=> ['type' => 'string',"index" => "not_analyzed"],
        'country_name'		=> ['type' => 'string',"index" => "not_analyzed"],
        
        'order_status' 		=> ['type' => 'string',"index" => "not_analyzed"],
        'cart' 				=> ['type' => 'string',"index" => "not_analyzed"],
        'order'				=> ['type' => 'string',"index" => "not_analyzed"],
        'category'			=> ['type' => 'string',"index" => "not_analyzed"],
        'login_email'		=> ['type' => 'string',"index" => "not_analyzed"],
        'register_email'	=> ['type' => 'string',"index" => "not_analyzed"],
        'search'			=> ['type' => 'string',"index" => "not_analyzed"],
        'currency'			=> ['type' => 'string',"index" => "not_analyzed"],
        'url_new'			=> ['type' => 'string',"index" => "not_analyzed"],
        'stay_seconds'		=> ['type' => 'integer',"index" => "not_analyzed"],
        'first_visit_this_url'	=> ['type' => 'string',"index" => "not_analyzed"],
      ]
    ];
  }
  
  public static function mapping()
    {
        return [
            static::type() => self::mapConfig(),
        ];
    }

    /**
     * Set (update) mappings for this model
     */
    public static function updateMapping(){
        $db = self::getDb();
        $command = $db->createCommand();
    if(!$command->indexExists(self::index())){
      $command->createIndex(self::index());
    }
        $command->setMapping(self::index(), self::type(), self::mapping());
    }
  
  public static function getMapping(){
    $db = self::getDb();
        $command = $db->createCommand();
    return $command->getMapping();
  }
  
  
  
}

使用:

public function actionMapping($websiteIds){
    $arr = explode(",",$websiteIds);
    foreach($arr as $website_id){
      TraceData::initDb($website_id);
      TraceData::updateMapping();
      $map = TraceData::getMapping();
      var_dump($map);
    }
}

通过updateMapping来更新mapping

通过getMapping得到定义好的mapping

在这里的一个坑就是:在添加表(type)mapping的时候,需要提前定义Index(相当于mysql的db),才能添加type(相当于表),否则添加不上,或者报错。

其他资料:
https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-create-index.html

http://www.open-open.com/lib/view/open1455452874636.html

Yii2 – 批量插入数据到 elasticSearch

elasticSearch 是目前来说,最强大的开源搜索引擎,对于一些搜索,放到ElasticSearch中,速度会快很多,当然,这个玩意也是非常消耗资源。

下面是,使用yii2,将数据批量导入到ES中,单行插入的效率太低,使用批量插入,速度还是可以。

安装ElasticSearch 这个参看

安装ElasticSearch ,以及在yii2中的使用

2. 安装yii2-ElasticSearch插件

https://github.com/yiisoft/yii2-elasticsearch

3. 配置

'elasticsearch_TA' => [
    'class' => 'yii\elasticsearch\Connection',
    'nodes' => [
        ['http_address' => '192.168.0.199:9200'],
        ['http_address' => '192.168.0.210:9200'],
    ],
],

4.使用

传递数据,我们还是用shell 脚本来传递数据 /appta/shell/customer/syncCustomerDataToEs.sh

#!/bin/sh

DIR=$(cd `dirname $0`; pwd)
# sync mongodb to elasticsearch
echo 'sync custom data to es'
processDate=$1
websiteIds=$2

arr=$(echo $websiteIds|tr "," "\n");
for website_id in $arr; do
  echo "website_id:".$website_id;
  variable=`$DIR/../../../yii ta/migrate/elasticsearch/customerdatapagecount $processDate $website_id`
  echo "$variable.."
  for (( i=1; i<=$variable; i++ ))
  do 
    $DIR/../../../yii ta/migrate/elasticsearch/customerdata $processDate $website_id $i 
    echo "Page $i done"
  done
done







controller文件:

<?php
namespace appadmin\code\Ta\console\migrate;
use Yii;
use appadmin\code\Ta\models\WebsiteBaseInfo;
use yii\console\Controller;
use appadmin\code\Ta\helper\mongoDb as MongoDb;
use appadmin\code\Ta\models\mongo\CustomerData as MgCustomerData;
use appadmin\code\Ta\models\elasticSearch\CustomerData as EsCustomerData;

use appadmin\code\Ta\models\mongo\TraceData as MgTraceData;
use appadmin\code\Ta\models\elasticSearch\TraceData as EsTraceData;


class ElasticsearchController extends Controller
{
  public $numPerPage = 1000;
  //public $dbName = "ta_".$processDate;
  //public $collName;
  
  public function initParam($processDate,$website_id){
    //$thidbName = "ta_".$processDate;
    $collName = "ta_".$website_id."_customer_data";
    //echo $processDate;exit;
    MongoDb::setDbByDate($processDate);
    MgCustomerData::initCollName($website_id);
    MgTraceData::initCollName($website_id);
  }
  # customer data  数据的总页数
  public function actionCustomerdatapagecount($processDate,$website_id){
    $this->initParam($processDate,$website_id);
    $count =  MgCustomerData::find()->count();
    //var_dump(MgCustomerData::getDb());
    //echo $count;exit;
    echo ceil($count/$this->numPerPage);
  }
  # 同步customer data的数据到ElasticSearch
  public function actionCustomerdata($processDate,$website_id,$pageNum){
    $this->initParam($processDate,$website_id);
    $skip = $this->numPerPage * ($pageNum - 1);
    $data = MgCustomerData::find()
        ->asArray()
        ->limit($this->numPerPage)
        ->offset($skip)
        ->all();
    $arr = [];
    $i = 0;
        
    if(is_array($data) && !empty($data )){
      $elasticsearch = Yii::$app->elasticsearch_TA;
      $bulkclient = $elasticsearch->createBulkCommand();
      //EsCustomerData::initDb($website_id);
      $index_name = 'ta_'.$website_id;
      $one_day_type = 'customer_data';
      //$EsCustomerDataOne = EsCustomerData::findOne($a['_id']);
      foreach($data  as $one){
        $i++;
        $a = [];
        $a['id'] = $one['_id'];
        $value = $one['value'];
        if(is_array($value) && !empty($value )){
          foreach($value  as $k => $v){
            if($k == 'data'){
              //var_dump($v);
              $v = serialize($v);
            }
            $a[$k] = $v;
          }
        }
        
        $bulkclient->addAction(array(
          'index' => array(
            '_index'=> $index_name,
            '_type' => $one_day_type,
            '_id' 	=> $one['_id'],
          )
        ), $a);
        /*
        # 保存数据到ES
        EsCustomerData::initDb($website_id);
        $EsCustomerDataOne = EsCustomerData::findOne($a['_id']);
        if(!$EsCustomerDataOne){
          $EsCustomerDataOne = new EsCustomerData;
          $EsCustomerDataOne->setPrimaryKey($a['_id']);
        }
        $EsCustomerDataOne->id = $a['_id'];
        $attributes = $EsCustomerDataOne->attributes();
        foreach($a as $k=>$v){
          if(in_array($k,$attributes)){
            if($k == 'data'){
              //var_dump($v);
              $v = serialize($v);
            }
            $EsCustomerDataOne[$k] = $v;
          }
        }
        $mtime=explode(' ',microtime());
        $startTime=$mtime[1]+$mtime[0];        
        
        $EsCustomerDataOne->save();
        $mtime=explode(' ',microtime());
        $endTime=$mtime[1]+$mtime[0];        
        echo "chaju_time :($i)".($endTime-$startTime)."\n"; 
        //$arr[] = $a; 
        */
      }
      $bulkclient->execute();
    }
    
    
  }
    
  # customer data  数据的总页数
  public function actionTracedatapagecount($processDate,$website_id){
    $this->initParam($processDate,$website_id);
    $count =  MgTraceData::find()->count();
    //var_dump(MgCustomerData::getDb());
    //echo $count;exit;
    echo ceil($count/$this->numPerPage);
  }
  # 同步customer data的数据到ElasticSearch
  public function actionTracedata($processDate,$website_id,$pageNum){
    $this->initParam($processDate,$website_id);
    $skip = $this->numPerPage * ($pageNum - 1);
    $data = MgTraceData::find()
        ->asArray()
        ->limit($this->numPerPage)
        ->offset($skip)
        ->all();
    $arr = [];
    $i = 0;
        
    if(is_array($data) && !empty($data )){
      $elasticsearch = Yii::$app->elasticsearch_TA;
      $bulkclient = $elasticsearch->createBulkCommand();
      //EsCustomerData::initDb($website_id);
      $index_name = 'ta_'.$website_id;
      $one_day_type = 'trace_data';
      //$EsCustomerDataOne = EsCustomerData::findOne($a['_id']);
      foreach($data  as $one){
        $i++;
        $a = [];
        
        if(is_array($one) && !empty($one )){
          foreach($one  as $k => $v){
            $a[$k] = $v;
          }
        }
        $a['id'] = $a['_id'];
        unset($a['_id']);
        
        $bulkclient->addAction(array(
          'index' => array(
            '_index'=> $index_name,
            '_type' => $one_day_type,
            '_id' 	=> $one['_id'],
          )
        ), $a);
        
      }
      $bulkclient->execute();
    }
    
    
  }	
    
    
    
    
    
    
    
    
    
    
    
}

appadmin\code\Ta\models\mongo\CustomerData

<?php  
# 商家SELLER 和  对应的 SELLERID 的设置。 
namespace appadmin\code\Ta\models\mongo; 
use yii\mongodb\ActiveRecord;
use fec\helpers\CDate;
use fec\helpers\CConfig;
use Yii;
use appadmin\code\Ta\helper\mongoDb;
# use appadmin\code\Ta\models\mongo\CustomerData; 
class CustomerData extends ActiveRecord  
{  
  
  public static $_collectionName;
  
  # 定义db
  public static function getDb()
    {
    return \Yii::$app->get('mongodb_ta_date');
    }
  
  
  
  # 定义collection name  
    public static function collectionName()  
    {  
        return self::$_collectionName;  
    }  
  
  
  
  
  public static function initCollName($website_id){
    self::$_collectionName = "ta_".$website_id."_customer_data";
  }
  
  
  public function attributes()
    {
        // path mapping for '_id' is setup to field 'id'
        return [
      '_id', 
      'value',
      
    ];
    }
  
  
}  

appadmin\code\Ta\models\ElasticSearch\CustomerData

<?php

namespace appadmin\code\Ta\models\elasticSearch;

use yii\elasticsearch\ActiveRecord;

class CustomerData extends ActiveRecord
{
  public static $currentIndex;
  
  # 定义db链接
  public static function getDb()
  {
    return \Yii::$app->get('elasticsearch_TA');
  }
  
  # 不同的website 使用的是不同的db ,使用前需要先初始化
  # db的名字
  public static function initDb($website_id){
    //echo 888;
    if($website_id){
      //echo 999;
      self::$currentIndex = 'ta'."_".$website_id;
      //echo self::$currentIndex;
      //echo 3;
    }
  }
  
  
  
  # db
  public static function index()
  {
    return self::$currentIndex;
  }
  # table
  public static function type()
  {
    return 'customer_data';
  }
  
   public function attributes()
    {
        // path mapping for '_id' is setup to field 'id'
        return [
      'id',
      
      'uuid',
      'customer_id',
      'pv',
      
      'ip',
      'service_date_str',
      'service_datetime',
      'service_timestamp',
      'devide',
      'user_agent',
      'browser_name',
      'browser_version',
      'browser_date',
      'browser_lang',
      'operate',
      'operate_relase',
      'domain',
      'url',
      'title',
      'refer_url',
      'first_referrer_domain',
      'is_return',
      'uuid',
      'device_pixel_ratio',
      'resolution',
      'color_depth',
      'website_id',
      'sku',
      'country_code',
      'country_name',
      
      'data',
      
      'order_status',
      'cart',
      'order',
      'category',
      'login_email',
      'register_email',
      'search',
      'currency',
      'stay_seconds',
    ];
    }
  
  
  
}

 

 

通过配置的方式重写某个Yii2 文件 或第三方扩展文件

1.我需要重写某个Yii的类方法,譬如:yii\helpers\ArrayHelper

我需要新建一个类,继承,然后覆盖这个类的方法。
如果我的系统都成型了,然后我在调用这个类的地方,需要将

use yii\helpers\ArrayHelper

改成

use xxxxxx\yii\helpers\ArrayHelper

2.现在用classMap

Yii::$classMap['yii\helpers\ArrayHelper'] = '@xxxxxx/yii/helpers/ArrayHelper.php';

直接就行了,对yii的文件不需要改动,调用的地方也不用改动。

可能是我在做fecshop考虑重写的事情,看到这个,真的豁然开朗的感觉,
这样可以在不改动yii2文件,和不改动我的fecshop文件的前提下,重写任何文件了

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

下面是代码举例说明的详细步骤:

  1. 下面是我写的一个类,内容如下:
<?php
/**
 * FecShop file.
 *
 * @link http://www.fecshop.com/
 * @copyright Copyright (c) 2016 FecShop Software LLC
 * @license http://www.fecshop.com/license/
 */
namespace fecshop\app\appfront\helper\test;
use Yii;
use fec\helpers\CConfig;
use fec\controllers\FecController;
use yii\base\InvalidValueException;
/**
 * @author Terry Zhao <2358269014@qq.com>
 * @since 1.0
 */
class My
{
  
  public static function test(){
    echo 'this is my first test php file';
  }
  
}

2. 然后我在controller中对这个类的静态方法进行了调用:

<?php
/**
 * FecShop file.
 *
 * @link http://www.fecshop.com/
 * @copyright Copyright (c) 2016 FecShop Software LLC
 * @license http://www.fecshop.com/license/
 */
namespace fecshop\app\appfront\modules\Customer\controllers;
use Yii;
use fec\helpers\CModule;
use fec\helpers\CRequest;
use fecshop\app\appfront\modules\AppfrontController;
use fecshop\app\appfront\helper\test\My;
/**
 * @author Terry Zhao <2358269014@qq.com>
 * @since 1.0
 */
class AccountController extends AppfrontController
{
  
  public function actionLogin()
    {
    My::test();
    exit;
  }
}

然后我在很多地方对My::test进行了调用,然后我想对这个My类的test的内容进行重写,但是前提是My这个文件是库包文件,我不能直接进行修改,否则,以后的升级会出现问题,那么我需要用一个类继承这个My类,然后重写test()方法,然后在各个调用My::test()的地方修改use部分,改成新的类的namespaces,这种方式的坏处是修改量大,对于维护起来很费劲,下面介绍另外一种方法,通过在Yii::classMap中配置:

Yii::$classMap['yii\helpers\ArrayHelper'] = '@app/components/ArrayHelper.php';

官网部分的介绍为:

http://www.yiiframework.com/doc-2.0/guide-helper-overview.html#customizing-helper-classes

下面是代码步骤:

1.原来的类的内容为:

<?php
/**
 * FecShop file.
 *
 * @link http://www.fecshop.com/
 * @copyright Copyright (c) 2016 FecShop Software LLC
 * @license http://www.fecshop.com/license/
 */
namespace fecshop\app\appfront\helper\test;
use Yii;
use fec\helpers\CConfig;
use fec\controllers\FecController;
use yii\base\InvalidValueException;
/**
 * @author Terry Zhao <2358269014@qq.com>
 * @since 1.0
 */
class My
{
  
  public static function test(){
    echo 'this is my first test php file';
  }
  
}

2.我写一个新类: 文件路径为: appfront/helper/My.php ,我想让controller调用的类为下面的类

<?php
/**
 * FecShop file.
 *
 * @link http://www.fecshop.com/
 * @copyright Copyright (c) 2016 FecShop Software LLC
 * @license http://www.fecshop.com/license/
 */
namespace fecshop\app\appfront\helper\test;
use Yii;
use fec\helpers\CConfig;
use fec\controllers\FecController;
use yii\base\InvalidValueException;
/**
 * @author Terry Zhao <2358269014@qq.com>
 * @since 1.0
 */
class My{
  
  public static function test(){
    echo 'this is my appfront test php file';
  }
  
}

注意:namespace和上面的那个My类的要一样,而不是按照 appfront/helper/My.php 写成 namespace appfront\helper  ,这样会报错的。

3. 我添加Yii::classMap  数组的值的新的My类的文件路径

Yii::$classMap['fecshop\app\appfront\helper\test\My'] = ['@appfront/helper/My.php'];

然后调用后,发现调用的是新的My类。

4. 需要注意的是,新的类的名字必须和之前的类的名字一样,否则会出错,另外,namespace要一致,一样。

5. 我们希望通过配置文件的方式,这样比较方面,我们可以这样做。

5.1 在app/config/下面添加文件 YiiClassMap.php ,内容如下:

<?php
return [
  'fecshop\app\appfront\helper\test\My' => '@appfront/helper/My.php',   
  
];

在web/index.php的代码

$application = new yii\web\Application($config);   上面添加代码:

/**
 * yii class Map Custom
 * 
 */ 
$yiiClassMap = require(__DIR__ . '/../config/YiiClassMap.php');
if(is_array($yiiClassMap) && !empty($yiiClassMap)){
  foreach($yiiClassMap as $namespace => $filePath){
    Yii::$classMap[$namespace] = $filePath;
  }
}

这样,通过上面的配置文件,就可以把classMap执行了,以后如果添加classMap,直接在文件

app/config/YiiClassMap.php 文件里面的数组中添加一条数据就可以了。

您也可以把yii2的库包文件,yii2的某个扩展库包里面的几个文件,通过这种方式进行重写。这个是非常非常非常方便的,尤其对于你写了一个扩展,让大家用,你的扩展需要升级,因此别人不能直接动你的库包文件,不然升级后,修改的会被覆盖,通过这种方式就可以解决这个问题。

总之,这个功能是更好的进行文件重写。

 

对于Yii2的自动加载的原理,可以参看:http://www.digpage.com/autoload.html,这里不多写了。

vagrant 下载部署linux环境

1. 安装 VirtualBox

虚拟机还是得依靠 VirtualBox 来搭建,免费小巧
下载地址:https://www.virtualbox.org/wiki/Downloads

* 虽然 Vagrant 也支持 VMware,不过 VMware 是收费的,对应的 Vagrant 版本也是收费的。

我下载的是:VirtualBox 5.1.6 for Windows hosts  x86/amd64


2. 下载  Vagrant

下载地址:http://downloads.vagrantup.com/

3.下载contos box,可以来这里下载:http://www.vagrantbox.es/

 

经过上面的下载,我们

下载了virtual box   vagrant   centos 6.6 box  三个文件

4.安装 virtualbox ,  vagrant ,这个基本都是下一步,安装完成后要重启

安装上面的两个成功后,重启后。

4.1

window建+r ,打开命令行,

进入命令行模式,输入vagrant,看看是否安装成功

4.2 添加centos box

进入d盘,添加centos box

进入contos box文件所在的文件夹,我的是在d:\vagrant文件路径下,按照上面截图的命令行进入d盘,在通过cd vagrant 进入相应文件夹。

D:\vagrant\centos-6.6-x86_64.box

按照这个命令添加一个box

vagrant box add 名称 路径

譬如:

D:\vagrant\vagrant box add centos-6.6-x86_64   centos-6.6-x86_64.box

这里将  box 名称:centos-6.6-x86_64  对应 D:\vagrant\vagrant\centos-6.6-x86_64.box这个文件,后面可以使用centos-6.6-x86_64,使用后直接对应上面对应的文件。

通过vagrant box list  查看添加的列表

4.3创建虚拟机:

添加了 Box 以后,我们就可以用 Vagrant 基于这个 Box 去创建虚拟机了。先找个地方去创建一个目录,这个目录就是你的项目所在的目录,它会自动跟虚拟机上的某个目录同步,也就是在你电脑上的这个目录里面的文 件,你同样可以在虚拟机里的某个目录里面找到。比如我的目录在d:\myvagrant,我创建这个目录,然后再进入到这个目录,在命令行工具下面执行:

初始化:

启动 vagrant up命令,第一次会慢一些,因为要复制文件。

在上面,发现了拨错:

Timed out while waiting for the machine to boot. This means that  
    Vagrant was unable to communicate with the guest machine within  
    the configured ("config.vm.boot_timeout" value) time period.  
  
    If you look above, you should be able to see the error(s)  that Vagrant had when attempting to connect to the machine. These errors are usually good hints as to what may be wrong.  
  
    If you're using a custom box, make sure that networking is properly working and you're able to connect to the machine. It is a common problem that networking isn't setup properly in these boxes. Verify that authentication configurations are also setup properly,as well.  
  
    If the box appears to be booting properly, you may want to increase the timeout ("config.vm.boot_timeout") value.

 

打开文件:D:\myvagrant\Vagrantfile

将Vagrantfile配置文件中vb.gui = true的注释去掉,下面将三行的代码的注释去掉了,也就是前面的  # 号,而不是一行,这里要注意,如果仅仅去掉vb.gui = true的注释,会报错。

config.vm.provider "virtualbox" do |vb|  
#   # Don't boot with headless mode  
    vb.gui = true  
#  
#   # Use VBoxManage to customize the VM. For example to change     memory:  
#   vb.customize ["modifyvm", :id, "--memory", "1024"]  
 end

然后关闭  vagrant  halt  ,重启,查看报错。
运行vagrant up 启动 virtualbox 后,GUI会给出提示:

VT-x/AMD-V硬件加速在您的系统中不可用。您的64-位虚拟机将无法检测到 64-位处理器,从而无法启动。  
这是由于在BOIS中没有开启cpu虚拟化支持,重启F2或F10等进入BIOS设置Virtualization为Enable(我的Thinkpad是Security=>Virtualizatio设置为Enable);

电脑重启后,再次vagrant up启动虚拟机还是有一些问题,当时也没有记录下来错误信息,只记得解决方案是使用vagrant destroy将虚拟机从磁盘中删除,然后使用vagrant up命令重新创建。

重启后,换一个文件路径,我原来的是d:\myvagrant,我换到了另外一个路径d:\disk\va1

上面出现waining,是没有问题的,现在我们可以通过ssh直连了。

查看vagrant的状态  vagrant status

关闭VM     vagrant halt

在启动  vagrant up, 第二次启动会比较快,因为不会复制文件,但是还是很慢,哈哈,多等下就好了。

ssh 连接

ip:127.0.0.1

端口:2222

用户名:vagrant

密码:vagrant

root的密码也是vagrant  ,上面连接一定要注意,更改ssh的默认端口22 为 2222

ssh 登录后,我发现语言是德语的,日,使用root账号登录,更改语言

 

vim /etc/sysconfig/i18n

语言改成英语

LANG="en_US.UTF-8"  
SYSFONT="latarcyrheb-sun16"

重启一下linux

查看centos 版本:

[root@localhost ~]# cat /etc/redhat-release  
CentOS release 6.6 (Final)  
[root@localhost ~]#

 

到此为止,初始环境搭配好了,下面需要安装 lnmp环境了。

具体参看下一节

vagrant 配置

Vagrant 初始化成功后,会在初始化的目录里生成一个 Vagrantfile 的配置文件,可以修改配置文件进行个性化的定制。

Vagrant 默认是使用端口映射方式将虚拟机的端口映射本地从而实现类似 http://localhost:80 这种访问方式,这种方式比较麻烦,新开和修改端口的时候都得编辑。

相比较而言,host-only 模式显得方便多了。打开 Vagrantfile,将下面这行的注释去掉(移除 #)并保存:

config.vm.network :private_network, ip: "192.168.33.10"

重启虚拟机,这样我们就能用 192.168.33.10 访问这台机器了,你可以把 IP 改成其他地址,只要不产生冲突就行。

注意:对于端口映射一定要配置,譬如:

config.vm.network “forwarded_port”, guest: 80, host: 80

不然,在开启nginx后,通过127.0.0.1是无法访问的,这个一定不要忘记

我暂时采用的是单个匹配的方式:如下:

# -*- mode: ruby -*-
# vi: set ft=ruby :

# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.
Vagrant.configure("2") do |config|
  # The most common configuration options are documented and commented below.
  # For a complete reference, please see the online documentation at
  # https://docs.vagrantup.com.

  # Every Vagrant development environment requires a box. You can search for
  # boxes at https://atlas.hashicorp.com/search.
  config.vm.box = "centos6.6"

  # Disable automatic box update checking. If you disable this, then
  # boxes will only be checked for updates when the user runs
  # `vagrant box outdated`. This is not recommended.
  # config.vm.box_check_update = false

  # Create a forwarded port mapping which allows access to a specific port
  # within the machine from a port on the host machine. In the example below,
  # accessing "localhost:8080" will access port 80 on the guest machine.
  config.vm.network "forwarded_port", guest: 8080, host: 8080
  config.vm.network "forwarded_port", guest: 80, host: 80
  config.vm.network "forwarded_port", guest: 2001, host: 2001
  config.vm.network "forwarded_port", guest: 2030, host: 2030
  
  config.vm.network "forwarded_port", guest: 2030, host: 2030
  config.vm.network "forwarded_port", guest: 2020, host: 2020
  config.vm.network "forwarded_port", guest: 2010, host: 2010
  config.vm.network "forwarded_port", guest: 2000, host: 2000
  config.vm.network "forwarded_port", guest: 1000, host: 1000
  config.vm.network "forwarded_port", guest: 1010, host: 1010
  #config.vm.network :private_network, ip: "192.168.111.1"
  # Create a private network, which allows host-only access to the machine
  # using a specific IP.
  # config.vm.network "private_network", ip: "192.168.33.10"

  # Create a public network, which generally matched to bridged network.
  # Bridged networks make the machine appear as another physical device on
  # your network.
  # config.vm.network "public_network"

  # Share an additional folder to the guest VM. The first argument is
  # the path on the host to the actual folder. The second argument is
  # the path on the guest to mount the folder. And the optional third
  # argument is a set of non-required options.
  # config.vm.synced_folder "../data", "/vagrant_data"

  # Provider-specific configuration so you can fine-tune various
  # backing providers for Vagrant. These expose provider-specific options.
  # Example for VirtualBox:
  #
  # config.vm.provider "virtualbox" do |vb|
  #   # Display the VirtualBox GUI when booting the machine
  #   vb.gui = true
  #
  #   # Customize the amount of memory on the VM:
  #   vb.memory = "1024"
  # end
  #
  # View the documentation for the provider you are using for more
  # information on available options.

  # Define a Vagrant Push strategy for pushing to Atlas. Other push strategies
  # such as FTP and Heroku are also available. See the documentation at
  # https://docs.vagrantup.com/v2/push/atlas.html for more information.
  # config.push.define "atlas" do |push|
  #   push.app = "YOUR_ATLAS_USERNAME/YOUR_APPLICATION_NAME"
  # end

  # Enable provisioning with a shell script. Additional provisioners such as
  # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the
  # documentation for more information about their specific syntax and use.
  # config.vm.provision "shell", inline: <<-SHELL
  #   apt-get update
  #   apt-get install -y apache2
  # SHELL
end

6. 打包分发

当你配置好开发环境后,退出并关闭虚拟机。在终端里对开发环境进行打包:

vagrant package

打包完成后会在当前目录生成一个 package.box 的文件,将这个文件传给其他用户,其他用户只要添加这个 box 并用其初始化自己的开发目录就能得到一个一模一样的开发环境了。

这个box文件,如果给了其他人用,先通过下面的命令行添加

vagrant box add  fecshop  fecshop.box

 

然后,和上面一样,找一个新的文件夹,

vagrant init fecshop

执行成功后,启动

vagrant up

 

另外需要注意的是,需要把 Vagrantfile 这个里面的配置复制到新的地方,配置一致才可以。这样就完成了分发。
7. 常用命令

vagrant init  # 初始化
vagrant up  # 启动虚拟机
vagrant halt  # 关闭虚拟机
vagrant reload  # 重启虚拟机
vagrant ssh  # SSH 至虚拟机
vagrant status  # 查看虚拟机运行状态
vagrant destroy  # 销毁当前虚拟机

更多内容请查阅官方文档 http://docs.vagrantup.com/v2/cli/index.html
8. 注意事项

使用 Apache/Nginx 时会出现诸如图片修改后但页面刷新仍然是旧文件的情况,是由于静态文件缓存造成的。需要对虚拟机里的 Apache/Nginx 配置文件进行修改:

# Apache 配置添加:
EnableSendfile off

# Nginx 配置添加:
sendfile off;

 

其他问题:

问题2: vagrant启动报错The following SSH command responded with a non-zero exit status.

The following SSH command responded with a non-zero exit status.  
Vagrant assumes that this means the command failed!  
  
ARPCHECK=no /sbin/ifup eth1 2> /dev/null  
  
Stdout from the command:  
  
Device eth1 does not seem to be present, delaying initialization.  
  
Stderr from the command:

解决方案
虽然vagrant up启动报错,但是vagrant ssh还是能登陆虚拟机的,进入虚拟机后,执行如下命令

sudo rm -f /etc/udev/rules.d/70-persistent-net.rules

对, 问题就处在在持久网络设备udev规则(persistent network device udev rules)是被原VM设置好的,再用box生成新VM时,这些rules需要被更新。而这和Vagrantfile里对新VM设置private network的指令发生冲突。删除就好了。
vagrant reload 再次启动就OK。

问题3:在配置好vagrant和虚拟机中的nginx后,当你通过宿主机访问虚拟机中的web站点时,出现页面一直在加载,无法正常显示页面
(此块由程序员 Young 614168741 添加)
解决方法:因为centos7.0(含7.0)及以上版本会默认安装firewalld防火墙,firewalld有zone的概念默认在public区,只能指定外部连接进入,首先systemctl status firewalld 查看防火墙状态,active:running表示开启转态,然后我们firewall-cmd –zone=public –add-port=80/tcp –permanent,或者firewall-cmd –zone=public –add-port=8080/tcp –permanent(取决于你的Vagrantfile端口端口那一个是映射到虚拟机的80),执行完,firewall-cmd –zone=public –list-ports检查下public区上的端口,出现刚才添加的端口说明成功,再firewall-cmd –reload重启下firewalld,这个时候通过宿主机通过制定的配置的端口访问虚拟机站点应该就可以了

yii2 AR 结构进行数据类型转换的原理(PDO)

通过PDO,默认出来的数据都是string类型,在Yii2的AR(Active Record)中,按照类型进行了数据转换,但是如果查询的时候使用了asArray(),则不会进行转换,也就是说

User::find()->asArray()->all()

查询的结果,包括id在内的所有结果,都是字符串类型

User::find()->all() ,返回的是对象数组,结果会进行类型转换

ActiveRecord转换的原理为下面:

yii\db\ActiveRecord

public static function populateRecord($record, $row)
   {
       $columns = static::getTableSchema()->columns;
       foreach ($row as $name => $value) {
           if (isset($columns[$name])) {
               $row[$name] = $columns[$name]->phpTypecast($value);
           }
       }
       parent::populateRecord($record, $row);
   }

通过上面的方法进行了类型的转换,也就是phpTypecast方法。

 

如果查询的时候加上了asArray(),则不会进行类型转换,出来的数据都是String

譬如代码:

$data = \fecadmin\models\AdminMicroRoleBrand::find()->asArray()
          ->where([
            'in','role_id',$micro_role_arr
          ])->all();

出来的是数据都是字符串

如果按照下面的代码,查询出来的对象里面的属性都是相应的数据库的类型:

$data = \fecadmin\models\AdminMicroRoleBrand::find()
          ->where([
            'in','role_id',$micro_role_arr
          ])->all();

所以,在加入asArray()查询的时候,速度是最快的,但是要注意一下数据类型,适当的时候需要手动转换。

Yii2 随机发生 php mongo报错:’Failed to connect to: XXXXX: Remote server has closed the connection’的解决方法

 

运行mongodb连接的时候,遇到了 Remote server has closed the connection 的错误问题。而且是随机性发生的。

在Yii2中,需要进行的修改为:

Yii2-mongodb/Connecting.php

修改函数:

public function open()
   {
       if ($this->mongoClient === null) {
           if (empty($this->dsn)) {
               throw new InvalidConfigException($this->className() . '::dsn cannot be empty.');
           }
           $token = 'Opening MongoDB connection: ' . $this->dsn;
           try {
               Yii::trace($token, __METHOD__);
               Yii::beginProfile($token, __METHOD__);
               $options = $this->options;
               $options['connect'] = true;
               if ($this->defaultDatabaseName !== null) {
                   $options['db'] = $this->defaultDatabaseName;
               }
              // $this->mongoClient = new \MongoClient($this->dsn, $options);
         $this->mongoClient = $this->getCustomMongoClient($options);
               $this->initConnection();
               Yii::endProfile($token, __METHOD__);
           } catch (\Exception $e) {
               Yii::endProfile($token, __METHOD__);
               throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
           }
       }
   }

 

添加函数

public function getCustomMongoClient($options,$retry = 4) {
    try {
      return new \MongoClient($this->dsn, $options);
    } catch(Exception $e) {
      /* Log the exception so we can look into why mongod failed later */
      //logException($e);
    }
    if ($retry > 0) {
      return $this->getCustomMongoClient($options, --$retry);
    }
    throw new Exception("I've tried several times getting MongoClient.. Is mongod really running?");
  }

也就是通过多次尝试连接的方式。