2017-03-24 15 views
4

Laravel 5.3私は既にプロダクションサーバーをセットアップしています。すべてのDB移行は、すでに次のデータベース設定を使用して作成された:Laravel MYSQLをutf8mb4に変更して既存のデータベースでEmojiをサポートする

'mysql' => [ 
      'driver' => 'mysql', 
      'host' => env('DB_HOST', 'localhost'), 
      'port' => env('DB_PORT', '3306'), 
      'database' => env('DB_DATABASE', 'forge'), 
      'username' => env('DB_USERNAME', 'forge'), 
      'password' => env('DB_PASSWORD', ''), 
      'charset' => 'utf8', 
      'collation' => 'utf8_unicode_ci', 
      'prefix' => '', 
      'strict' => true, 
      'engine' => null, 
     ], 

しかし、今は、私のユーザーの一部は、彼らがそれらに絵文字アイコンを持つフォームを保存しようとすると、彼らはエラーを取得することを報告していました。

'mysql' => [ 
      'driver' => 'mysql', 
      'host' => env('DB_HOST', 'localhost'), 
      'port' => env('DB_PORT', '3306'), 
      'database' => env('DB_DATABASE', 'forge'), 
      'username' => env('DB_USERNAME', 'forge'), 
      'password' => env('DB_PASSWORD', ''), 
      'charset' => 'utf8mb4', 
      'collation' => 'utf8mb4_unicode_ci', 
      'prefix' => '', 
      'strict' => true, 
      'engine' => null, 
     ], 

これは本番サーバにあるので、私はmigrate:refreshを行うことはできません。検索した後、私は私が私の設定ではなく、このような何かされている必要があり、これが動作するためにutf8mb4にMySQLの文字セットを設定する必要があることが分かりました。だから、私の質問は以下のとおりです。

  1. どのように私は私が代わりにutf8utf8mb4を使用しても同じでlaravelを更新するlaravelの移行を使用して作成した既存のデータベースを変更できますか?これを行う簡単な方法はありますか?
  2. 上記の場合は、すべてのテーブルに対してutf8mb4を設定する方がよいでしょう。または、私が実際に絵文字を使用する2つのテーブルの列に対してのみ使用してください。

ありがとうございました。

答えて

4
  1. 使用生のMySQLのクエリは、更新テーブルの移行スクリプトを書いて、php artisan migrateコマンド

    use Illuminate\Database\Migrations\Migration; 
    
    class UpdateTableCharset extends Migration { 
    
        /** 
        * Run the migrations. 
        * 
        * @return void 
        */ 
        public function up() { 
          DB::unprepared('ALTER TABLE `table_name` CONVERT TO CHARACTER SET utf8mb4'); 
        } 
    
        /** 
        * Reverse the migrations. 
        * 
        * @return void 
        */ 
        public function down() { 
          DB::unprepared('ALTER TABLE `table_name` CONVERT TO CHARACTER SET utf8'); 
        } 
    } 
    
  2. 私の個人的な好み、更新テーブルを実行します。私はそれが良いと言う証拠を持っていません

注:まだデータベースの設定をutf8mb4にしておく必要があります。

希望これは私自身がそのようなスクリプトを必要としたデータベース全体でこれを達成するために喜んで、誰のためにあなたに

+0

@Sauminiありがとうございました。私は今それを与えるだろう。 – Neel

2

を助ける:@ insomniak-devの答えに基づいて

<?php 

use Illuminate\Support\Facades\Schema; 
use Illuminate\Database\Schema\Blueprint; 
use Illuminate\Database\Migrations\Migration; 
use Illuminate\Support\Facades\DB; 
use Illuminate\Support\Facades\Config; 

class ChangeDbCharset extends Migration 
{ 

    /** 
    * Run the migrations. 
    * 
    * @return void 
    */ 
    public function up() 
    { 
     $charset = "utf8mb4"; 
     $collate = $charset."_unicode_ci"; 
     $dbName = Config::get('database.connections.'.Config::get('database.default').'.database'); 
     $query = "ALTER SCHEMA $dbName DEFAULT CHARACTER SET $charset DEFAULT COLLATE $collate;\n"; 
     DB::connection()->getPdo()->exec($query); 

     $dbName = Config::get('database.connections.'.Config::get('database.default').'.database'); 
     $result = DB::select(DB::raw('show tables')); 
     $test = DB::select(DB::raw("select * from INFORMATION_SCHEMA.COLUMNS where DATA_TYPE = 'varchar' AND TABLE_SCHEMA = '$dbName';")); 
     //var_dump($test); 
     foreach($test as $t) 
     { 
      $query = "ALTER TABLE $t->TABLE_NAME CHANGE $t->COLUMN_NAME $t->COLUMN_NAME VARCHAR(191) CHARACTER SET $charset COLLATE $collate; \n"; 
      echo $query; 
      DB::connection()->getPdo()->exec($query); 
     } 
     $test = DB::select(DB::raw("select * from INFORMATION_SCHEMA.COLUMNS where DATA_TYPE = 'text' AND TABLE_SCHEMA = '$dbName';")); 
     foreach($test as $t) 
     { 
      $query = "ALTER TABLE $t->TABLE_NAME CHANGE $t->COLUMN_NAME $t->COLUMN_NAME TEXT CHARACTER SET $charset COLLATE $collate; \n"; 
      echo $query; 
      DB::connection()->getPdo()->exec($query); 
     } 


     $result = DB::select(DB::raw('show tables')); 
     foreach($result as $r) 
     { 
      foreach($r as $k => $t) 
      { 
       $query = "ALTER TABLE `$t` CONVERT TO CHARACTER SET $charset COLLATE $collate; \n"; 
       echo $query; 
       DB::connection()->getPdo()->exec($query); 
      } 
     } 
     echo "DB CHARSET set to $charset , $collate"; 
    } 

    /** 
    * Reverse the migrations. 
    * 
    * @return void 
    */ 
    public function down() 
    { 
     $charset = "utf8"; 
     $collate = $charset."_unicode_ci"; 
     $dbName = Config::get('database.connections.'.Config::get('database.default').'.database'); 
     $query = "ALTER SCHEMA $dbName DEFAULT CHARACTER SET $charset DEFAULT COLLATE $collate;\n"; 
     DB::connection()->getPdo()->exec($query); 

     $dbName = Config::get('database.connections.'.Config::get('database.default').'.database'); 
     $result = DB::select(DB::raw('show tables')); 
     $test = DB::select(DB::raw("select * from INFORMATION_SCHEMA.COLUMNS where DATA_TYPE = 'varchar' AND TABLE_SCHEMA = '$dbName';")); 
     //var_dump($test); 
     foreach($test as $t) 
     { 
      $query = "ALTER TABLE $t->TABLE_NAME CHANGE $t->COLUMN_NAME $t->COLUMN_NAME VARCHAR(255) CHARACTER SET $charset COLLATE $collate; \n"; 
      echo $query; 
      DB::connection()->getPdo()->exec($query); 
     } 
     $test = DB::select(DB::raw("select * from INFORMATION_SCHEMA.COLUMNS where DATA_TYPE = 'text' AND TABLE_SCHEMA = '$dbName';")); 
     foreach($test as $t) 
     { 
      $query = "ALTER TABLE $t->TABLE_NAME CHANGE $t->COLUMN_NAME $t->COLUMN_NAME TEXT CHARACTER SET $charset COLLATE $collate; \n"; 
      echo $query; 
      DB::connection()->getPdo()->exec($query); 
     } 


     $result = DB::select(DB::raw('show tables')); 
     foreach($result as $r) 
     { 
      foreach($r as $k => $t) 
      { 
       $query = "ALTER TABLE `$t` CONVERT TO CHARACTER SET $charset COLLATE $collate; \n"; 
       echo $query; 
       DB::connection()->getPdo()->exec($query); 
      } 
     } 
     echo "DB CHARSET set to $charset , $collate"; 
    } 

} 
1

、だけ縮小されます制限を超えたときにvarcharがインデックスに使用されます。それ以外の場合は変換されますが、サイズはそのままです。列が縮小されると、データが切り捨てられるかどうかが確認されます。

これはすべてのテキストタイプを処理し、各テーブルのすべての変換を1つのステートメントにまとめてバッチ処理します。

Dryrunフラグは、直接適用する代わりに使用可能なsqlを出力します。

/** 
* Run the migrations. 
* 
* @return void 
*/ 
public function up() 
{ 
    $dryRun = true; 
    $this->convertDb('mysql', 'utf8mb4', 'utf8mb4_unicode_ci', $dryRun); 
    $this->convertDb('archive', 'utf8mb4', 'utf8mb4_unicode_ci', $dryRun); 
} 

/** 
* Reverse the migrations. 
* 
* @return void 
*/ 
public function down() 
{ 
    $dryRun = true; 
    $this->convertDb('archive', 'utf8', 'utf8_unicode_ci', $dryRun); 
    $this->convertDb('mysql', 'utf8', 'utf8_unicode_ci', $dryRun); 
} 

private function convertDb($connection, $charset, $collate, $dryRun) 
{ 
    $dbName = config("database.connections.{$connection}.database"); 

    $varchars = \DB::connection($connection) 
     ->select(\DB::raw("select * from INFORMATION_SCHEMA.COLUMNS where DATA_TYPE = 'varchar' and (CHARACTER_SET_NAME != '{$charset}' or COLLATION_NAME != '{$collate}') AND TABLE_SCHEMA = '{$dbName}'")); 
    // Check if shrinking field size will truncate! 
    $skip = []; // List of table.column that will be handled manually 
    $indexed = []; 
    if ($charset == 'utf8mb4') { 
     $error = false; 
     foreach($varchars as $t) { 
      if ($t->CHARACTER_MAXIMUM_LENGTH > 191) { 
       $key = "{$t->TABLE_NAME}.{$t->COLUMN_NAME}"; 

       // Check if column is indexed 
       $index = \DB::connection($connection) 
        ->select(\DB::raw("SHOW INDEX FROM `{$t->TABLE_NAME}` where column_name = '{$t->COLUMN_NAME}'")); 
       $indexed[$key] = count($index) ? true : false; 

       if (count($index)) { 
        $result = \DB::connection($connection) 
         ->select(\DB::raw("select count(*) as `count` from `{$t->TABLE_NAME}` where length(`{$t->COLUMN_NAME}`) > 191")); 
        if ($result[0]->count > 0) { 
         echo "-- DATA TRUNCATION: {$t->TABLE_NAME}.{$t->COLUMN_NAME}({$t->CHARACTER_MAXIMUM_LENGTH}) => {$result[0]->count}" . PHP_EOL; 
         if (!in_array($key, $skip)) { 
          $error = true; 
         } 
        } 
       } 
      } 
     } 
     if ($error) { 
      throw new \Exception('Aborting due to data truncation'); 
     } 
    } 

    $query = "SET FOREIGN_KEY_CHECKS = 0"; 
    $this->dbExec($query, $dryRun, $connection); 

    $query = "ALTER SCHEMA {$dbName} DEFAULT CHARACTER SET {$charset} DEFAULT COLLATE {$collate}"; 
    $this->dbExec($query, $dryRun, $connection); 

    $tableChanges = []; 
    foreach($varchars as $t) { 
     $key = "{$t->TABLE_NAME}.{$t->COLUMN_NAME}"; 
     if (!in_array($key, $skip)) { 
      if ($charset == 'utf8mb4' && $t->CHARACTER_MAXIMUM_LENGTH > 191 && $indexed["{$t->TABLE_NAME}.{$t->COLUMN_NAME}"]) { 
       $tableChanges["{$t->TABLE_NAME}"][] = "CHANGE `{$t->COLUMN_NAME}` `{$t->COLUMN_NAME}` VARCHAR(191) CHARACTER SET {$charset} COLLATE {$collate}"; 
       echo "-- Shrinking: {$t->TABLE_NAME}.{$t->COLUMN_NAME}({$t->CHARACTER_MAXIMUM_LENGTH})" . PHP_EOL; 
      } else if ($charset == 'utf8' && $t->CHARACTER_MAXIMUM_LENGTH == 191) { 
       $tableChanges["{$t->TABLE_NAME}"][] = "CHANGE `{$t->COLUMN_NAME}` `{$t->COLUMN_NAME}` VARCHAR(255) CHARACTER SET {$charset} COLLATE {$collate}"; 
       echo "-- Expanding: {$t->TABLE_NAME}.{$t->COLUMN_NAME}({$t->CHARACTER_MAXIMUM_LENGTH})"; 
      } else { 
       $tableChanges["{$t->TABLE_NAME}"][] = "CHANGE `{$t->COLUMN_NAME}` `{$t->COLUMN_NAME}` VARCHAR({$t->CHARACTER_MAXIMUM_LENGTH}) CHARACTER SET {$charset} COLLATE {$collate}"; 
      } 
     } 
    } 

    $texts = \DB::connection($connection) 
     ->select(\DB::raw("select * from INFORMATION_SCHEMA.COLUMNS where DATA_TYPE like '%text%' and (CHARACTER_SET_NAME != '{$charset}' or COLLATION_NAME != '{$collate}') AND TABLE_SCHEMA = '{$dbName}'")); 
    foreach($texts as $t) { 
     $tableChanges["{$t->TABLE_NAME}"][] = "CHANGE `{$t->COLUMN_NAME}` `{$t->COLUMN_NAME}` {$t->DATA_TYPE} CHARACTER SET {$charset} COLLATE {$collate}"; 
    } 

    $tables = \DB::connection($connection) 
     ->select(\DB::raw("select * from INFORMATION_SCHEMA.TABLES where TABLE_COLLATION != '{$collate}' and TABLE_SCHEMA = '{$dbName}';")); 
    foreach($tables as $t) { 
     $tableChanges["{$t->TABLE_NAME}"][] = "CONVERT TO CHARACTER SET {$charset} COLLATE {$collate}"; 
     $tableChanges["{$t->TABLE_NAME}"][] = "DEFAULT CHARACTER SET={$charset} COLLATE={$collate}"; 
    } 

    foreach ($tableChanges as $table => $changes) { 
     $query = "ALTER TABLE `{$table}` ".implode(",\n", $changes); 
     $this->dbExec($query, $dryRun, $connection); 
    } 

    $query = "SET FOREIGN_KEY_CHECKS = 1"; 
    $this->dbExec($query, $dryRun, $connection); 

    echo "-- {$dbName} CONVERTED TO {$charset}-{$collate}" . PHP_EOL; 
} 

private function dbExec($query, $dryRun, $connection) 
{ 
    if ($dryRun) { 
     echo $query . ';' . PHP_EOL; 
    } else { 
     \DB::connection($connection)->getPdo()->exec($query); 
    } 
} 
関連する問題