20201017のMySQLに関する記事は8件です。

【MySQL】テーブルを作成して、CSVファイルからデータをインポートする方法

CSVファイルのデータをDBに突っ込む方法を調べたので備忘録的まとめ。

ちなみに、突っ込みたいCSVファイルはこんな感じ。

user_id install_datetime delete_flg
10001 2016-03-19 15:45:00 0
10002 2016-03-21 12:30:00 1
10003 2016-03-23 09:15:00 0
10004 2016-03-25 06:00:00 0
10005 2016-04-02 10:00:00 1
10006 2016-04-02 12:50:00 0
10007 2016-04-30 15:00:00 0

1.テーブル作成

mysql> create table user_master(
    -> user_id int,
    -> install_datetime timestamp,
    -> delete_flg int,
    -> primary key(user_id)
    -> );
Query OK, 0 rows affected (0.01 sec)

今回自分が必要なテーブルを作成した。
テーブル作成方法についてはググればたくさん出てくるのでそちらを参照のこと。

2.テーブルの確認

mysql> show tables;
+------------------+
| Tables_in_testdb |
+------------------+
| user_master      | // テーブルができている
+------------------+
1 row in set (0.01 sec)

3.テーブルの中身を確認

mysql> select * from user_master;
Empty set (0.00 sec)

今はまだ何も入っていない

4.CSVファイルをテーブルにインポート

mysql> load data local infile "Downloads/user_master.csv" // インポートするファイルのパス
    -> into table user_master // 突っ込むテーブル名
    -> fields terminated by ',' // 何でデータを区切るか(CSVの場合は",")
    -> ;
Query OK, 8 rows affected, 10 warnings (0.02 sec)
Records: 8  Deleted: 0  Skipped: 0  Warnings: 10

5.テーブルを確認

mysql> select * from user_master;
+---------+---------------------+------------+
| user_id | install_datetime    | delete_flg |
+---------+---------------------+------------+
|   10001 | 2016-03-19 15:45:00 |          0 |
|   10002 | 2016-03-21 12:30:00 |          1 |
|   10003 | 2016-03-23 09:15:00 |          0 |
|   10004 | 2016-03-25 06:00:00 |          0 |
|   10005 | 2016-04-02 10:00:00 |          1 |
|   10006 | 2016-04-02 12:50:00 |          0 |
|   10007 | 2016-04-30 15:00:00 |          0 |
+---------+---------------------+------------+
8 rows in set (0.00 sec)

これでデータが入った。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【初心者向け】MySQLでローカルにDBを作成する方法

MySQLを勉強するためにローカルでDBを作成する方法を調べたので、備忘録的まとめ。
※MySQLが使える状態であることが前提。

1.MySQLを起動

$ mysql.server start
Starting MySQL
 SUCCESS! 

2.rootユーザーでログイン

2020-10-17T13:43:42.6NZ mysqld_safe A mysqld process already exists
mysql -u root // これを入力する
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.7.31 Homebrew

Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

3.DBを作成

mysql> create database testDB; // これを入力する
Query OK, 1 row affected (0.01 sec)

4.DBを確認

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| testDB             | // 作成したDBが入っている
+--------------------+
7 rows in set (0.00 sec)

5.DBを選択する

mysql> use testDB; // これを入力する
Database changed

6.テーブルを確認

mysql> show tables; // これを入力する
Empty set (0.00 sec)

→DBを作成しただけで、中身は空の状態

ここまでで、DBの作成は完了。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【PHP】初めてのマイグレーション

Laravelでのモデル作成とマイグレーションファイル編集に関する備忘録。

■ モデル作成

モデル作成(※モデル名は、大文字から始まる単数形)
% php artisan make:model モデル名             // app直下にファイル生成
% php artisan make:model Entities/モデル名    // app/Entities下にファイル生成される
% php artisan make:model モデル名 -m -c -r    // モデル、マイグレーション、コントローラーを一気に生成

% php artisan make:model -h                 // どんなオプションがあるか?
  • マイグレーションクラスは、2つのメソッドがある(up、down)。
    • upメソッド : マイグレーション実行時の処理 を記述。
    • downメソッド : ロールバック時の処理を記述(upメソッドの逆)。
作成されるマイグレーションファイルのざっくりイメージ(database/migrations/2020_10_15_012345_create_xxxxx_table.php)
public function up()           // マイグレーション実行時の処理は、up() に記述
  {
    Schema::create('xxxxxs', function (Blueprint $table) {
      $table->increments('id');        // デフォルト
      $table->データ型('カラム名');        // カラム作成
      $table->tinyInteger('gender')->unsigned()->comment('性別 1:男、2:女');
      $table->timestamps();            // デフォルト
    });
  }
public function down()        // ロールバック時の処理は、down() に記述
  {
    Schema::dropIfExists('xxxxxs');    // テーブル削除
    Schema::create('xxxxxs', function (Blueprint $table) {
      $table->dropColumn('カラム名');    // カラム削除
      $table->dropColumn('gender');
    });
  }

よく使うマイグレーション関連のコマンド

マイグレーションに関するコマンド
% php artisan migrate:status              // 状態確認
% php artisan migrate                     // マイグレーション実行
% composer dump-autoload                  // 「クラスが見つかりません」エラーが発生したら、migrateコマンドを再発行してみる
% php artisan migrate:refresh             // 初期化→再実行(マイグレーションファイル追加でエラーが出た時など)
% php artisan migrate:fresh               // 全テーブル削除 →マイグレーション実行
% php artisan migrate:rollback            // ロールバック(1つ前)
% php artisan migrate:rollback --step=2   // ロールバック(戻る位置指定)
% php artisan migrate:reset               // 全マイグレーションをリセット(初期化)

■ マイグレーションファイルのみ作成したいなら

例)マイグレーションファイルのみ作成したい場合
% php artisan make:migration create_boxs_table --create=boxs             // boxsテーブル生成
% php artisan make:migration add_カラム名_to_users_table --table=users     // usersテーブルにカラム追加(既存テーブルを更新)
% php artisan make:migration rename_旧テーブル名_to_新テーブル名_table        // テーブル名変更

// マイグレーションファイルの編集ができたら、実行
% php artisan migrate
% composer require doctrine/dbal     // 注)変更・追加には、変更追加用のパッケージのインストールが必要

■ マイグレーションファイルの書き方

テーブル関連

テーブル
// テーブル名変更
Schema::rename("旧テーブル名", "新テーブル名");

// テーブル削除
Schema::drop('users');
Schema::dropIfExists('users');     // テーブルがあれば削除して、なければ何もしない(エラー回避)

カラム関連

カラム追加・削除

カラム追加・削除
// カラム追加
$table->カラム型('カラム名');
$table->string('カラム名');

// カラム削除
$table->dropColumn('削除したいカラム名');
$table->dropColumn(['削除したいカラム名1', '削除したいカラム名2',..]);   // 複数のカラムを削除したい場合は、配列で渡す
  • カラム削除のコマンド
削除コマンド 説明
$table->dropRememberToken( ); remember_tokenカラム(Eloquent認証ドライバーを扱うためのカラム )
$table->dropSoftDeletes( ); deleted_atカラム(ソフトデリート用のNULL可能なカラム)
$table->dropSoftDeletesTz( ); ↑ と同義
$table->dropTimestamps( ); created_atとupdated_atカラム
$table->dropTimestampsTz( ); ↑ と同義
  • ソフトデリートとは?
    • 論理削除のこと。データは削除せず、データを削除したものをして取り扱う。

カラム変更

  • カラム変更前に、composer.jsonファイルでdoctrine/dbalの追加が必要。
    • 現在の状態を決め、指定カラムの修正を行うSQLクエリ生成に使われる。
composer require doctrine/dbal
カラム変更
// カラム名変更
$table -> renameColumn('以前のカラム名', '新しいカラム名');

// カラム属性の変更
$table->string('name', 50) -> change();                 // nameカラムのサイズ変更
$table->string('name', 50) -> nullable() -> change();   // NULLを許可する場合
  • chengeメソッド : カラム型、属性の変更。

インデックスや制約など

インデックス

  • Laravelでは、デフォルトで意味が通る名前をインデックスに付ける。
    • インデックス名 : テーブル名_インデックスしたカラム名_インデックスタイプ

インデックス 【追加】

インデックス追加コマンド 概要 使用例
index('カラム名') インデックス追加 $table->integer('category_id')->index('category_id');
unique('カラム名') ユニークキー追加 $table->unique('email');、$table->string('email')->unique();
primary('id'); 主キー追加 $table->primary('id');
primary(['id', 'parent_id']); 複合キー追加 $table->primary(['id', 'parent_id']);
spatialIndex('カラム名'); 空間インデックス追加(SQLite以外) $table->spatialIndex('location');

インデックス 【削除】

  • インデックス削除は、インデックス名を指定する。
インデックス削除コマンド 概要
$table->dropIndex('テーブル名_カラム名 _index'); インデックス削除
$table->dropUnique('テーブル名_カラム名 _unique'); ユニークキー削除
$table->dropPrimary('テーブル名_id _primary'); 主キー削除
$table->dropSpatialIndex('テーブル名_カラム名 _spatialindex'); 空間インデックス削除(SQLite以外)
  • カラムの配列をインデックスの削除メソッドに渡すと、インデックス名が自動生成される。
例)インデックス削除
$table -> dropIndex(['content']);   // 例) boxs_content_index

外部キー

外部キー 【追加】

  • 紐づけたいテーブルのid(unsined(符号無し))と同様の制約をつけないと形式不一致でエラーになるので注意。
既存カラムを外部キーにする(紐づける)場合
例)既存のuser_idカラムを外部キーにする
$table->integer('user_id')->unsigned()->change();                  // 符号無し属性に変更
$table->foreign('user_id')->references('id')->on('users');         // 外部キー参照
新規カラムとして、外部キーを作成する場合
例)usersテーブルのidカラムを使って、postsテーブルにuser_idカラムを定義する
Schema::table('posts', function (Blueprint $table) {
  $table->integer('user_id')->unsigned();                           // 符号無し属性を作成
  $table->foreign('user_id')->references('id')->on('users');        // 外部キー参照
});

外部キー 【削除】

  • dropForeignメソッド : 他のインデックスと似た命名規則。
  • テーブル名_カラム名_foreign
外部キーの削除
$table->dropForeign('posts_user_id_foreign');
$table->dropForeign(['user_id']);              // 配列で渡せば、命名規則に従った名前が使用される
  • 参)マイグレーション中の外部キー制約を一時的に、有効/無効化ができるメソッドも用意されてる。
    • 外部キー制約を無効にしてDBを操作し、終わったら有効に戻すような時に使う。
参)外部キー制約の有効/無効化の変更
Schema::enableForeignKeyConstraints();    //外部キー制約を有効化
Schema::disableForeignKeyConstraints();   //外部キー制約を一時的に無効化

マイグレーション関連のエラー

MySQL接続エラー: SQLSTATE[HY000][2002]No such file or directory

[PDOException]
SQLSTATE[HY000] [2002] No such file or directory

  • DBに接続できていないエラー。
  • (原因例)
    • .envファイルとMySQL(DB)の、DB_DATABASE、DB_USERNAME、DB_PASSWORDが異なる。
    • .envファイルの修正後、キャッシュをクリアできてない。
    • rootユーザーのPW変更後に、MySQLを再起動してない。
    • MySQLソケットに関するファイル(mysql.sock)が読み込まれてない。 → database.phpで unix_socket を設定する。
ターミナルからunix_socketを確認
// MySQL
% show variables like '%sock%';
            :
 | socket | /Applications/MAMP/tmp/mysql/mysql.sock |
unix_socketの設定(database.php)
'mysql' => [
  'driver'      => 'mysql',
  'host'        => 'localhost',
          : 
  'unix_socket' => '/Applications/MAMP/tmp/mysql/mysql.sock', 
],

■ カラム型 一覧

コマンド 概要
increments('id'); 符号なしの数値(INT)を使ったID(主キー)。0〜4294967295。
bigIncrements('id'); 符号なしのBIGINTを使ったID(主キー)。0〜18446744073709551615。※ 21億行以上にならないなら、↑ を使う。
tinyInteger('numbers'); 数値(TINYINT型)。1バイト
smallInteger('カラム名'); 数値(SMALLINT型)。2バイト
mediumInteger('カラム名'); 数値(MEDIUMINT型)。3バイト
integer('カラム名'); 数値(INT型)。4バイト
bigInteger('カラム名'); 数値(BIGINT型)。8バイト
float('カラム名', ○, △); トータルで8桁、小数点以下2桁以内の小数
double('カラム名', ○, △); トータルで15桁、小数点以下8桁以内の小数
decimal('カラム名', ○, △); トータルで5桁、小数点以下2桁以内の小数
char('カラム名', 5); 固定長文字列(長さが決まってる文字列)
string('カラム名'); 可変長文字列。デフォルトの最大文字数:255文字
string('カラム名', 100); 最大文字数指定の可変長文字列
text('カラム名'); 文字列。65,535文字(約64Kバイト)
mediumText('カラム名'); 長い文字列。16,777,215文字(約16Mバイト)
longText('カラム名'); めっちゃ長い文字列。4,294,967,295文字(約4Gバイト)
date('カラム名'); 日付
time('カラム名'); 時間
timeTz('カラム名'); 時間(タイムゾーン付き)
dateTime('カラム名'); 日時
dateTimeTz('カラム名'); 日時(タイムゾーン付き)
boolean('カラム名')->default(false); 真偽値
enum('カラム名', ['定数', '定数']); enum型
binary('カラム名'); バイナリデータ
json('カラム名'); JSON
jsonb('カラム名'); JSONB
timestamp('カラム名'); タイムスタンプ
timestampTz('カラム名'); タイムスタンプ(タイムゾーン付き)
timestamps(); created_atとupdated_at
nullableTimestamps(); ↑と同じ(NULLを許可)
softDeletes(); NULL可能な deleted_at カラム。例:退会日時などの管理用途(nullでなければ、退会済)
ipAddress('カラム名'); IPアドレス
macAddress('カラム名'); MACアドレス
rememberToken(); Eloquent認証ドライバーを扱うためのカラム。remember_tokenVARCHAR(100)NULLとして追加
uuid('id'); UUID。重複の可能性が小さく、一意な識別子として扱えるID
morphs('カラム名'); 符号なし数値(taggable_id)と文字列(taggable_type)を追加

■ オプション(カラム修飾子)一覧

カラム修飾子 概要
->comment('コメント内容') コメント追加(MySQLのみ)
->nullable() NULL許可
->unsigned() 整数カラムを符号なしに設定(MySQLのみ)。外部キーでよく使う
->default('値')、->default($value) デフォルト値を設定
->first() テーブルの最初(first)に設置(MySQLのみ)
->after('カラム名') 指定カラムの次にカラム追加(MySQLのみ)
->autoIncrement() 整数カラムをID(主キー)に設定
->charset('utf8') キャラクタセットを指定(MySQLのみ)
->collation('utf8_unicode_ci') コロケーションを指定(MySQL/SQL Serverのみ)
->nullable($value = true)、->nullable()->default(null) デフォルトでNULL値を指定
->storedAs($expression) stored generatedカラムを生成(MySQLのみ)
->useCurrent() TIMESTAMPカラムのデフォルト値をCURRENT_TIMESTAMPに指定
->virtualAs($expression) virtual generatedカラムを生成(MySQLのみ)

■ データの作成

  • テーブルが作成できたら、データを作成してみる。

seederで初期値を作成する場合

例)UsersTableSeeder.php生成
% php artisan make:seeder UsersTableSeeder        // UsersTableSeeder.phpが生成される
database/seeds/UsersTableSeeder.php
public function run(){
  DB::table('users')->insert([
    'name' => 'ユーザー1',
    'email' => 'ユーザー1@gmail.com',
    'password' => bcrypt('ユーザー1') 
  ]);
}
database/seeds/DatabaseSeeder.php
public function run()
{
  // $this->call(UsersTableSeeder::class);   // コメントアウトする
}
seederから、データを作成する
% php artisan db:seed --class=UsersTableSeeder     // データ作成
% php artisan tinker                               // 作成したデータの確認
  >>> use App\User
  >>> User::all()

コンソール操作でレコードを作成する場合

ターミナル操作で作成する方法
% php artisan tinker               // コンソール起動
  // レコード作成( インスタンスを作成して、saveメソッドを使う )
  >>> $box = new App\Box();        // インスタンス作成(※ 名前空間がデフォルトでAppなので、App\モデル名(); )
  >>> $box -> title = 'タイトル1';
  >>> $box -> content = '詳細1';
  >>> $box -> save();              // 保存
  => true                         // レコード作成成功

  >>> App\Box::all();             // 作成したデータの確認
  >>> App\Box::all()->ToArray();  // 作成したデータの確認(配列で取得)
  >>> exit
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel】初めてのマイグレーション

Laravelでのモデル作成とマイグレーションファイル編集に関する備忘録。

■ モデル作成

モデル作成(※モデル名は、大文字から始まる単数形)
% php artisan make:model モデル名             // app直下にファイル生成
% php artisan make:model Entities/モデル名    // app/Entities下にファイル生成される
% php artisan make:model モデル名 -m -c -r    // モデル、マイグレーション、コントローラーを一気に生成

% php artisan make:model -h                 // どんなオプションがあるか?
  • マイグレーションクラスは、2つのメソッドがある(up、down)。
    • upメソッド : マイグレーション実行時の処理 を記述。
    • downメソッド : ロールバック時の処理を記述(upメソッドの逆)。
作成されるマイグレーションファイルのざっくりイメージ(database/migrations/2020_10_15_012345_create_xxxxx_table.php)
public function up()           // マイグレーション実行時の処理は、up() に記述
  {
    Schema::create('xxxxxs', function (Blueprint $table) {
      $table->increments('id');        // デフォルト
      $table->データ型('カラム名');        // カラム作成
      $table->tinyInteger('gender')->unsigned()->comment('性別 1:男、2:女');
      $table->timestamps();            // デフォルト
    });
  }
public function down()        // ロールバック時の処理は、down() に記述
  {
    Schema::dropIfExists('xxxxxs');    // テーブル削除
    Schema::create('xxxxxs', function (Blueprint $table) {
      $table->dropColumn('カラム名');    // カラム削除
      $table->dropColumn('gender');
    });
  }

よく使うマイグレーション関連のコマンド

マイグレーションに関するコマンド
% php artisan migrate:status              // 状態確認
% php artisan migrate                     // マイグレーション実行
% composer dump-autoload                  // 「クラスが見つかりません」エラーが発生したら、migrateコマンドを再発行してみる
% php artisan migrate:refresh             // 初期化→再実行(マイグレーションファイル追加でエラーが出た時など)
% php artisan migrate:fresh               // 全テーブル削除 →マイグレーション実行
% php artisan migrate:rollback            // ロールバック(1つ前)
% php artisan migrate:rollback --step=2   // ロールバック(戻る位置指定)
% php artisan migrate:reset               // 全マイグレーションをリセット(初期化)

■ マイグレーションファイルのみ作成したいなら

例)マイグレーションファイルのみ作成したい場合
% php artisan make:migration create_boxs_table --create=boxs             // boxsテーブル生成
% php artisan make:migration add_カラム名_to_users_table --table=users     // usersテーブルにカラム追加(既存テーブルを更新)
% php artisan make:migration rename_旧テーブル名_to_新テーブル名_table        // テーブル名変更

// マイグレーションファイルの編集ができたら、実行
% php artisan migrate
% composer require doctrine/dbal     // 注)変更・追加には、変更追加用のパッケージのインストールが必要

■ マイグレーションファイルの書き方

テーブル関連

テーブル
// テーブル名変更
Schema::rename("旧テーブル名", "新テーブル名");

// テーブル削除
Schema::drop('users');
Schema::dropIfExists('users');     // テーブルがあれば削除して、なければ何もしない(エラー回避)

カラム関連

カラム追加・削除

カラム追加・削除
// カラム追加
$table->カラム型('カラム名');
$table->string('カラム名');

// カラム削除
$table->dropColumn('削除したいカラム名');
$table->dropColumn(['削除したいカラム名1', '削除したいカラム名2',..]);   // 複数のカラムを削除したい場合は、配列で渡す
  • カラム削除のコマンド
削除コマンド 説明
$table->dropRememberToken( ); remember_tokenカラム(Eloquent認証ドライバーを扱うためのカラム )
$table->dropSoftDeletes( ); deleted_atカラム(ソフトデリート用のNULL可能なカラム)
$table->dropSoftDeletesTz( ); ↑ と同義
$table->dropTimestamps( ); created_atとupdated_atカラム
$table->dropTimestampsTz( ); ↑ と同義
  • ソフトデリートとは?
    • 論理削除のこと。データは削除せず、データを削除したものをして取り扱う。

カラム変更

  • カラム変更前に、composer.jsonファイルでdoctrine/dbalの追加が必要。
    • 現在の状態を決め、指定カラムの修正を行うSQLクエリ生成に使われる。
composer require doctrine/dbal
カラム変更
// カラム名変更
$table -> renameColumn('以前のカラム名', '新しいカラム名');

// カラム属性の変更
$table->string('name', 50) -> change();                 // nameカラムのサイズ変更
$table->string('name', 50) -> nullable() -> change();   // NULLを許可する場合
  • chengeメソッド : カラム型、属性の変更。

インデックスや制約など

インデックス

  • Laravelでは、デフォルトで意味が通る名前をインデックスに付ける。
    • インデックス名 : テーブル名_インデックスしたカラム名_インデックスタイプ

インデックス 【追加】

インデックス追加コマンド 概要 使用例
index('カラム名') インデックス追加 $table->integer('category_id')->index('category_id');
unique('カラム名') ユニークキー追加 $table->unique('email');、$table->string('email')->unique();
primary('id'); 主キー追加 $table->primary('id');
primary(['id', 'parent_id']); 複合キー追加 $table->primary(['id', 'parent_id']);
spatialIndex('カラム名'); 空間インデックス追加(SQLite以外) $table->spatialIndex('location');

インデックス 【削除】

  • インデックス削除は、インデックス名を指定する。
インデックス削除コマンド 概要
$table->dropIndex('テーブル名_カラム名 _index'); インデックス削除
$table->dropUnique('テーブル名_カラム名 _unique'); ユニークキー削除
$table->dropPrimary('テーブル名_id _primary'); 主キー削除
$table->dropSpatialIndex('テーブル名_カラム名 _spatialindex'); 空間インデックス削除(SQLite以外)
  • カラムの配列をインデックスの削除メソッドに渡すと、インデックス名が自動生成される。
例)インデックス削除
$table -> dropIndex(['content']);   // 例) boxs_content_index

外部キー

外部キー 【追加】

  • 紐づけたいテーブルのid(unsined(符号無し))と同様の制約をつけないと形式不一致でエラーになるので注意。
既存カラムを外部キーにする(紐づける)場合
例)既存のuser_idカラムを外部キーにする
$table->integer('user_id')->unsigned()->change();                  // 符号無し属性に変更
$table->foreign('user_id')->references('id')->on('users');         // 外部キー参照
新規カラムとして、外部キーを作成する場合
例)usersテーブルのidカラムを使って、postsテーブルにuser_idカラムを定義する
Schema::table('posts', function (Blueprint $table) {
  $table->integer('user_id')->unsigned();                           // 符号無し属性を作成
  $table->foreign('user_id')->references('id')->on('users');        // 外部キー参照
});

外部キー 【削除】

  • dropForeignメソッド : 他のインデックスと似た命名規則。
  • テーブル名_カラム名_foreign
外部キーの削除
$table->dropForeign('posts_user_id_foreign');
$table->dropForeign(['user_id']);              // 配列で渡せば、命名規則に従った名前が使用される
  • 参)マイグレーション中の外部キー制約を一時的に、有効/無効化ができるメソッドも用意されてる。
    • 外部キー制約を無効にしてDBを操作し、終わったら有効に戻すような時に使う。
参)外部キー制約の有効/無効化の変更
Schema::enableForeignKeyConstraints();    //外部キー制約を有効化
Schema::disableForeignKeyConstraints();   //外部キー制約を一時的に無効化

マイグレーション関連のエラー

MySQL接続エラー: SQLSTATE[HY000][2002]No such file or directory

[PDOException]
SQLSTATE[HY000] [2002] No such file or directory

  • DBに接続できていないエラー。
  • (原因例)
    • .envファイルとMySQL(DB)の、DB_DATABASE、DB_USERNAME、DB_PASSWORDが異なる。
    • .envファイルの修正後、キャッシュをクリアできてない。
    • rootユーザーのPW変更後に、MySQLを再起動してない。
    • MySQLソケットに関するファイル(mysql.sock)が読み込まれてない。 → database.phpで unix_socket を設定する。
ターミナルからunix_socketを確認
// MySQL
% show variables like '%sock%';
            :
 | socket | /Applications/MAMP/tmp/mysql/mysql.sock |
unix_socketの設定(database.php)
'mysql' => [
  'driver'      => 'mysql',
  'host'        => 'localhost',
          : 
  'unix_socket' => '/Applications/MAMP/tmp/mysql/mysql.sock', 
],

■ カラム型 一覧

コマンド 概要
increments('id'); 符号なしの数値(INT)を使ったID(主キー)。0〜4294967295。
bigIncrements('id'); 符号なしのBIGINTを使ったID(主キー)。0〜18446744073709551615。※ 21億行以上にならないなら、↑ を使う。
tinyInteger('numbers'); 数値(TINYINT型)。1バイト
smallInteger('カラム名'); 数値(SMALLINT型)。2バイト
mediumInteger('カラム名'); 数値(MEDIUMINT型)。3バイト
integer('カラム名'); 数値(INT型)。4バイト
bigInteger('カラム名'); 数値(BIGINT型)。8バイト
float('カラム名', ○, △); トータルで8桁、小数点以下2桁以内の小数
double('カラム名', ○, △); トータルで15桁、小数点以下8桁以内の小数
decimal('カラム名', ○, △); トータルで5桁、小数点以下2桁以内の小数
char('カラム名', 5); 固定長文字列(長さが決まってる文字列)
string('カラム名'); 可変長文字列。デフォルトの最大文字数:255文字
string('カラム名', 100); 最大文字数指定の可変長文字列
text('カラム名'); 文字列。65,535文字(約64Kバイト)
mediumText('カラム名'); 長い文字列。16,777,215文字(約16Mバイト)
longText('カラム名'); めっちゃ長い文字列。4,294,967,295文字(約4Gバイト)
date('カラム名'); 日付
time('カラム名'); 時間
timeTz('カラム名'); 時間(タイムゾーン付き)
dateTime('カラム名'); 日時
dateTimeTz('カラム名'); 日時(タイムゾーン付き)
boolean('カラム名')->default(false); 真偽値
enum('カラム名', ['定数', '定数']); enum型
binary('カラム名'); バイナリデータ
json('カラム名'); JSON
jsonb('カラム名'); JSONB
timestamp('カラム名'); タイムスタンプ
timestampTz('カラム名'); タイムスタンプ(タイムゾーン付き)
timestamps(); created_atとupdated_at
nullableTimestamps(); ↑と同じ(NULLを許可)
softDeletes(); NULL可能な deleted_at カラム。例:退会日時などの管理用途(nullでなければ、退会済)
ipAddress('カラム名'); IPアドレス
macAddress('カラム名'); MACアドレス
rememberToken(); Eloquent認証ドライバーを扱うためのカラム。remember_tokenVARCHAR(100)NULLとして追加
uuid('id'); UUID。重複の可能性が小さく、一意な識別子として扱えるID
morphs('カラム名'); 符号なし数値(taggable_id)と文字列(taggable_type)を追加

■ オプション(カラム修飾子)一覧

カラム修飾子 概要
->comment('コメント内容') コメント追加(MySQLのみ)
->nullable() NULL許可
->unsigned() 整数カラムを符号なしに設定(MySQLのみ)。外部キーでよく使う
->default('値')、->default($value) デフォルト値を設定
->first() テーブルの最初(first)に設置(MySQLのみ)
->after('カラム名') 指定カラムの次にカラム追加(MySQLのみ)
->autoIncrement() 整数カラムをID(主キー)に設定
->charset('utf8') キャラクタセットを指定(MySQLのみ)
->collation('utf8_unicode_ci') コロケーションを指定(MySQL/SQL Serverのみ)
->nullable($value = true)、->nullable()->default(null) デフォルトでNULL値を指定
->storedAs($expression) stored generatedカラムを生成(MySQLのみ)
->useCurrent() TIMESTAMPカラムのデフォルト値をCURRENT_TIMESTAMPに指定
->virtualAs($expression) virtual generatedカラムを生成(MySQLのみ)

■ データの作成

  • テーブルが作成できたら、データを作成してみる。

seederで初期値を作成する場合

例)UsersTableSeeder.php生成
% php artisan make:seeder UsersTableSeeder        // UsersTableSeeder.phpが生成される
database/seeds/UsersTableSeeder.php
public function run(){
  DB::table('users')->insert([
    'name' => 'ユーザー1',
    'email' => 'ユーザー1@gmail.com',
    'password' => bcrypt('ユーザー1') 
  ]);
}
database/seeds/DatabaseSeeder.php
public function run()
{
  // $this->call(UsersTableSeeder::class);   // コメントアウトする
}
seederから、データを作成する
% php artisan db:seed --class=UsersTableSeeder     // データ作成
% php artisan tinker                               // 作成したデータの確認
  >>> use App\User
  >>> User::all()

コンソール操作でレコードを作成する場合

ターミナル操作で作成する方法
% php artisan tinker               // コンソール起動
  // レコード作成( インスタンスを作成して、saveメソッドを使う )
  >>> $box = new App\Box();        // インスタンス作成(※ 名前空間がデフォルトでAppなので、App\モデル名(); )
  >>> $box -> title = 'タイトル1';
  >>> $box -> content = '詳細1';
  >>> $box -> save();              // 保存
  => true                         // レコード作成成功

  >>> App\Box::all();             // 作成したデータの確認
  >>> App\Box::all()->ToArray();  // 作成したデータの確認(配列で取得)
  >>> exit
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP+MySQLで作成したアプリをHerokuでデプロイする手順(workbenchの設定まで)

概要

初心者がPHPで作成したwebアプリをHerokuでデプロイするまでの過程をまとめてみました。

環境

 【言語】: HTML、CSS(レスポンジブ未対応)、PHP 7.3.9、JavaScript、jQuery2.2.2
 【DBMS】: MySQL 5.7.26
 【フレームワーク】: なし
 【開発環境】: MacOS Catalina 10.15.5
 【バージョン管理】: Git 2.24.3
 【本番環境】: Heroku

大まかな流れ

1.Gitと連携

2.Heroku操作

3.DB情報の移行(workbench操作)

4.ライブラリの追加

5.Herokuへpush

1.Gitと連携

※既にGithubにリポジトリを作成している場合はの過程はスキップ

①新規リモートリポジトリを作成

②デプロイするディレクトリに移動し、ローカルリポジトリの作成↓

$ git init

③add~push↓

$ git add.
$ git commit -m 'commit'
$ git push origin master

2.Heroku操作

①Herokuにログイン↓(Herokuのインストールの説明は省略)

$heroku login

②Heroku上に新しくアプリケーションを作成↓

$ heroku create つけたいアプリ名
 Warning: heroku update available from 7.35.0 to 7.44.0.
Creating ⬢ つけたいアプリ名... done
https://munakatatest8.herokuapp.com/ | https://git.heroku.com/munakatatest8.git

③HerokuのアプリケーションにClearDBを追加↓(igniteは無料です)

$ heroku addons:create cleardb:ignite
Creating cleardb:ignite on ⬢ つけたいアプリ名... free
Created cleardb-rectangular-89660 as CLEARDB_PUCE_URL
Use heroku addons:docs cleardb to view documentation

④データベース情報の確認↓(後々この情報を使用します)

$ heroku config | grep CLEARDB_DATABASE_URL

CLEARDB_DATABASE_URL: mysql://******:******@us-cdbr-east-02.cleardb.com/heroku_dff14ec3ad68e70?reconnect=true
//↓下記のデータが入ってる認識
CLEARDB_DATABASE_URL: mysql://ユーザー名:パスワード@ホスト名/データベース名?reconnect=true

3.DB情報の移行(workbench操作)

①Workbenchのインストール
MySQL Workbenchの公式ページに飛んで、「ダウンロードはこちら」をクリック。

②インストールが完了したら、phpmyadmin から使用したいDBをエクスポート↓
スクリーンショット 2020-10-05 20.03.40.png

③workbenchを起動し、Connectionを新規作成↓
2-④で取得したCLEARDBのユーザー名、ホスト名、データベース名、パスワードを打ち込む
スクリーンショット 2020-10-05 20.17.15.png

『Test Connection』を実行したところ謎のwarningが表示されたが、無視して続行。↓

スクリーンショット 2020-10-05 20.16.57.png

パスワードを再度打ち込み→any continueと進み
『MySQL接続が正常に確立されました』との表示がでたため、↓
スクリーンショット 2020-10-05 20.23.15.png

戻って立ち上げたconectionをクリック
④DB情報をインポート
Data Import/Restoreをクリックしインポート画面へ。

エクスポートした、DB情報ファイルを入力、
スクリーンショット 2020-10-17 13.04.25.png

【記入箇所】
・Import from Self-Contained→Fileを選び、エクスポートした「〇〇〇〇.sql」を記入。

・Default Target Schema→Newではなくデフォルトで表示されるデータベース名を選択。

・Start Importを押してインポート。

Import Completedと表示されレバ、インポートが完了し、Workbenchのデータベース設定も無事終了。

⑤エラー例
私がDB情報のインポートの際実際に直面したエラーです。
スクリーンショット 2020-10-05 22.23.16.png
スクリーンショット 2020-10-05 20.41.45.png

原因

SQL文内に「CREATE DATABASE IF NOT EXISTS」が含まれていた。
ここではデータベースが1つしか使えない設定になっている為エラーが起きた。

解決法

1.エクスポートしたSQLファイルをメモ帳などのエディタで開く
2.CREATE DATABASE IF NOT EXISTSで始まる1行を削除
2.その下にUSE ‘データベース名’があればそれも削除

Import Completedと表示されたため、 DBのインポート完了

⑥自分が作ったwebアプリをデータベースに接続できるようするためコードを書き換える。

function dbConnect()
{

  $dsn = 'mysql:dbname=DB名;host=ホスト名;charset=utf8';
  $user = 'ユーザー名';
  $password = 'パスワード';
  $option = array(

    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true,
  );

  $dbh = new PDO($dsn, $user, $password, $option);
  return $dbh;
}

⑦完了したら、git add~pushまで行う

4.ライブラリの追加

デプロイする前にフォルダの中に「composer.json」「composer.lock」「Procfile」がきちんと入っているか確認する。

もし入っていない場合は、フォルダ内にこれらのファイルを追加する。

①ディレクトリ内にcomposerのインストール

brew install composer

②ディレクトリにcomposer.jsonファイルが追加されているので中身にPHPのバージョンを記述

{
  "require": {
    "php": "~7.3.9",
    "ext-mbstring": "*"
  }
}

③次にcomposer.lockファイルの追加

composer install

vendorディレクトリ、composer.lockファイルが追加されていることを確認。

スクリーンショット 2020-10-17 13.27.28.png

↑追加されている状態

5.Herokuへpush

①フォルダの中身を確認し「composer.json」「composer.lock」が存在していれば、ターミナルに下記を打ち込む。

$ git push heroku master

エラーメッセージが出なければ、無事にデプロイ完了。

②実際にページを開いて確認

$ heroku open

アプリが表示されていればデプロイ完了。

デプロイ後私が経験したエラー

・DBのカラム形式が間違っている(invaild~)
・小文字大文字が混同してる。(404)

最後に

以上、Herokuを使って、PHPポートフォリオサイトを無料でデプロイする方法でした。認識違いなどございましたら、ご指摘していただけると幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【PHP】今日、Laravel始めました

ビルトインサーバーでのLaravel導入についての備忘録。
MacのPHPバージョンアップ、Laravel導入、DB(MySQL)への接続まで。

環境構築(Mac)

  • 現在のPHPの最新バージョン 7.4に、Homebrewを使って、バージョンアップ。
PHPのバージョンアップ
% php -v                      // Macに入ってるバージョン確認
  PHP 7.3.11 ...
% brew search php@7           // Homebrewコマンドで、インストールができるPHP7系を検索してみる
  => Formulae
  php@7.2       php@7.3       php@7.4
% brew install php@7.4        // Homebrewを使って、7.4 をインストール
  Updating Homebrew...
         :

// インストールが完了したら、PATHを通す設定を上書きする
% echo 'export PATH="/usr/local/opt/php@7.4/bin:$PATH"' >> ~/.bash_profile
% echo 'export PATH="/usr/local/opt/php@7.4/sbin:$PATH"' >> ~/.bash_profile
% source ~/.bash_profile

% brew services start php       // PHPを起動
// ターミナルを一旦閉じてから、バージョン確認してみる
% php -v
  PHP 7.4.11 ...
  • Laravel導入には、以下が必要なのでインストールする。
    • Homebrew : 導入済み
    • PHP : 導入済み
    • Composer : ライブラリやパッケージを管理し、それをもとにインストールする機能。
    • Laravel
Laravelの導入まで
// Composer インストール
% brew search composer
  ==> Formulae
  composer
  ==> Casks
  composercat
% brew install composer
% composer -v                  // 導入を確認
   ______
  / ____/___  ____ ___  ____  ____  ________  _____
 / /   / __ \/ __ `__ \/ __ \/ __ \/ ___/ _ \/ ___/
/ /___/ /_/ / / / / / / /_/ / /_/ (__  )  __/ /
\____/\____/_/ /_/ /_/ .___/\____/____/\___/_/
                    /_/
  Composer version 1.10.6 2020-05-06 10:28:10
          :
// Laravelのインストール
% composer global require "laravel/installer"
// Laravelのコマンドを使えるようにするために、パスを通す(.zshrcファイル)
% vim ~/.zshrc                // i(挿入モード)で編集し、escキーで抜け、:wqで保存して終了
  export PATH="$PATH:/Users/自分のPC名/.composer/vendor/bin"
% source ~/.zshrc            // 変更を反映
% laravel -v                 // 導入を確認
  Laravel Installer 4.0.5
          :

Laravelアプリ作成の実装手順

アプリの雛形作成

  • artisan(アーティサン)コマンド : Laravelの機能を呼び出すコマンド。
Laravelアプリの作成
% cd ~/projects
% laravel new laravel_sample    // アプリ名 「 laravel_sample 」
% cd sample
% php artisan serve             // サーバーを起動し、ブラウザで確認(localhost:8000)
                                // サーバー停止は、 control + C
  • localhost:8000 にアクセス↓(※ app/resources/views/welcome.blade.php が表示されてればOK) Alt text

laravelアプリのディレクトリ構造

ざっくりしたディレクトリ構造
・app
  └─ Http  
       └─ コントローラー     
  └─ モデル
・database
    └─ マイグレーション
・resources
    └─ ビュー
・routes
    └─ web.php (ルーティング)

タイムゾーンと言語だけ設定しとく

  • レコード保存時の時間情報の設定など。
タイムゾーンと言語の設定(config\app.php)
'timezone' => 'Asia/Tokyo',
'locale' => 'ja',
'faker_locale' => 'ja_JP',

DB作成(mySQL)

  • DB接続には、 ホスト、ポート、DB名、ユーザー名、PW が必要。
  • Laravelでは、.envファイルに、環境変数を記載。多分、デフォルトのままでok。

DB作成のための設定

アプリ作成時に作成された.envファイル(DB関連部分)
        :
DB_CONNECTION=mysql         // DBの種類
DB_HOST=127.0.0.1           // 接続先のホスト
DB_PORT=3306                // 接続先のポート
DB_DATABASE=laravel_sample  // DB名
DB_USERNAME=root            // ユーザー名
DB_PASSWORD=                // PW(なくてok)
        :
config/database.php
'mysql' => [
                 :
  'database' => env('DB_DATABASE', 'laravel_sample'),
  'username' => env('DB_USERNAME', 'root'),
  'password' => env('DB_PASSWORD', ''),
                 :

DB作成

  • MySQLにrootでログインし、DBを作成する。
ターミナル
% mysql -u root                            // rootでMySQLにログイン
  mysql> create database laravel_sample;   // DB作成
    Query OK, 1 row affected (0.01 sec)
  mysql> show databases;                   // DB一覧で確認
  mysql> exit

DBをアプリと接続する

アプリのディレクトリ(ターミナル)
% php artisan tinker                              // 対話コマンド
  Psy Shell v0.10.4 (PHP 7.4.11 — cli) by Justin Hileman
  >>> DB::connection();                           // DBが接続されてるか確認
  => Illuminate\Database\MySqlConnection {#3238}
  >>> exit                                        // 確認できたら終了しとく
% php artisan migrate                             // マイグレーション実行
  • これで、MySQLと繋がり、空のDBが作成できたハズ。

よく使いそうな Laravel artisanコマンド

よく使いそうなコマンド
% php artisan --version             // バージョンの確認

% php artisan                      // コマンド一覧の確認方法 (引数の使い方も確認できるみたい)
% php artisan list
% php artisan make:model -h        // モデル作成に、どんなオプションがあるか?
% php artisan make:controller -h   // コントローラー作成に、どんなオプションがあるか?

% php artisan serve                // ローカルサーバー起動(8000)
% php artisan route:list           // ルーティングの確認
% php artisan tinker               // 対話コンソールの起動
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MySQLのreplicationをcustom mackerel-plugin で監視する

managed じゃないRDBで replicationするのは、できれば避けて過ごしたい人生ですが、https://qiita.com/masudakz/items/1afafcca1c01d8b9c276 のようなシステム構成もありうるのです。そして replicationを組んでいると、「同期できなくなって障害」というのにいつかは出くわすので、早期警戒体制が欲しくなります。

Mackerel公式の mackerel-plugin-mysql にはmysql.seconds_behind_master.Seconds_Behind_Master があってよさげな感じです。

スレーブ SQL スレッドとスレーブ I/O スレッドの間の時間差 (秒単位)スレーブ SQL スレッドとスレーブ I/O スレッドの間の時間差 (秒単位)

この plugin を入れて Monitors でAlert設定入れて検証してみると、検出できない障害パターンもありました。Master側がAWS RDS でSecurityGroupで、Slaveからの接続を遮断したパターンです。Master側のbinlogが見えないので、遅延を計測できない感じです。MySQLの show slave status にも異常所見が見えない(少なくともAlert出したい時間感覚の中では)。

SecurityGroupの誤設定とか、Master側でのGRANTの誤設定とか、Replication用ユーザを間違って無効化するとか、遅延以外のオペレーションミスでの通信途絶リスクがあるので、これではちょっと足りない。足りないなら自作すればいいのです。

手動の同期トラブルシューティング

すでに開発環境で2度の同期事故経験があるので、どこを見てどう判断したかを思い出してみます。

  1. show slave statusLast_Error
  2. いつ止まったか、高頻度INSERTのTABLEの最終レコードを見る
    SELECT id, ts_created FROM ... ORDER BY id DESC LIMIT 1;
  3. replication用userで Slave - Master の疎通テスト

plugin概要

show slave statusLast Error はどんなパターンが出るか事前には網羅できないし、通信途絶だとそもそも Error 認識できないので custom plugin のスコープからは外します。

高頻度INSERTのTABLEの最終レコードを Slaveからlocalhost と Master(remote)で比較すれば、疎通テストにもなります。疎通失敗で比較不可能だったらその時点でAlert出るようにすればいいでしょう。

plugin骨格

SELECT MAX(${keycolumn}) FROM ${table};

Primary Keyの MAX() で瞬時レスポンスで毎分実行しても負荷にはならない想定です。

sqloption="--silent --skip-column-names --connect-timeout=2"
mysql -h ${host1} -u ${user1} -D ${database} -e "${sql}" ${sqloption}

SQL出力は --silent --skip-column-names で最もシンプルな数値だけにします。
通信障害のときにはさっさと(2秒で)あきらめてほしいので --connect-timeout=2 です。

plugin metric

Alert目的なので SELECT MAX... の結果の差分 max1 - max2 が必須です。
0 が正常、WARN, CRITICAL は1でも10でも1000でもINSERT頻度に応じて。

でもこれだけだと平常時はずっと 0 が続くので、 custom pluginがちゃんと動いているかどうかが不安になります。元の max1, max2 も出力します。ただし、必須の diff とは値域も重要度も違うので、namespace を分割して別グラフにします。

echo replica.key.max1 ${max1}  ${EPOCH}
echo replica.key.max2 ${max2}  ${EPOCH}
echo replica.diff.diff ${keydiff}  ${EPOCH}

replica.key の方を Mackrelで差分グラフにすると Insert数/min のグラフになります。Insert密度があがっていくとどこかでSlaveが追いつけなくなって diff が非0になるでしょう。それが障害か、利用頻度上昇によるものか、判断の一助になることを期待しています。

pluginコード

...
sql="SELECT MAX(${keycolumn}) FROM ${table};"
sqloption="--silent --skip-column-names --connect-timeout=2"

EPOCH=$(date +'%s')
max2=`mysql -h ${host2} -u ${user2} -D ${database} -e "${sql}" ${sqloption}`
max1=`mysql -h ${host1} -u ${user1} -D ${database} -e "${sql}" ${sqloption}`
if [ $? != 0 ]; then
  keydiff=1000
else
  keydiff=`expr $max1 - $max2`
fi
echo replica.key.max1 ${max1}  ${EPOCH}
echo replica.key.max2 ${max2}  ${EPOCH}
echo replica.diff.diff ${keydiff}  ${EPOCH}

全容は https://github.com/masudakz/mackerel-plugin-replica で開示しています。

余談

2018年06月のMy first mackerel pluginの記事が「自助」になりました。面白みの薄い pluginだなあと思いながら当時書いたものですが、自分が読むには自分の記事が一番読みやすい。2つ目を作るのに大いに助けになってくれました。
https://qiita.com/masudakz/items/654d65041a7507e59ee3

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【CircleCI】CircleCIの自動テストでハマった(rails + mysql)【メモ】

はじめに

CircleCIを使ってRailsアプリの自動テストをやろうとしてハマったのでメモ。
特にmysqlコンテナとの接続が上手くいきませんでした。
各種設定ファイルの内容や、エラーへの対策を備忘録として残すことに。

環境

CircleCI  2.1
Bundler  2.1.4
Rails 6.0.3.4
ruby 2.6.5
MySQL 5.7

開発環境はDocker-composeで構築。
サービス名とコンテナ名の関係は以下のよう。

サービス名 コンテナ名
web webapp_web_1
db webapp_db_1
app webapp_app_1

設定ファイル

アプリ名はwebappとする。

database.ymlとdb.env

データベースの設定ファイル。
この設定がCircleCI側で上手く使えなかったので、今回は以下の記事を参考にCircleCI用のデータベース設定ファイルを作成することにしました。
よって、ここの設定はCircleCIとは関係ないです。
CircleCI 2.0の設定メモ

config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: <%= ENV.fetch('MYSQL_USER') { 'root' } %>
  password: <%= ENV.fetch('MYSQL_PASSWORD') { 'rootpass' } %>
  port: 3306
  host: db

development:
  <<: *default
  database: webapp_development

test:
  <<: *default
  database: webapp_test
environments/db.env
MYSQL_ROOT_USER=root
MYSQL_ROOT_PASSWORD=rootpass
MYSQL_USER=kagamiya
MYSQL_PASSWORD=kagamiya

ポートの設定は要らないかも。
host: dbはDocker-composeのサービス名です。
ENV.fetchの{ 'root' }とかも要らないと思います。

database.yml.ci

CI用のデータベース設定ファイル。
CirclCIでの自動テストではこちらを使います。

config/database.yml.ci
test:
  adapter: mysql2
  encoding: utf8
  pool: 5
  username: 'root'
  port: 3306
  host: '127.0.0.1'
  database: webapp_test

config.yml

肝心のCircleCIの設定ファイル。
参考はこちら。
CircleCIでテストを自動化

circleci/config.yml
version: 2.1 # バージョン指定  

executors:  
  default:  
    working_directory: ~/webapp  
    docker:  
      - image: circleci/ruby:2.6.5-node
        environment:  
          BUNDLER_VERSION: 2.1.4
          RAILS_ENV: test
          DB_HOST: 127.0.0.1
          DB_USERNAME: 'root'
          DB_PASSWORD: ''
      - image: circleci/mysql:5.7 
        environment:
          MYSQL_ROOT_HOST: '%'
          MYSQL_ALLOW_EMPTY_PASSWORD: 'true'

commands:  
  setup:  
    steps:  
      - checkout  
      - run:  
          name: Update bundler # bundlerのバージョン2へのアップデート  
          command: gem update bundler  

      - run:  
          name: Which bundler? # バージョン確認  
          command: bundle -v  

      - restore_cache: # キャッシュを読み込む  
          keys:  
            - gem-cache-v1-{{ checksum "Gemfile.lock" }}  
            - gem-cache-v1-  

      - run:  
          name: Bundle Install  
          command: bundle check --path vendor/bundle || bundle install --deployment  

      - save_cache: # キャッシュを保存する  
          key: gem-cache-v1-{{ checksum "Gemfile.lock" }}  
          paths:  
            - vendor/bundle  

      # 以下はRails6でWebpackerを使う場合は必須   
      - restore_cache:  
          keys:  
            - yarn-cache-v1-{{ checksum "yarn.lock" }}  
            - yarn-cache-v1-  

      - run:  
          name: Yarn Install  
          command: yarn install --cache-folder ~/.cache/yarn  

      - save_cache:  
          key: yarn-cache-v1-{{ checksum "yarn.lock" }}  
          paths:  
            - ~/.cache/yarn  

jobs:  
  test:  
    executor: default  
    environment:  
      RAILS_ENV: test  
    steps:  
      - checkout  
      - setup  
      - run:  
          name: Wait for DB  
          command: dockerize -wait tcp://127.0.0.1:3306 -timeout 90s  

      - run:
          name: Use specific database.yml # CircleCI用のデータベース設定を使う
          command: mv config/database.yml.ci config/database.yml

      - run:  
          name: Database setup  
          command: |
            bin/rails db:create
            bin/rails db:schema:load --trace  

      - run: # 普通のテストとシステムテストを実行する  
          name: Rails Test  
          command: |  
            bin/rails test  
            bin/rails test:system  

workflows:  
  build_and_test:  
    jobs:  
      - test  

(MySQLの8系を使う場合は記述の追加が必要なようで、5.7を使うことにしました。
また、rubyのイメージはnodeがついていないとyarnコマンドが使えませんでした。)

特にデータベース周りの設定でハマりました。
というのも、私がMySQLの仕様をあまり理解できていなかった(というか今も理解できていない)からで…
当初は参考記事にならって、

DB_USER: 'kagamiya'
DB_PASSWORD: 'kagamiya'

MYSQL_USER: 'kagamiya'
MYSQL_PASSWORD: 'kagamiya'

としていました。
しかし、そもそもkagamiyaなんていうユーザーはCircleCIのMySQLイメージには登録されていません。(開発環境のdbコンテナにはもちろんありますが)
そこで必然的にrootを使うことになるので、以下のようにしました。

DB_USER: 'root'
DB_PASSWORD: 'rootpass'

MYSQL_USER: 'root'
MYSQL_PASSWORD: 'rootpass'

しかし、上手くいきませんでした。
というのは、rootユーザーのデフォルトのパスワードは設定されていないからです(多分)。
そういうわけで、パスワードを空にしても上手くいきません。

DB_USER: 'root'
DB_PASSWORD: ''

MYSQL_USER: 'root'
MYSQL_PASSWORD: ''

というわけで、ユーザーはroot、パスワードはMYSQL_ALLOW_EMPTY_PASSWORD: 'true'として(空を許可?)で接続しなければならないようです。

      - image: circleci/ruby:2.6.5-node
        environment:  
          BUNDLER_VERSION: 2.1.4
          RAILS_ENV: test
          DB_HOST: 127.0.0.1
          DB_USERNAME: 'root'
          DB_PASSWORD: ''
      - image: circleci/mysql:5.7 
        environment:
          MYSQL_ALLOW_EMPTY_PASSWORD: 'true'

ここでまたまたエラーを吐きました。

Access denied for user 'root'@'127.0.0.1'

ここの理解に苦しんだのですが、どうやら'root'@'127.0.0.1'というユーザーはいないということのようです。
mysqlのユーザー一覧を見てみると、'root'@'%'やら'root'@'localhost'はいますが、'root'@'127.0.0.1'はいません。
mysqlのホスト名(DB_HOST)にlocalhostを使うか127.0.0.1を使うかは、また別の問題として色々あるようです。
しかしながら、今は接続することが最優先なので、rootのホストとして%を指定してやります。
(%は「全てのホスト」という意味だそうですが、よく分かりません)

MYSQL_ROOT_HOST: '%'
MYSQL_ALLOW_EMPTY_PASSWORD: 'true'

理由はどうあれ、これでやっと接続できました。

次に、CircleCIのイメージでは独自のデータベース設定を使うため、database.ymlをdatabase.yml.ciで上書きします。
データベースのセットアップ前に処理を追加します。

      - run:
          name: Use specific database.yml # CircleCI用のデータベース設定を使う
          command: mv config/database.yml.ci config/database.yml

私の環境では、指定したデータベース(webapp_test)が無いと怒られたので、データベースの作成を行いました。

      - run:  
          name: Database setup  
          command: |
            bin/rails db:create
            bin/rails db:schema:load --trace  

(ここは次回以降の実行でエラーの原因になるかも…?)

まとめ

MySQLの仕様とconfigの内容は理解しきれていません。
configのサンプルは多くの方がアップしておられますが、詳細まで解説されているものが少ない印象です。
よって、自分が理解できていない部分でエラーとなればまたハマる可能性が大ですね…

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む