- 投稿日:2020-05-25T12:33:25+09:00
phpMyAdminでテーブル構造を表示させる方法
phpMyAdminのバージョン5.0.1で言語が英語だと構造が表示されるのに、下記のように日本語に変更すると表示されない場合の対処法です。
こちらの記事を参考にしています。
解決!phpMyAdmin テーブル構造の内容が表示されない問題作業環境
OS : Windows 10 HOME
phpMyAdmin : 5.0.1Xamppのインストール方法
インストール方法については、別記事にて解説しています。
【環境構築】Windows10にXAMMPをインストール方法解決方法
「Util.php」書き換える
Xamppを起動している場合は、停止します。
C:\xampp\phpMyAdmin\libraries\classes
に移動します。
Util.php
をエディタで開きます。
1617行目の記述$ret = strftime($date, (int) $timestamp);
を以下のように変更します。$ret = strftime('%Y-%B-%d %H:%M', (int) $timestamp);
Xamppを再起動して、phpMyAdminを開くと下記のように表示されました。
表示されない原因
下記によると、表示されない原因は以下のとおりです。
strftime()の第一引数に、$dateの中身として%Y 年 2 月 %d 日 %H:%Mが渡されている。これ自体は仕様通りで問題が無いように見える。
しかし、strftimeを処理する際に、依存するWindowsのCライブラリでは、文字列を一度Shift-JISに変換している。つまり、UTF-8→SJIS→UTF-8の変換が行われている。ここで、漢字の「月」の字が文字化けを起こし、不正なマルチバイト文字と認識され、strftime()がfalseを吐いている、というのが真相である。
引用元:phpMyAdmin5.0.1の「状態」タブがFatal Errorで表示されないバグ
以上、phpMyAdminでテーブル構造を表示させる方法でした。
- 投稿日:2020-05-25T10:34:38+09:00
丁寧にNginx + MySql + Rails6 (Using Webpacker) on Dockerな開発環境を構築します。
Nginx + MySql + Rails6 (Using Webpacker) on Dockerな開発環境を構築します。
新しくプロジェクトから作成する機会なんてそうそうないので、以下手順をできるだけ要点を抑えつつまとめました。今回しないこと。
プロジェクトの立ち上げにコミットするために以下についてはしない。
- 実際のコーディングはしない。1
- RSpec, Rubocopのようなコードの品質を上げるための設定はしない。
- 開発効率を上げるためのgem登録などは設定しない。
- i18nのような初期に設定するようなものも今回は設定しない。
asset pipelineの代わりにWebpackerを使用しています。なので、Webpackerについてはこの辺をざっと見ておくとよいです。
- 参考:webpacker/blob/master/docs/docker.md
- 参考:webpacker/blob/master/docs/deployment.md
- 参考:Rails 6+Webpacker開発環境をJS強者ががっつりセットアップしてみた(翻訳)
- 参考:Webpacker使うなら最低限これだけは知っておいてほしいこと
- 参考:Webpackerが提供しているコマンドの内部処理を追ってみた
- 参考:Webpacker の基本的な仕組み
各バージョンについて
name version Ruby(on docker) 2.6.5 Ruby on Rails 6.0.3 node 10.19.0 yarn 1.22.4 bundler 2.1.4 webpacker 5.1 docker 19.03.8 docker-compose 1.25.5 MySQL(on docker) 5.7 nginx(on docker) 1.17.3 ※ (on docker)は、Dockerfileで指定しているバージョンです。
Rails6ではyarnを利用するので、事前にインストールしておく。
brew install yarn
1. プロジェクト構築手順
1-1. Githubにプロジェクトを作成し、git cloneしておく。
# githubでプロジェクトを作成しておく。 $ git clone git@github.com:{github_acount}/{project_name}.git $ cd {project_name}1-2. ruby,nodeのバージョンを指定する。
rubyとnodeのバージョンをしているするために
.*-version
ファイルを作成します。
※ anyenvを事前にbrew install anyenv
しておくとよい。
また、rbenv, nodenvの設定については公式を参考にするとよい。
- 参考:GitHub - anyenv/anyenv: All in one for **env1-2-1. Rubyバージョン指定
ruby : 2.6.5
を指定# インストールできるバージョンをの一覧を確認する。 $ rbenv install -l $ rbenv install 2.6.5 $ rbenv local 2.6.5 $ ruby -v ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-darwin18]1-2-2. Nodeバージョン指定
node : v10.19.0
を指定# インストールできるバージョンをの一覧を確認する。 $ nodenv install -l $ nodenv install 10.19.0 $ nodenv local 10.19.0 $ node -v v10.19.01-3 不要なファイルをコミットできないように.gitignoreを作成する。
.gitignoreを作成し、
git commit
しておく。
※ .gitignoreを作成するために事前にbrew install gibo
しておくとよい。# .gitignoreを作成 $ gibo dump macOS Node Ruby Rails VisualStudioCode > .gitignore1-4. GemfileとGemfile.lockを作成する。
$ gem install bundler Successfully installed bundler-2.1.4 Parsing documentation for bundler-2.1.4 Done installing documentation for bundler after 1 seconds 1 gem installed次に、Gemfileを作成する。
# Gemfileを作成する。 $ bundle initGemfileファイルを開きrailsのgemを登録します。
バージョンについてはrails | RubyGems.orgを確認する。
- ./Gemfile
# frozen_string_literal: true source "https://rubygems.org" git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } - # gem "rails" + gem 'rails', '~> 6.0', '>= 6.0.3'bundle installを実行しGemfile.lockファイルを作成します。
$ bundle install Fetching gem metadata from https://rubygems.org/............. Fetching gem metadata from https://rubygems.org/. Resolving dependencies... Using rake 13.0.1 Using concurrent-ruby 1.1.6 ... Using rails 6.0.3 Bundle complete! 1 Gemfile dependency, 43 gems now installed. Use `bundle info [gemname]` to see where a bundled gem is installed.Ruby on Railsプロジェクトを作成するための準備が完了しました。
2. Ruby on Railsアプリケーションを構築する。
Ruby on Railsのアプリケーションを作る。
$ bundle exec rails new . --database=mysql --skip-git --skip-test-unit --skip-sprockets ... ├─ webpack-dev-server@3.11.0 └─ ws@6.2.1 ✨ Done in 3.19s. Webpacker successfully installed
オプション 説明 . カレントディレクトリ名がプロジェクト名 --database=mysql databaseはmysql --skip-git .gitignoreを組み込まない(事前に作成済み) --skip-test-uni Test::Unitを組み込まない --skip-sprockets sprocketsを利用しない 2-1 Ruby on Railsの設定する。
2-1-1 webpackerのバージョンを最新にする。
- ./Gemfile
- gem 'webpacker', '~> 4.0' + gem 'webpacker', '~> 5.1', '>= 5.1.1'2-1-2 MySQLを利用するためにdocker-composeを用意する。
# mysqlのDockerfileとmy.cnfを用意する。 # docker-compose.ymlを用意する。 $ mkdir -p ./docker/mysql $ touch ./docker/mysql/{Dockerfile, my.cnf} $ touch ./docker-compose.yml各ファイルの記述はこちら。
- ./docker/mysql/Dockerfile
FROM mysql:5.7 RUN touch /var/log/mysql/mysqld.log
- ./docker/mysql/my.cnf
[mysqld] collation-server=utf8mb4_general_ci character-set-server=utf8mb4 skip-character-set-client-handshake init-connect = SET NAMES utf8mb4 default-time-zone=+9:00 explicit-defaults-for-timestamp=1 general-log-file=/var/log/mysql/mysqld.log [client] default-character-set=utf8mb4
- ./docker-compose.yml
version: '3.3' services: database: build: context: . dockerfile: ./docker/mysql/Dockerfile environment: MYSQL_ROOT_PASSWORD: password MYSQL_DATABASE: {project_name}_development ports: - "3306:3306" volumes: - ./docker/mysql/my.cnf:/etc/mysql/conf.d/my.cnf - ./log/mysql:/var/log/mysqlmysqlアカウントでrootを利用する場合は、MYSQL_ROOT_PASSWORDを設定するだけでよい。
MYSQL_DATABASEは開発で利用するデータベース名にする。2-1-3 Ruby on Railsのdatabase.ymlを修正する。
database.ymlを修正してrailsからmysql on dockerに接続できるようにする。
- ./config/database.yml
default: &default adapter: mysql2 encoding: utf8mb4 charset: utf8mb4 collation: utf8mb4_general_ci pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: root password: password host: 127.0.0.1 development: <<: *default database: {project_name}_developmentlogを取得するために2
# Nginx用のlog吐き出し用 $ mkdir ./log/mysql $ touch ./log/mysql/mysqld.log2-1-4 Ruby on Railsのディレクトリ構成を修正する。
Webpackerを利用してJS,CSS,Image等を扱うための構成にします。
$ mkdir -p ./app/javascript/{stylesheets, images} $ touch ./app/javascript/stylesheets/application.scss2-1-5 [S]CSSファイル、ImageをWebpackerで扱う
- ./app/javascript/packs/application.js
- // const images = require.context('../images', true) - // const imagePath = (name) => images(name, true) + const images = require.context('../images', true) + const imagePath = (name) => images(name, true) + import "../stylesheets/application";2-1-6 stylesheet_pack_tagに切り替える。
- ./app/views/layouts/application.html.erb
- <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> + <%= stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>2-1-7 check_yarn_integrityの設定をfalseにする。
- config/webpacker.yml
development: <<: *default compile: true # Verifies that correct packages and versions are installed by inspecting package.json, yarn.lock, and node_modules - check_yarn_integrity: true + check_yarn_integrity: false2-1-8 extract_css: false -> trueにする。
開発中でもCSSファイルを作成してほしいので
extract_css
をtrueにします。- config/webpacker.yml
# Extract and emit a css file - extract_css: false + extract_css: true2-1-8 assetsを生成しないようにする
- ./config/initializers/generators.rb
Rails.application.config.generators do |g| g.assets false g.test_framework false end2-2 MySQL on Dockerを起動する。
# mysqlを起動する。 $ docker-compose -d up databasemysqlを起動したらmysqlの照合順序を確認する。
mysql> show variables like '%char%'; +--------------------------+----------------------------+ | Variable_name | Value | +--------------------------+----------------------------+ | character_set_client | utf8mb4 | | character_set_connection | utf8mb4 | | character_set_database | utf8mb4 | | character_set_filesystem | binary | | character_set_results | utf8mb4 | | character_set_server | utf8mb4 | | character_set_system | utf8 | | character_sets_dir | /usr/share/mysql/charsets/ | +--------------------------+----------------------------+2-3 ローカルPCからRuby on Railsを起動する。
# railsを起動する。 $ bin/rails s --port=3000 -b 0.0.0.0以下の画面が表示されればOK。
3. Ruby on Rails on Dockerを構築する。
3-1. Dockerfileを作成する。
- ./Dockerfile
FROM ruby:2.6.5 RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list RUN apt-get update && apt-get install -y \ build-essential \ nodejs \ yarn \ default-mysql-client \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* ENV LANG ja_JP.UTF-8 RUN mkdir -p /app WORKDIR /app COPY Gemfile Gemfile.lock ./ RUN gem update bundler && bundle install COPY . . RUN yarn install --check-files RUN yarn webpack --config ./config/webpack/development.js CMD ["rails", "server", "-b", "0.0.0.0"]3-2. docker-compose.ymlに
app:
を追加する。
environment
で環境変数[RAILS_HOST]にdatabase
を指定します。
- ./docker-compose.ymlversion: '3.3' services: + app: + build: . + command: bundle exec rails s -b 0.0.0.0 + volumes: + - .:/app:cached + ports: + - 3000:3000 + depends_on: + - database + tty: true + stdin_open: true + environment: + RAILS_HOST: database database: build: context: . dockerfile: ./docker/mysql/Dockerfile environment: MYSQL_ROOT_PASSWORD: password MYSQL_DATABASE: nginx_rails_with_webpacker_mysql_on_docker_development ports: - "3306:3306" volumes: - ./docker/mysql/my.cnf:/etc/mysql/conf.d/my.cnf - ./log/mysql:/var/log/mysqldocker-composeで立ち上げる場合は環境変数hostを指定し、ローカル上で起動する場合は
127.0.0.1
を指定する。
- config/database.yml
default: &default adapter: mysql2 encoding: utf8mb4 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: root password: password - host: 127.0.0.1 + host: <%= ENV.fetch("RAILS_HOST") { '127.0.0.1' } %>3-3. Ruby on Rails on Dockerを起動する。
# Ruby on Rails + MySQL on Dockerで起動する。 $ docker-compose up -d --buildブラウザで
http://localhost:3000
にアクセスします。改めて、
Yay! You’re on Rails!
の画面が表示されればOK.4. nginx on Dockerを用意する。
Nginx(webサーバー)を用意します。
Nginx用のDockerfileとnginx.confを用意します。
# nginx用のファイルを作成する。 $ mkdir -p ./docker/nginx $ touch ./docker/nginx/{Dockerfile, nginx.conf}各ファイルの記述はこちら。
- ./docker/nginx/Dockerfile
FROM nginx:1.17.3 RUN mkdir -p /usr/share/nginx/public COPY ./docker/nginx/nginx.conf /etc/nginx/conf.d/default.conf CMD ["nginx", "-g", "daemon off;"]
- ./docker/nginx/nginx.conf
server { listen 80; server_name web; root /usr/share/nginx/public; access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; location @app { proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Proto $scheme; proxy_pass http://app:3000; } location / { try_files $uri @app; } location ~ ^/(assets|packs)/ { gzip_static on; expires max; add_header Cache-Control public; } location = /favicon.ico { access_log off; log_not_found off; } location = /robots.txt { access_log off; log_not_found off; } location ~ /\.(?!well-known).* { deny all; } }logを取得するために
# Nginx用のlog吐き出し用 $ mkdir ./log/nginx $ touch ./log/nginx/{access.log, error.log}
docker-compose up --build
を実行します。ブラウザで
http://localhost
にアクセスします。
改めて、Yay! You’re on Rails!
の画面が表示されればOK.5. プロジェクトをちょっとだけカスタマイズ
こちらは、やったほうがいいと思われるが任意なあれこれです。
5-1. Add SplitChunks (Webpack V4)
SplitChunksPluginとは、共通で使用するpackageのJavaScriptファイルを纏める機能のことです。
デフォルトの設定を例としてあげておきます。チューニングする場合は公式を確認する。
- ./config/webpack/environment.js
const { environment } = require('@rails/webpacker') + environment.splitChunks() module.exports = environment
splitChunksを利用する場合は、JavaScriptファイルを読み込むヘルパーメソッドは
javascript_packs_with_chunks_tag
になる。- <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %> + <%= javascript_packs_with_chunks_tag 'application', 'data-turbolinks-track': 'reload' %>
5-2. app/assetsを削除する。
でも、
app/assets/config/manifest.js
がないと怒らせる。Expected to find a manifest file in `app/assets/config/manifest.js` (Sprockets::Railtie::ManifestNeededError)プロジェクト作成時に
-–skip-sprockets
を実行してもsprocketsのgemはインストールされプロジェクト内部ではまだ利用しているようです。そのため、app/assets
配下のManifestファイルを削除してしまうとSprockets::Railtie::ManifestNeededErrorが発生します。対策としては、Gemfileに
gem 'sprockets', '< 4'
を追加し、bundle update sprockets
すると良いようです。(2020/05時点)$ bundle update sprockets Using sprockets 3.7.2 (was 4.0.0)5-3. app/javascriptをapp/frontendにする。
app/javascriptからapp/frontendに変更するという記事が多いように感じます。
./config/webpacker.yml
でsource_entry_pathを指定できるのでお好みで。
- ./config/webpacker.yml
default: &default - source_path: app/javascript + source_path: app/frontend source_entry_path: packs public_root_path: public public_output_path: packs cache_path: tmp/cache/webpacker check_yarn_integrity: false webpack_compile_output: true5-4. ブラウザにlocalhostとか127.0.0.1とか0.0.0.0と入れるのはちょっと。
sudo vi /etc/hosts
を実行し、HOST DATABASEにお好みのドメイン名を追加する。
保存してブラウザでdev-application.localhost
と入力してみよう。127.0.0.1 dev-application.localhost5-5. docker-compose upで、
A server is already running. Check /app/tmp/pids/server.pid.
と言われて起動できない。今回環境構築するために何度か
docker-compose up
を実行しました。その時に当該事象が発生しました。
これはdocker-composeのバグないそうですが、よく起きている事象のようです。
対策は、調べた限りですが2つあります。45-5-1. 対処方法 1
docker-compose.ymlのcommandで
bundle exec rails s..
の前に削除コマンドを仕込む方法です。command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
- 参考:How to safely stop the images #989
- 参考:When closing a container with a rails app, rails doesn't delete /tmp/pids/server.pid #1393
5-5-1. 対処方法 2
DockerのCMDにスクリプトを登録しておく。
対応していることはtmp/pids/server.pid
を削除するということは一緒ですが、スクリプトにすることで実行時に事前に行いたい処理を追加していくことができます。ワンライナーなスクリプトよりも見通しがいいのでこちらもおすすめです。# Docker CMD [foreground]# foreground.sh #!/bin/bash set -e rm -f tmp/pids/server.pid bin/rails s -b 0.0.0.0 -p 30005-6. webpack-dev-server on Dockerを用意する。
webpack-dev-serverをDockerで構築します。5
Dockerで構築するほうがいい理由についてはまだ腹落ちしていないため任意# ./docker-compose.yml version: '3.3' services: web: build: context: . dockerfile: ./docker/nginx/Dockerfile volumes: - ./public:/usr/share/nginx/public:cached - ./log/nginx:/var/log/nginx ports: - 80:80 depends_on: - app app: build: . command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'" volumes: - .:/app:cached ports: - 3000:3000 depends_on: - database tty: true stdin_open: true environment: RAILS_HOST: database + WEBPACKER_DEV_SERVER_HOST: webpacker + webpacker: + build: . + command: ./bin/webpack-dev-server + volumes: + - .:/webpacker-example-app + ports: + - '3035:3035' + tty: true + stdin_open: true + environment: + - NODE_ENV=development + - RAILS_ENV=development + - WEBPACKER_DEV_SERVER_HOST=0.0.0.0 + depends_on: + - app database: build: context: . dockerfile: ./docker/mysql/Dockerfile environment: MYSQL_ROOT_PASSWORD: password MYSQL_DATABASE: nginx_rails_with_webpacker_mysql_on_docker_development ports: - "3306:3306" volumes: - ./docker/mysql/my.cnf:/etc/mysql/conf.d/my.cnf - ./log/mysql:/var/log/mysql
- ./docker/nginx/nginx.conf
server { listen 80; server_name web; root /usr/share/nginx/public; access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; location @app { proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Proto $scheme; proxy_pass http://app:3000; } location / { try_files $uri @app; } location ~ ^/(assets|packs)/ { gzip_static on; expires max; add_header Cache-Control public;`v } location = /favicon.ico { access_log off; log_not_found off; } location = /robots.txt { access_log off; log_not_found off; } + # Proxy webpack dev server websocket requests + location /sockjs-node { + proxy_redirect off; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_pass http://127.0.0.1:3035; # change to match your +webpack-dev-server host + } location ~ /\.(?!well-known).* { deny all; } }
docker-compose up --build
を実行します。ブラウザで
http://localhost
にアクセスします。
改めて、Yay! You’re on Rails!
の画面が表示されればOK.まとめ
- rails/webpackarには情報がちゃんと記載されている。(webpacker/doc)
- webpackerはおすすめしないという記事もあるが、みなさん共通して利用するならちゃんと考えましょう。とおっしゃっているのでちゃんと考えるために一度導入してみて感触を掴んだほうがいい。(逆に導入しない方法も構築してみるといいと思う。)
- 開発環境のセットアップだけでも時間が掛かる。しかし、はじめにいろいろ検討しておくと開発がしやすくなる。
- それぞれの設定についてもなぜ、そうしたのか。をまとめておけると後から再検討や情報共有する時に役に立つ。
- rails6のセットアップと注意点を一通り学習することができた。
- 実際は、rails new のタイミングで react や veuの利用を選択することがあると思うのでそこは適宜この手順と照らし合わせながら進めればいいと思う。
- 投稿日:2020-05-25T07:16:45+09:00
ElixirでMySQLを使う #3(Ecto/CRUD・クエリ作成)
概要
の続きです。
前回、
Ecto
マイグレーション〜スキーマ作成を行って、Elixir側からデータをMySQLに渡しました。
本記事では、ElixirでMySQLのCRUD操作(データ作成・読み出し・更新・削除)を実行していきます。
- 本記事シリーズにおける実行環境等については、シリーズ #1 の記事をご参照ください。
(事前準備)データベース再作成
事前準備として、前回つくったデータベースを削除して、あたらしく作り直します。
terminal$ mix ecto.drop Compiling 1 file (.ex) The database for Friendsmysql.Repo has been droppedterminal$ mix ecto.create The database for Friendsmysql.Repo has been createdterminal$ mix ecto.migrate 13:36:23.224 [info] == Running 20200521003013 Friendsmysql.Repo.Migrations.CreatePeople.change/0 forward 13:36:23.226 [info] create table people 13:36:23.241 [info] == Migrated 20200521003013 in 0.0s 13:36:23.265 [info] == Running 20200522004701 Friendsmysql.Repo.Migrations.ApplyNotnullToNames.change/0 forward 13:36:23.265 [info] alter table people 13:36:23.278 [info] == Migrated 20200522004701 in 0.0s
- 結果確認(MySQL)
terminal(MySQL)mysql> desc people; +------------+---------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------+---------------------+------+-----+---------+----------------+ | id | bigint(20) unsigned | NO | PRI | NULL | auto_increment | | first_name | varchar(255) | NO | | NULL | | | last_name | varchar(255) | NO | | NULL | | | age | int(11) | YES | | NULL | | +------------+---------------------+------+-----+---------+----------------+ 4 rows in set (0.00 sec) mysql> select * from people; Empty set (0.00 sec)作りなおしたデータベースと、からっぽの
people
テーブルが準備できました。CRUD
それでは、IEx(
$ iex -S mix
)でCRUD操作を実行していきます。Create(データ作成)
Repo.insert()
はじめに、データの作成(Create)です。
以下の計4レコードを用意します。
terminaliex(1)> people = [ ...(1)> %Friendsmysql.Person{first_name: "Ryan", last_name: "Bigg", age: 28}, ...(1)> %Friendsmysql.Person{first_name: "John", last_name: "Smith", age: 27}, ...(1)> %Friendsmysql.Person{first_name: "Jane", last_name: "Smith", age: 26}, ...(1)> %Friendsmysql.Person{first_name: "im", last_name: "miolab", age: 28}, ...(1)> ] [ %Friendsmysql.Person{ __meta__: #Ecto.Schema.Metadata<:built, "people">, age: 28, first_name: "Ryan", id: nil, last_name: "Bigg" }, %Friendsmysql.Person{ __meta__: #Ecto.Schema.Metadata<:built, "people">, age: 27, first_name: "John", id: nil, last_name: "Smith" }, %Friendsmysql.Person{ __meta__: #Ecto.Schema.Metadata<:built, "people">, age: 26, first_name: "Jane", id: nil, last_name: "Smith" }, %Friendsmysql.Person{ __meta__: #Ecto.Schema.Metadata<:built, "people">, age: 28, first_name: "im", id: nil, last_name: "miolab" } ]
Enum.each
でinsert()
していきます。terminaliex(2)> Enum.each(people, fn(p) -> Friendsmysql.Repo.insert(p) end) 13:45:40.520 [debug] QUERY OK db=5.9ms decode=1.3ms queue=7.0ms idle=1457.6ms INSERT INTO `people` (`age`,`first_name`,`last_name`) VALUES (?,?,?) [28, "Ryan", "Bigg"] 13:45:40.528 [debug] QUERY OK db=1.3ms queue=4.8ms idle=1475.8ms INSERT INTO `people` (`age`,`first_name`,`last_name`) VALUES (?,?,?) [27, "John", "Smith"] 13:45:40.531 [debug] QUERY OK db=0.4ms queue=1.8ms idle=1482.1ms INSERT INTO `people` (`age`,`first_name`,`last_name`) VALUES (?,?,?) [26, "Jane", "Smith"] :ok 13:45:40.534 [debug] QUERY OK db=2.3ms queue=1.0ms idle=1484.5ms INSERT INTO `people` (`age`,`first_name`,`last_name`) VALUES (?,?,?) [28, "im", "miolab"]
people
テーブルにINSERT
できました。ちなみに、前回の記事みたいに1レコードだけをシンプルに
insert()
実行するには、以下の通りです。terminaliex(3)> iiii = %Friendsmysql.Person{first_name: "iiii", last_name: "mimimi", age: 18} %Friendsmysql.Person{ __meta__: #Ecto.Schema.Metadata<:built, "people">, age: 18, first_name: "iiii", id: nil, last_name: "mimimi" } iex(4)> Friendsmysql.Repo.insert(iiii) 13:46:54.648 [debug] QUERY OK db=2.5ms queue=3.0ms idle=1575.2ms INSERT INTO `people` (`age`,`first_name`,`last_name`) VALUES (?,?,?) [18, "iiii", "mimimi"] {:ok, %Friendsmysql.Person{ __meta__: #Ecto.Schema.Metadata<:loaded, "people">, age: 18, first_name: "iiii", id: 5, last_name: "mimimi" }}
- 結果確認(MySQL)
terminal(MySQL)mysql> select * from people; +----+------------+-----------+------+ | id | first_name | last_name | age | +----+------------+-----------+------+ | 1 | Ryan | Bigg | 28 | | 2 | John | Smith | 27 | | 3 | Jane | Smith | 26 | | 4 | im | miolab | 28 | | 5 | iiii | mimimi | 18 | +----+------------+-----------+------+ 5 rows in set (0.02 sec)
Create
操作ができました。Read(データ読み出し)
つづいて、登録データの読み出し・抽出(Read)を実行していきます。
全レコード 取得
Repo.all()
terminaliex(5)> Friendsmysql.Person |> Friendsmysql.Repo.all 13:51:56.586 [debug] QUERY OK source="people" db=0.3ms queue=5.9ms idle=1513.0ms SELECT p0.`id`, p0.`first_name`, p0.`last_name`, p0.`age` FROM `people` AS p0 [] [ %Friendsmysql.Person{ __meta__: #Ecto.Schema.Metadata<:loaded, "people">, age: 28, first_name: "Ryan", id: 1, last_name: "Bigg" }, %Friendsmysql.Person{ __meta__: #Ecto.Schema.Metadata<:loaded, "people">, age: 27, first_name: "John", id: 2, last_name: "Smith" }, %Friendsmysql.Person{ __meta__: #Ecto.Schema.Metadata<:loaded, "people">, age: 26, first_name: "Jane", id: 3, last_name: "Smith" }, %Friendsmysql.Person{ __meta__: #Ecto.Schema.Metadata<:loaded, "people">, age: 28, first_name: "im", id: 4, last_name: "miolab" }, %Friendsmysql.Person{ __meta__: #Ecto.Schema.Metadata<:loaded, "people">, age: 18, first_name: "iiii", id: 5, last_name: "mimimi" } ]レコード全件を読み出しました。
(SELECT * FROM table
相当)
id
を指定してレコード取得
Repo.get_by()
terminaliex(6)> Friendsmysql.Person |> Friendsmysql.Repo.get(3) 13:53:31.176 [debug] QUERY OK source="people" db=6.2ms queue=0.3ms idle=1102.8ms SELECT p0.`id`, p0.`first_name`, p0.`last_name`, p0.`age` FROM `people` AS p0 WHERE (p0.`id` = ?) [3] %Friendsmysql.Person{ __meta__: #Ecto.Schema.Metadata<:loaded, "people">, age: 26, first_name: "Jane", id: 3, last_name: "Smith" }
id: 3
に該当するレコードを抽出しました。カラム名とフィールドに基づいてレコードを取得
Repo.where()
terminaliex(7)> Friendsmysql.Person |> Friendsmysql.Repo.get_by(first_name: "im") 13:54:58.257 [debug] QUERY OK source="people" db=1.8ms queue=2.0ms idle=1185.3ms SELECT p0.`id`, p0.`first_name`, p0.`last_name`, p0.`age` FROM `people` AS p0 WHERE (p0.`first_name` = ?) ["im"] %Friendsmysql.Person{ __meta__: #Ecto.Schema.Metadata<:loaded, "people">, age: 28, first_name: "im", id: 4, last_name: "miolab" } iex(8)> Friendsmysql.Person |> Friendsmysql.Repo.get_by(age: 18) 13:56:15.218 [debug] QUERY OK source="people" db=1.9ms queue=6.1ms idle=1269.7ms SELECT p0.`id`, p0.`first_name`, p0.`last_name`, p0.`age` FROM `people` AS p0 WHERE (p0.`age` = ?) [18] %Friendsmysql.Person{ __meta__: #Ecto.Schema.Metadata<:loaded, "people">, age: 18, first_name: "iiii", id: 5, last_name: "mimimi" }
first_name: im
に該当するレコードを1件age: 18
に該当するレコードを1件それぞれ抽出しました。
Ecto.Queryでフィルタリング抽出
もう少しつっこんだ条件抽出のクエリを書いてみます。
require Ecto.Query
で事前準備をします。terminaliex(9)> require Ecto.Query Ecto.Queryカラム名とフィールドに基づいて重複レコードを取得
それでは、
Ecto.Query
でまず重複するレコードを抽出します。
具体例として、last_name
がSmith
さんが2人いるので、こちらのお二方を抽出することとします。なお、先ほどの
get_by()
ではレコード1件の抽出しかできないため、以下のようにEcto.Query
でのオペレーションが必要となります。terminaliex(10)> Friendsmysql.Person |> Ecto.Query.where(last_name: "Smith") |> Friendsmysql.Repo.all 14:00:10.899 [debug] QUERY OK source="people" db=0.2ms queue=5.0ms idle=1827.0ms SELECT p0.`id`, p0.`first_name`, p0.`last_name`, p0.`age` FROM `people` AS p0 WHERE (p0.`last_name` = 'Smith') [] [ %Friendsmysql.Person{ __meta__: #Ecto.Schema.Metadata<:loaded, "people">, age: 27, first_name: "John", id: 2, last_name: "Smith" }, %Friendsmysql.Person{ __meta__: #Ecto.Schema.Metadata<:loaded, "people">, age: 26, first_name: "Jane", id: 3, last_name: "Smith" } ]もしくは、
terminaliex(11)> Ecto.Query.from(p in Friendsmysql.Person, where: p.last_name == "Smith") |> Friendsmysql.Repo.all 14:00:57.013 [debug] QUERY OK source="people" db=5.5ms idle=1940.3ms SELECT p0.`id`, p0.`first_name`, p0.`last_name`, p0.`age` FROM `people` AS p0 WHERE (p0.`last_name` = 'Smith') [] [ %Friendsmysql.Person{ __meta__: #Ecto.Schema.Metadata<:loaded, "people">, age: 27, first_name: "John", id: 2, last_name: "Smith" }, %Friendsmysql.Person{ __meta__: #Ecto.Schema.Metadata<:loaded, "people">, age: 26, first_name: "Jane", id: 3, last_name: "Smith" } ]2人のSmithさんを抽出しました。
絞り込み抽出
複数の抽出条件を組み合わせ実行します。
クエリをパイプライン演算子で繋いでいきます。
last_name
条件抽出クエリに、first_name
条件抽出クエリをつないで、AND
検索っぽくします。terminaliex(12)> Friendsmysql.Person |> Ecto.Query.where(last_name: "Smith") |> Ecto.Query.where(age: 27) |> Friendsmysql.Repo.all 14:02:44.259 [debug] QUERY OK source="people" db=0.6ms queue=3.9ms idle=1184.8ms SELECT p0.`id`, p0.`first_name`, p0.`last_name`, p0.`age` FROM `people` AS p0 WHERE (p0.`last_name` = 'Smith') AND (p0.`age` = 27) [] [ %Friendsmysql.Person{ __meta__: #Ecto.Schema.Metadata<:loaded, "people">, age: 27, first_name: "John", id: 2, last_name: "Smith" } ]
last_name
がSmithで、かつage
が27の人を抽出しました。その他、抽出いろいろ
適当にいくつか例を実行します。
参考のため、それぞれに、相当するSQLを添えています。
- 事前準備
terminaliex(13)> import Ecto.Query, only: [from: 2] Ecto.Query
- 全レコードの
age
平均値を抽出
(SELECT AVG(age) FROM people
)terminaliex(14)> query_avg = from p in "people", ...(14)> select: avg(p.age) #Ecto.Query<from p0 in "people", select: avg(p0.age)> iex(15)> Friendsmysql.Repo.all(query_avg) 14:14:19.725 [debug] QUERY OK source="people" db=18.2ms queue=1.8ms idle=1914.1ms SELECT avg(p0.`age`) FROM `people` AS p0 [] [#Decimal<25.4000>]
age > 20
の人のfirst_name
を抽出
(SELECT first_name FROM people WHERE age > 20
)terminaliex(16)> query_over_twenty = from p in "people", ...(16)> where: p.age > 20, ...(16)> select: p.first_name #Ecto.Query<from p0 in "people", where: p0.age > 20, select: p0.first_name> iex(17)> Friendsmysql.Repo.all(query_over_twenty) 14:17:21.281 [debug] QUERY OK source="people" db=1.1ms queue=3.6ms idle=1491.4ms SELECT p0.`first_name` FROM `people` AS p0 WHERE (p0.`age` > 20) [] ["Ryan", "John", "Jane", "im"]
age > 26
の人をage降順
で並びかえて抽出
(SELECT id, first_name, age FROM people WHERE age > 26 ORDER BY age DESC
)terminaliex(18)> query_over_twentysix = from p in "people", ...(18)> where: p.age > 26, ...(18)> order_by: [desc: :age], ...(18)> select: [:id, :first_name, :age] #Ecto.Query<from p0 in "people", where: p0.age > 26, order_by: [desc: p0.age], select: [:id, :first_name, :age]> iex(19)> Friendsmysql.Repo.all(query_over_twentysix) 14:19:08.225 [debug] QUERY OK source="people" db=4.1ms queue=3.9ms idle=1431.1ms SELECT p0.`id`, p0.`first_name`, p0.`age` FROM `people` AS p0 WHERE (p0.`age` > 26) ORDER BY p0.`age` DESC [] [ %{age: 28, first_name: "Ryan", id: 1}, %{age: 28, first_name: "im", id: 4}, %{age: 27, first_name: "John", id: 2} ]ほんの一例ですが、いずれも問題なく抽出実行できました。
この他にも、以下公式リファレンスに
Ecto.Query
の抽出もろもろについて詳細がありますので、ご覧いただき試してみられると良いかとおもいます。
- Hexdocs
(補足)
Ecto.Query
内での変数使用の際は^
が必要変数をクエリ内で展開する際には、
^
(ピン演算子)が必要になります。terminaliex(20)> first_name_im = "im" "im" iex(21)> Friendsmysql.Person |> Ecto.Query.where(first_name: ^first_name_im) |> Friendsmysql.Repo.all 14:24:47.240 [debug] QUERY OK source="people" db=2.0ms idle=1170.0ms SELECT p0.`id`, p0.`first_name`, p0.`last_name`, p0.`age` FROM `people` AS p0 WHERE (p0.`first_name` = ?) ["im"] [ %Friendsmysql.Person{ __meta__: #Ecto.Schema.Metadata<:loaded, "people">, age: 28, first_name: "im", id: 4, last_name: "miolab" } ]
first_name_im
で束縛した文字列im
をEcto.Query
で展開する際、適用変数を^first_name_im
の形にします。Update(データ更新)
CRUD
のつづきで、次はデータの更新を実行します。
Repo.update()
terminaliex(1)> require Ecto.Query Ecto.Query iex(2)> ryan = Friendsmysql.Person |> Ecto.Query.where(first_name: "Ryan") |> Friendsmysql.Repo.one 14:28:40.395 [debug] QUERY OK source="people" db=0.3ms decode=1.0ms queue=2.7ms idle=1582.4ms SELECT p0.`id`, p0.`first_name`, p0.`last_name`, p0.`age` FROM `people` AS p0 WHERE (p0.`first_name` = 'Ryan') [] %Friendsmysql.Person{ __meta__: #Ecto.Schema.Metadata<:loaded, "people">, age: 28, first_name: "Ryan", id: 1, last_name: "Bigg" }上記で準備したRyanさんのデータ
age: 28
を、29
に更新してみます。terminaliex(3)> change_age_twentynine = Friendsmysql.Person.changeset(ryan, %{age: 29}) #Ecto.Changeset< action: nil, changes: %{age: 29}, errors: [], data: #Friendsmysql.Person<>, valid?: true > iex(4)> Friendsmysql.Repo.update(change_age_twentynine) 14:31:55.745 [debug] QUERY OK db=7.2ms queue=4.1ms idle=1929.9ms UPDATE `people` SET `age` = ? WHERE `id` = ? [29, 1] {:ok, %Friendsmysql.Person{ __meta__: #Ecto.Schema.Metadata<:loaded, "people">, age: 29, first_name: "Ryan", id: 1, last_name: "Bigg" }}結果を確認します。
terminaliex(5)> Friendsmysql.Person |> Friendsmysql.Repo.get_by(first_name: "Ryan") 14:32:16.250 [debug] QUERY OK source="people" db=0.5ms queue=0.8ms idle=1442.8ms SELECT p0.`id`, p0.`first_name`, p0.`last_name`, p0.`age` FROM `people` AS p0 WHERE (p0.`first_name` = ?) ["Ryan"] %Friendsmysql.Person{ __meta__: #Ecto.Schema.Metadata<:loaded, "people">, age: 29, first_name: "Ryan", id: 1, last_name: "Bigg" }
age: 29
に更新がされています。Delete(データ削除)
CRUD
の最後に、データの削除を実行します。
Repo.delete()
先ほどの
IEx
で実行したUpdate
のつづきに、コードを書いていきます。
Ryanさん レコードを、ごめんなさいですが削除します。terminaliex(6)> Friendsmysql.Repo.delete(ryan) 14:35:11.201 [debug] QUERY OK db=4.8ms queue=4.8ms idle=1386.5ms DELETE FROM `people` WHERE `id` = ? [1] {:ok, %Friendsmysql.Person{ __meta__: #Ecto.Schema.Metadata<:deleted, "people">, age: 28, first_name: "Ryan", id: 1, last_name: "Bigg" }}削除できているか全件抽出して確認します。
terminaliex(7)> Friendsmysql.Person |> Friendsmysql.Repo.all 14:35:52.982 [debug] QUERY OK source="people" db=0.4ms queue=2.0ms idle=173.7ms SELECT p0.`id`, p0.`first_name`, p0.`last_name`, p0.`age` FROM `people` AS p0 [] [ %Friendsmysql.Person{ __meta__: #Ecto.Schema.Metadata<:loaded, "people">, age: 27, first_name: "John", id: 2, last_name: "Smith" }, %Friendsmysql.Person{ __meta__: #Ecto.Schema.Metadata<:loaded, "people">, age: 26, first_name: "Jane", id: 3, last_name: "Smith" }, %Friendsmysql.Person{ __meta__: #Ecto.Schema.Metadata<:loaded, "people">, age: 28, first_name: "im", id: 4, last_name: "miolab" }, %Friendsmysql.Person{ __meta__: #Ecto.Schema.Metadata<:loaded, "people">, age: 18, first_name: "iiii", id: 5, last_name: "mimimi" } ]Ryanさんのレコードが削除されています。
終わり
IEx
でElixir(Ecto)によるMySQLのCRUD操作(データ作成・読み出し・更新・削除)を実行しました。
- Ectoはドキュメントも詳しく、パイプラインでガシガシ書けて良い感じなので、個人的にも色々もっと調べてみようと思ってます。
- もしかしたら続きを何かしら書くかもしれません。