- 投稿日:2021-01-24T23:07:00+09:00
MySQLパーティションを使い、バッチ処理で日毎にデータを登録、取得するSQL
バッチ処理で、自動でパーティション追加、データ登録、パーティション削除をする処理を作り、それに必要なSQL部分を抜粋しました。
やりたいこと(実際に作った処理とは異なります)
- あるゲームのプレイ結果を、日時で集計し、集計結果を画面に表示したい。
- バッチ処理で、集計し、結果を過去1週間分を保持し、過去分は毎日削除したい。
- 最新のパーティションからデータを取得したい。
事前準備
テーブルを作る
CREATE TABLE IF NOT EXISTS `game_point_log` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID', `user_id` INT NOT NULL COMMENT 'ユーザーID', `point` INT UNSIGNED NOT NULL COMMENT 'ポイント', `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '登録日時', `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新日時', PRIMARY KEY (`id`, `created_at`)) ENGINE = InnoDB PARTITION BY RANGE COLUMNS(created_at)( PARTITION p20210113 VALUES LESS THAN ('2021-01-13'),
- パーティションの種類は、「RANGE」にします。
- パーティションのキーには、created_atを使います。
- パーティションのキーは、PRIMARY KEYにしないといけないので、id、created_atをプライマリーキーに指定します。
- パーティションはバッチ処理で追加しますが、1つだけ事前に作っておきます。
バッチ処理
以下の順で処理します。
1. 追加するパーティション名を決める。
ここでは日付とします。(例:2021年1月23日なら、p20210123)
2. パーティションを追加する
ALTER TABLE game_point_log ADD PARTITION ( PARTITION [パーティション名] VALUES LESS THAN ('[日付条件、どの日時までを対象にするか?]') ); 例: ALTER TABLE game_point_log ADD PARTITION ( PARTITION p20210124 VALUES LESS THAN ('2021-01-24') );
- 日付をキーに作ります。
- LESS THANは、既存のパーティションの条件〜指定した日付までの間のデータを入れるという意味なので、今日の日付データを入れたい場合は、翌日の日付にすると良いです。
3. データを登録する
DB側でパーティションに入れてくれるので、通常のINSERT文で登録できます。
4. 古くなったパーティションを削除する
古くなったパーティションを一発で削除するコマンドはない(たぶん・・・。)
ので、今使っているパーティションを取得して、その中から削除したいパーティションを取得します。EXPLAIN PARTITIONS SELECT * FROM game_point_logSQLを実行すると、partitionsという項目で、カンマ区切りでパーティション名が取得できます。
例:p20210124,p20210125,p20210126それをカンマで分割で配列化し並び替えたりして消したい等でパーティションを探し、
ALTER TABLE game_point_log DROP PARTITION [パーティション名] 例: ALTER TABLE game_point_log DROP PARTITION p20210124で削除します。
取得処理(最新のパーティションからデータ取得)
最新のパーティションを自動で選択して取得してくるコマンドはない(たぶん・・・。)
ので、取得する時も、古くなったパーティションを削除する時に使ったEXPLAINコマンドを使って、partitionsを取得し、自分で探します・・。調べたパーティションを指定して、
SELECT * FROM game_point_log PARTITION ([パーティション名]); 例: SELECT * FROM game_point_log PARTITION (p20210113);とすると、対象のパーティションに入っているデータ(対象の日付データのみ)が取得できるので、where文なしで取得でき便利です。
参考URL
- 投稿日:2021-01-24T21:47:26+09:00
docker-composeでのMySQL環境構築(タイムゾーン、UTF-8対応)
はじめに
ローカル開発環境を立てるためにMySQLのコンテナを用意することがよくあるが、(主にタイムゾーンとUTF-8の対応で)毎回同じようなところで詰まって調べていたので、備忘録も兼ねて自分的な「いつものやつ」を残しておく。
結論
https://github.com/p5750/docker-mysql-sample
ディレクトリ構成
./docker/myqsl/data
をvolumeとすることでDBのデータを永続化する。
なお、初回起動時にこのディレクトリに余計なファイルが入っているとコケることがあるので注意。
.gitkeep
は置いておいて大丈夫だった。構成. ├── docker │ └── mysql │ ├── data │ │ └── .gitkeep │ └── Dockerfile ├── .env ├── .gitignore └── docker-compose.yml各ファイルの内容
Dockerfile
MySQLのバージョンはお好みで。
タイムゾーンをAsia/Tokyo
に、ロケールはen_US.UTF-8
にしている。 日本語にしたければja_JP.UTF-8
にする。DockerfileFROM mysql:8.0.23 RUN apt-get update && \ apt-get install -y tzdata locales && \ cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && \ echo "en_US.UTF-8 UTF-8" > /etc/locale.gen && \ locale-gen ENV LANG en_US.UTF-8 CMD ["mysqld", "--character-set-server=utf8mb4", "--collation-server=utf8mb4_unicode_ci"].env
適宜書き換える。
.envMYSQL_ROOT_PASSWORD=root_password MYSQL_DATABASE=sample MYSQL_USER=sample_user MYSQL_PASSWORD=sample_password MYSQL_PORT=3306 ADMINER_PORT=8080docker-compose.yml
version
は特に理由がなければ最新でよさそう。使えるものは公式のドキュメントでわかる。
Adminerはお好みで入れる。個人的にはローカル用なら入れといて損はしないかなと思っている。docker-compose.ymlversion: '3.8' services: db: build: ./docker/mysql env_file: - .env ports: - "${MYSQL_PORT}:3306" volumes: - ./docker/mysql/data:/var/lib/mysql adminer: image: adminer:4.7.8-standalone ports: - "${ADMINER_PORT}:8080".gitignore
データ永続化用の
docker/mysql/data
と、認証情報が含まれる.env
は必ずignoreする。
.idea
はJetBrainsのIDE用。VSCodeなら.vscode
になる。.gitignoredocker/mysql/data .env .ideaコマンド
Dockerの基本操作は本稿の趣旨ではないので割愛する。
DBを抹消して作り直すとき
ゴミが残るので、必ず先に
down
する。docker-compose down # コンテナを止めて削除する rm -rf ./docker/mysql/data # volumeでマウントしているデータをディレクトリごと消す mkdir ./docker/mysql/data # ディレクトリを作り直す touch ./docker/mysql/data/.gitkeep # gitに.gitkeepをコミットしているなら作り直す。 # ※差分出てるはずなのでgit resetとかで復旧してもよい。 docker-compose up -d # 再度立ち上げる。Dockerfileに変更が入っていたら --build も必要リンク集
- 公式MySQLコンテナ https://hub.docker.com/_/mysql
- 環境変数とかたまに調べる
- 公式Adminerコンテナ https://hub.docker.com/_/adminer
- 投稿日:2021-01-24T20:20:19+09:00
AWS_WEB3層環境構築③
前回の記事
https://qiita.com/shinichi_yoshioka/items/e358f57a3ecb7735c091
前々回(初回)の記事
https://qiita.com/shinichi_yoshioka/items/7226b9ebaad06c569c80
構成図
前回の続きをやっていく。
VSCodeすら使ったことなかったが、VSCodeの拡張機能にdrawioというものがあって、
カッコ良いアイコンなどが使えるのを教えてもらった。
さっそく使ってみたが、色のセンスはお察しだ。初回記事ではDBの切り替えをしたいと書いたが、まずは正常な状態を作りたい。
WEBサーバにはリバースプロキシの設定(To:APサーバ)をし、APサーバでJSPを使って、
DBを参照できることを正常な状態とすることにした。◆リバースプロキシの設定
対象:WEBサーバ
/etc/nginx/conf.d 配下にserver.confというファイルを作る。cd /etc/nginx/conf.d vi server.confserver.confの中身は、APサーバのプライベートアドレス:ポート番号(8080)を指定した。
#server.conf server{ location / { proxy_pass http://172.16.3.246:8080/; } }nginxのサービスを再起動(systemctl restart nginx)して、ブラウザにWEBサーバのパブリックアドレスを入力し、
APサーバのtomcatのページが表示されることを確認した。
[自宅PC]-[Internet]-[WEBサーバ]-[APサーバ] ←ここまでのイメージ
◆JDBC for MySQLのインストール
対象:APサーバ
APサーバにJDBCドライバーをインストールしようと思ったのだが、AmazonLinux用がなかった^^;;;
ということで、前回作ったAPサーバはぶっ壊して、再度RedHatで作りなおして、Tomcatのインストールまでは済んだとこ。
これがクラウドの良いところである。(オンプレでOS選定ミスってたら、首飛んでた)
EIPを使っていたので、記事の不整合もない!
OSやJavaのバージョン、Tomcatの状態はこんな感じ。
MySQLコミュニティサーバからJDBCドライバーのRPM(RHEL用)をダウンロードしインストールしようとしたが、
依存関係でopenjdk-headlessが必要と言われた。
yumでopenjdk-headlessをインストール後に、mysql-connector-javaをRPMコマンドでインストールした。wget https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.23-1.el8.noarch.rpm yum install java-1.8.0-openjdk-headless -y rpm -ivh mysql-connector-java-8.0.23-1.el8.noarch.rpm rpm -qa | grep mysql◆サンプルデータベースの用意
対象:DBサーバ
参照するためのDBを用意するため、以下からworld databaseをインポートすることにした。
https://dev.mysql.com/doc/index-other.html
まずはDBサーバにssh接続し、wgetコマンドにてダウンロードした。
gz形式だったため、gunzipコマンドにて解凍した。wget https://downloads.mysql.com/docs/world.sql.gz gunzip world.sql.gz次にMySQLにログインし、SOURCEコマンドにて、world.sqlをインポートした。
mysql> SOURCE root/world.sql;/rデータベースに"world"がインポートされた。
mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | TEST | | auth | | mysql | | performance_schema | | sys | | world | +--------------------+データベースをworldに切り替え、テーブルが存在することを確認した。
mysql> use world; mysql> show tables; +-----------------+ | Tables_in_world | +-----------------+ | city | | country | | countrylanguage | +-----------------+ 3 rows in set (0.00 sec)MySQLはデフォルトで、外部からのアクセスを許可されていないため、
以下の権限追加をした。
権限追加後は、FLUSHにて権限を再読み込みした。
※*(すべてのデータベース).*(すべてのテーブル)を認識できるようにユーザ名@接続元IPアドレス(APサーバ)へ権限を与えるmysql> GRANT all ON *.* TO root@'172.16.3.246' IDENTIFIED BY 'MySQLのパスワード'; mysql> FLUSH PRIVILEGES; ←権限関係の再読み込み◆JDBCドライバの配置とサンプルJSPの作成
対象:APサーバ
JDBCドライバーは/lib配下に配置しないとダメらしく、初期配置から移動させた。mv /usr/share/java/mysql-connector-java.jar /opt/tomcat/lib/
JSPについては初心者でggりまくった結果、JSPも配置が重要らしく、
/opt/tomcat/webapps配下にjspディレクトリを作成し、パーミッションを750にし、所有者はtomcatに変更した。cd /opt/tomcat/webapps mkdir jsp chown tomcat:tomcat jsp chmod 750 jsp/opt/tomcat/webapps/jsp配下には、test.jspを作成したが、
JSPの知識がなさすぎて、以下URLを参考にさせていただいた。
https://michael-e29.hatenadiary.org/entry/20111107/1320630444jdbc:mysql://172.16.3.6:3306/world
↑DBサーバのアドレス:ポート番号/データベース名
※worldはデータベース名。
※ID,Name,CountryCode,District,Populationはデータベースのカラム名###test.jspの中身### <%@ page contentType="text/html; charset=utf-8" import="java.sql.*" %> <html> <head> <title>DB参照テスト</title> </head> <body> <h1>DB参照テスト</h1> <tr> <td>ID</td> <td>Name</td> <td>CountryCode</td> <td>District</td> <td>Population</td> </tr> <% Class.forName("com.mysql.jdbc.Driver"); Connection conn=DriverManager.getConnection("jdbc:mysql://172.16.3.6:3306/world?" + "user=root&password=MySQL@001&useUnicode=true&characterEncoding=utf-8"); Statement st=conn.createStatement(); ResultSet res = st.executeQuery("select * from city;"); while(res.next()){ out.println("<tr>"); out.println("<td>" + res.getString("ID") + "</td>"); out.println("<td>" + res.getString("Name") + "</td>"); out.println("<td>" + res.getString("CountryCode") + "</td>"); out.println("<td>" + res.getString("District") + "</td>"); out.println("<td>" + res.getString("Population") + "</td>"); out.println("</tr>"); } st.close(); conn.close(); %> </table> </body> </html>APサーバとDBサーバの連携の準備ができたので、APサーバのTomcatサービスを再起動。
あとDBサーバのmysqldサービスを再起動し、ブラウザにJSPのパスを入力する。
ちなみにJSPファイルを置いたパスは /opt/tomcat/webapps/jsp/test.jsp だが、
ブラウザでは/webapps/配下を入力する。
http://WEBサーバのパブリックアドレス/jsp/test.jsp
ぐちゃぐちゃだけど、一応DB参照できたので
一応WEB3層構成はできた・・・。
めちゃ時間かかったけど、つまづきながらハマりながら色々覚えれたので良かったです。
次回はNATゲートウェイの作成と、AnsibleでJSP内の文字列を置換することで
参照するDBを切り替えようと思います。つづく
- 投稿日:2021-01-24T18:00:16+09:00
Docker環境のDB(MySQL)コンテナに接続する方法
やりたいこと
Dockerで作成したDB(MySQL)コンテナに入ってデータベースをいじりたい。
ソースコード
長いので折りたたんでいます。
docker-compose.yml
docker-compose.yml#docker-compose.ymlのバージョン version: "3.8" #docker volumeの設定 volumes: docker-volume: #services以下に各コンテナの設定を書く services: #Webサーバーのコンテナ web: image: nginx:1.18 ports: - "8000:80" depends_on: - app volumes: - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf - .:/var/www/html #アプリケーションのコンテナ app: build: ./docker/php volumes: - .:/var/www/html #データベースのコンテナ db: image: mysql:5.7 ports: - "3306:3306" environment: MYSQL_DATABASE: db_name MYSQL_USER: db_user MYSQL_PASSWORD: db_password MYSQL_ROOT_PASSWORD: root TZ: "Asia/Tokyo" volumes: - docker-volume:/var/lib/mysql # phpMyadominのコンテナ作成 phpmyadmin: image: phpmyadmin/phpmyadmin depends_on: - db environment: - PMA_ARBITRARY=1 - PMA_HOSTS=db - PMA_USER=db_user - PMA_PASSWORD=db_password ports: - "8080:80" volumes: - ./docker/phpmyadmin/sessions:/sessions
.env
.envAPP_NAME=Laravel APP_ENV=local APP_KEY=base64:8EM1Yt7LJjZSRaEjtdhXCVShJEI0GGo6FG6IZHXCuis= APP_DEBUG=true APP_URL=http://localhost:8000 LOG_CHANNEL=stack DB_CONNECTION=mysql DB_HOST=db DB_PORT=3306 DB_DATABASE=db_name DB_USERNAME=db_user DB_PASSWORD=db_password BROADCAST_DRIVER=log CACHE_DRIVER=file QUEUE_CONNECTION=sync SESSION_DRIVER=cookie SESSION_LIFETIME=120 REDIS_HOST=127.0.0.1 REDIS_PASSWORD=null REDIS_PORT=6379 MAIL_DRIVER=smtp MAIL_HOST=smtp.mailtrap.io MAIL_PORT=2525 MAIL_USERNAME=null MAIL_PASSWORD=null MAIL_ENCRYPTION=null AWS_ACCESS_KEY_ID= AWS_SECRET_ACCESS_KEY= AWS_DEFAULT_REGION=us-east-1 AWS_BUCKET= PUSHER_APP_ID= PUSHER_APP_KEY= PUSHER_APP_SECRET= PUSHER_APP_CLUSTER=mt1 MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}" MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"試したこと
rootのパスワードを使っても接続できない。
terminal$ docker-compose exec db bash root@74539701c71c:/# mysql -u user -p Enter password: ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)ちなみにホストとポート番号を確認すると...
Name Command State Ports -------------------------------------------------------------------------------- laravel_todo_api_app_1 docker-php-entrypoint Up 9000/tcp php-fpm laravel_todo_api_db_1 docker-entrypoint.sh Up 0.0.0.0:3306->3306/tcp mysqld , 33060/tcp laravel_todo_api_phpmy /docker-entrypoint.sh Up 0.0.0.0:8080->80/tcp admin_1 apac ... laravel_todo_api_web_1 /docker-entrypoint.sh Up 0.0.0.0:8000->80/tcp ngin ...
解決法
$ mysql -h 127.0.0.1 -P 3306 -u root -pホストは
localhost
と指定するとローカルマシンのmysql
ソケットを探しに行くのでエラーになる。
ポート番号の指定は大文字で-P
参考
参考にさせていただきました。ありがとうございました。
【Docker】コンテナ内のデータベース閲覧(ローカル,EC2)
docker内のMySQLに接続したい
- 投稿日:2021-01-24T16:34:40+09:00
XAMPPの環境設定でFound Path: " " -k runservice Expected Path: " " -k runservice とエラーが出た時の対処法
はじめに
Windows環境でXAMPPの環境構築をした時にハマった内容と解決した方法をまとめます。
環境
Windows 10
XAMPP 8.0.1ハマった内容
XAMPPで環境構築をしたところ、Apache,mysqlともに下記エラーが出て使えない。
やってみたこと
以前(私以外の方が)ApacheとMySQLを同PCにインストールして使っていたようで、
アンインストールがしっかりされているかの確認と対象ファイルの削除をした。
しかし、エラーの表示内容は変わらずだった。参考にしたページ
https://stackoverflow.com/questions/30547759/apache-service-detected-with-wrong-path/52457073
解決した方法
コマンドプロンプトで下記実行しました。
C:¥WINDOWS¥system32>sc delete Apache2.4 C:¥WINDOWS¥system32>sc delete MySQL結果的にこれで問題が解決しました。
まとめ
どうやらアンインストールしてもファイルが残ってたようです。
無事にXAMPPが使えるようになりました。
メキシコの方に感謝します。
- 投稿日:2021-01-24T12:43:41+09:00
DockerでPHP環境を作る(mac)
はじめに
勤め先でもPHPを使っており自宅でPHPを勉強するために、ローカルでPHP開発環境を整えることにしました。
MAMP(マンプ)でも良いかなと思いましたが、せっかくなのでDockerで作ってみます。
(開発中に色々詰まってしまったところがあるので、備忘録がわりのメモです。)手順
- Dockerによる環境構築
- ファイル・ディレクトリの準備
- 各ファイルの作成
- コンテナを起動
- サイトにアクセス
1. Dockerによる環境構築
Dockerを使うには、「Docker for Mac」のインストールが必要です。
Qitaに素敵な記事がたくさんあるので、ここでの説明は省略します。今回はDockerで以下のコンテナを使います。
- nginx
- PHP
- MySQL
- PHPMyAdmin
使用するコンテナが複数あるので、今回は「Docker Compose」を使って環境を作っていきます。
2. ディレクトリ・ファイルの準備
Dockerを起動する場所を作成しましょう。
まずはフォルダを1つ用意し、次のようなファイル構造となるようにしてください。
用意するフォルダは何でも良いのですが、今回は「php-test」という名前で作成します。php-test ├── docker-compose.yml ├── mysql │ └── data ├── nginx │ └── nginx.conf ├── php │ ├── Dockerfile │ └── php.ini └── www └── html └── index.php3. 各ファイルの作成
用意したファイルの中身を作成していきます。
中身を作成するファイルは、docker-compose.yml
、nginx.conf
、Dockerfile
、php.ini
、index.php
の5つです。docker-compose.yml
version: '3' services: nginx: image: nginx:latest ports: - 8080:80 volumes: - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf - ./www/html:/var/www/html depends_on: - php php: build: ./php volumes: - ./www/html:/var/www/html depends_on: - db db: image: mysql:5.7 ports: - 13306:3306 volumes: - ./mysql/data:/var/lib/mysql environment: MYSQL_ROOT_PASSWORD: secret phpmyadmin: image: phpmyadmin/phpmyadmin:latest ports: - 8888:80 depends_on: - dbnginx.conf
server { listen 80; server_name _; root /var/www/html; index index.php index.html; access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; location / { try_files $uri $uri/ /index.php$is_args$args; } location ~ \.php$ { fastcgi_pass php:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } }Dockerfile
FROM php:7.2-fpm COPY php.ini /usr/local/etc/php/ RUN docker-php-ext-install pdo_mysqlphp.ini
date.timezone = "Asia/Tokyo"index.php
設定情報を確認するために
phpinfo();
としていますが、なんでもいいです。<?php phpinfo(); ?>4. コンテナを起動
いよいよコンテナ起動です。
ターミナルを開き、docker-compose.yml
があるフォルダ(今回はphp-test)に移動します。下記コマンドで、php-testフォルダに移動。
cd php-test下記コマンドで、コンテナを起動。
docker-compose up -d以下のような記述がターミナル内に表示されたら、起動は成功です。
Creating php_01_db_1 ... done Creating php_01_phpmyadmin_1 ... done Creating php_01_php_1 ... done Creating php_01_nginx_1 ... doneなお下記コマンドで、コンテナを停止できます。
docker-compose stop5. サイトにアクセス
最後にサイトにアクセスします。
それぞれ下記画像のような画面が表示されたら、成功です。
お疲れ様でした。Webサイト:http://localhost:8080/
ユーザー名:root
パスワード:secret以上です。
最後までお読みいただき、ありがとうございました。
お世話になったサイト
- [PHP]DockerでPHP, MySQL(MariaDB), nginxを使ったローカル開発環境を構築する
- Docker Composeでphpでmysqlにアクセスする
- docker-composeでPHPとMySQLを連携させてみる
- Docker ドキュメント
【補足】Qitaに投稿されているDockerインストール記事
下記の中から自分に合いそうなものを1つ選んで、進めてみると良いと思います。
- 投稿日:2021-01-24T11:31:43+09:00
【Laravel】テーブル名が長い時に、Laravel7から導入された外部キー制約をつけるマイグレーションエイリアスを使う方法
久しぶりのQiitaへの投稿です。
未経験からエンジニアに転職をして、2年目に突入したバックエンドエンジニアです。
DBに外部キー制約を追加をしようと思い、Laravel7から使えるエイリアスでmigrationファイルを作成していました。
ただ、テーブル名が長い時にハマってしまい、できる人には当たり前すぎる内容かもしれませんが、振り返りの意味も込めて投稿します。開発環境
- 言語:PHP7.4.8
- フレームワーク:Laravel7.28.3
- DB:MySQL8.0.22
テーブル名が短い時
目標のテーブル構造
公式ガイドでもよく使われるような下記のようなテーブルを作り、postsテーブルのuser_idに外部キー制約をかけることを目指します。
Laravel7より前の時の記法
Laravel7より前のバージョンで外部キー制約をつけるためには、下記のようにmigrationファイルを作成をしていました。
一度user_id
のカラムを作成してから別の行で外部キー制約をつけています。Schema::create('posts', function (Blueprint $table) { $table->id(); $table->unsignedBigInteger('user_id'); $table->foreign('user_id')->references('id')->on('users'); });そんなに難しくもないですが、少しだけ面倒です。
Laravel7以降で使えるエイリアスを使用
そこでLaravel7以降では下記のようなエイリアスができ、
foreignId
とconstrained()
を使えば一行でも外部キー制約をかけることができるようになりました。
短くて楽ですね。Schema::create('posts', function (Blueprint $table) { $table->id(); $table->foreignId('user_id')->constrained(); $table->timestamps(); });テーブル名が長い時
目標のテーブル構造
問題のパターンです。下記のようなテーブルを想定して試してみます。
名前は適当に僕が好きなスポーツからとってきましたが、とりあえず2つのテーブル名が長く文字数の合計が一定数を超えると同じ現象になります。Laravel7以降で使えるエイリアスを使用
せっかく新しい記法があるということなので、
foreignId
を使った新しいエイリアスを使ってやってみます。Schema::create('national_basketball_association_players', function (Blueprint $table) { $table->id(); $table->string('name'); $table->foreignId('national_basketball_association_team_id')->constrained(); $table->timestamps(); });キー名が長すぎるようで下記のようなエラーが出てしまいました。
SQLSTATE[42000]: Syntax error or access violation: 1059 Identifier name 'national_basketball_association_players_national_basketball_association_team_id_foreign' is too long (SQL: alter table `national_basketball_association_players` add constraint `national_basketball_association_players_national_basketball_association_team_id_foreign` foreign key (`national_basketball_association_team_id`) references `national_basketball_association_teams` (`id`))通常であれば、Laravelのスキームビルダの制約命名規則に従い、自動的に外部キー制約の名前は変換されるため、外部キー制約名は下記のようになります。
(接続元テーブル名)_(外部キー名)_foreign
しかし、今回この命名規則に当てはめようとすると、
national_basketball_association_players_national_basketball_association_team_id_foreign
という名前になりますが、これは文字数が長すぎるということでエラーになってしまいました。Laravel7以降で使えるエイリアスを使用(nameでキー名変更)
同じようなことが他の制約を作る時でも起こっていたのを思い出します。
name
メソッドを使えば直せたような気がするのでname
メソッドを使って試してみました。
外部キー制約名は省略した、basketball_players_team_id_foreign
を設定します。Schema::create('national_basketball_association_players', function (Blueprint $table) { $table->id(); $table->string('name'); $table->foreignId('national_basketball_association_team_id')->constrained()->name('basketball_players_team_id_foreign'); $table->timestamps(); });先程と違い、今度はエラーも起こらず、migrationファイルが無事に通りました。
念のためにdatabaseを確認してみます。外部キー制約がついていません。。。
多分routeを作成する時に使うname
メソッドと勘違いしていたのですが、ここで結構ハマってしまいました。migration関連でnameメソッドは用意されていなさそうでした。
エイリアスメソッドのコードを確認
改めて今回のエイリアスの
foreignId
メソッドを確認してみます。public function foreignId($column) { $this->columns[] = $column = new ForeignIdColumnDefinition($this, [ 'type' => 'bigInteger', 'name' => $column, 'autoIncrement' => false, 'unsigned' => true, ]); return $column; }引数がカラム名のみで1つしか受け付けていないので、キー名を短くして渡そうとしても無理そうです。
また、
foreignId
の後に使っているconstrainedも確認してみます。public function constrained($table = null, $column = 'id') { return $this->references($column)->on($table ?? Str::plural(Str::beforeLast($this->name, '_'.$column))); }こちらも外部キー制約名を渡す引数がなく、無理そうです。
結果:Laravel7以降で使えるエイリアスを使用(indexでキー名変更)
下記のように
index
メソッドを使って、外部キー名を指定するとうまくいきました。Schema::create('national_basketball_association_players', function (Blueprint $table) { $table->id(); $table->string('name'); $table->foreignId('national_basketball_association_team_id')->constrained()->index('basketball_players_team_id_foreign'); $table->timestamps(); });公式ガイドを見てみると
index
メソッドは「基本的なインデックス追加」とだけ書いてありましたが、外部キー制約の名前を作成をする際にも使えるようです。
uniqueキー作るときは、第2引数にインデックスキー名指定できるのに対して、外部キー制約の場合はindex
メソッドを使わないといけなさそうなので書き方が結構違いますね。https://readouble.com/laravel/7.x/ja/migrations.html
※何か誤解していたり、他にいい方法があれば教えていただけると助かります!!!
Laravel7より前の時の記法でやる方法
もちろん元々使っていた記法を使って、
foreign
メソッドの中の第2引数に外部キー制約名を指定してもうまくいきます。
このやり方は元々やったことあったのですが、ハマるくらいなら最初からこっち使っておけばよかったなと思いました、、、Schema::create('national_basketball_association_players', function (Blueprint $table) { $table->id(); $table->string('name'); $table->unsignedBigInteger('national_basketball_association_team_id'); $table->foreign('national_basketball_association_team_id', 'basketball_players_team_id_foreign')->references('id')->on('national_basketball_association_teams'); $table->timestamps(); });感想
チュートリアルで使うようなusersとかpostsとかのテーブルと違い、実務ではテーブル名が長くなるケースは命名規則によりけりだと思いますが、結構発生しそうなイメージなので意外と困ることもあるかなと思います。
また、今回少し深く調べてみて、フレームワークのコードをより深くみるようにしたのはよかったなと思いました。
それにしても
name
メソッドを使ったときに何故かmigrationファイル通るのに外部キー制約がついていないのは結構たちが悪かったです。。。
- 投稿日:2021-01-24T00:13:18+09:00
railsで簡単ログイン(ゲストユーザーログイン機能)を実装する方法
今回はポートフォリオを見てもらう確率を上げるために必須な、簡単ログインの実装方法を書いていきます。
開発環境
Mac OS Catalina 10.15.7
ruby 2.6系
rails 6.0系前提
devise導入済み
通常の新規登録、ログイン、ログアウト機能に関しては実装済み
投稿機能ができるアプリを作っており、postsモデルとpostsコントローラーが登場しますが、ご自身の例に置き換えてご理解ください。ルーティング設定
まずはルーティングを設定します。
# HTTPメソッドはpostで、'/posts/guest_sign_in'というURLでpostsコントローラーのnew_guestアクションを参照する post '/posts/guest_sign_in', to: 'posts#new_guest'routes.rbRails.application.routes.draw do devise_for :users root to: "posts#index" # 次の1行を追加 post '/posts/guest_sign_in', to: 'posts#new_guest' resources :posts do resources :comments, only: [:create, :destroy] collection do get 'search' end end resources :users, only: :show post 'like/:id' => 'likes#create', as: 'create_like' delete 'like/:id' => 'likes#destroy', as: 'destroy_like' endコントローラーにnew_guestアクションを記述
それでは次にpostsコントローラーにアクションを定義していきます。
deviseのメソッド find_or_create_by や sign_in を使っていますので、気になる方は調べてみてください。
また、パスワードは SecureRandom.alphanumeric を用いてランダム生成しつつも、全て英字、全て数字のパスワードが生成されないように工夫しています。参考:https://qiita.com/take95/items/61b181449b38d4415fc3
posts_controller.rbdef new_guest # emailでユーザーが見つからなければ作ってくれるという便利なメソッド user = User.find_or_create_by(email: 'guest@example.com') do |user| # 自分はユーザー登録時にニックネームを必須にしているのでこの記述が必要 user.nickname = "ゲスト" # 英数字混合を必須にしているので、ランダムパスワードに、英字と数字を追加してバリデーションに引っかからないようにしています。 user.password = SecureRandom.alphanumeric(10) + [*'a'..'z'].sample(1).join + [*'0'..'9'].sample(1).join end # sign_inはログイン状態にするデバイスのメソッド、userは3行目の変数userです。 sign_in user # ログイン後root_pathに飛ぶようにしました。 redirect_to root_path endまた、deviseでメール確認機能を実装済みの場合、user.confirmed_at = Time.nowを追加する必要があります。
posts.controller.rbdef new_guest user = User.find_or_create_by(email: 'guest@example.com') do |user| user.nickname = "ゲスト" user.password = SecureRandom.alphanumeric(10) + [*'a'..'z'].sample(1).join + [*'0'..'9'].sample(1).join # 以下一文を追加 user.confirmed_at = Time.now end sign_in user redirect_to root_path endビューを追加
簡単ログインのリンクをヘッダーに追加します。
自分の場合は部分テンプレートに切り出しているので、その部分テンプレート内の記述を編集します。コンソールでrails routesを打ってパスを確認しましょう。その後ヘッダーに次の1文を追加します。
<li><%= link_to 'ゲストログイン(閲覧用)',posts_guest_sign_in_path, class: "guest-login", method: :post %></li>この時、HTTPメソッドをつけ忘れないように注意してください
_header.html.erb<nav> <ul class='lists-right'> <% if user_signed_in? %> <li><%= link_to current_user.nickname, user_path(current_user.id), class: "user-nickname" %></li> <li><%= link_to 'ログアウト', destroy_user_session_path, method: :delete, class: "logout" %></li> <% else %> # 以下1文を追加 <li><%= link_to 'ゲストログイン(閲覧用)',posts_guest_sign_in_path, class: "guest-login", method: :post %></li> <li><%= link_to 'ログイン',new_user_session_path, class: "header-login" %></li> <li><%= link_to '新規登録',new_user_registration_path, class: "sign-up" %></li> <% end %> </ul> </nav>以上で簡単ログイン(ゲストユーザーログイン機能)を実装できました。
参考になれば幸いです。