はじめに
初 AdventCalendar です!よろしくお願いします!
昨日の記事は@takamizawa46 さんでよわよわな新卒が運営する勉強会の開催までの経緯と得られた知見でした。
僕は Phoenix はインターン先で使っていて慣れていますが、Rails は勉強中の身分です。 (まあ、Phoenix もまだまだ完璧に使いこなせていないのですが・・・)
何か間違えてる部分やこっちの方がええで!ってのがありましたら、ぜひぜひコメントお願いします。
(特に Rails に関しては初学者なのでよろしくお願いします・・・)
Phoenix とは
関数型言語 Elixir の Web フレームワークに当たります。 「そもそも Elixir が Ruby のインスパイアを受けていると言われており、関数型言語の中では学習コストが低い」とどこかで読んだ気がします。
福岡の Elixir コミュニティ fukuoka.ex さんでは以下のように Phoenix の紹介がなされています。(勝手に引用ごめんなさい)
Phoenix は、Web アプリケーションの世界では、最もメジャーな「Ruby on Rails」を作っていたメンバーによって開発された、大量アクセスと高速分散の捌きが得意な Web アプリケーションフレームワークで、Rails 同等の高度な Web アプリ開発を、とても気軽に行えます(中でも、WebSocket 性能は、あらゆる言語の FW 中でも最速) — 福岡の Elixir コミュニティ fukuoka.ex さんはこちら
ということで構成が酷似しています。
Elixir / Phoenix を扱う企業も増えており、Rails を普段使っている方にとってはハードルも低いので一緒に Phoenix を勉強しておくと一石二鳥なわけですね
最近では Qiita をはじめ多くの方が Phoenix の扱い方を記事にしていますので日本語でも学習しやすくなっています。
また、先ほどのfukuoka.exさんや昨日の記事の@takamizawa46 さんの運営する清流 elixirなど全国で多くの勉強会が開かれています。 (初学者の方の参加も OK のものが多く、またリモートでの参加も可能になっている勉強会も多くあります。)
この記事があなたの Phoenix ライフのきっかけになると嬉しいです。
Elixir 勉強してみたいって方は
今回のこの記事内では Elixir の文法などは扱いません。 純粋にフレームワークとして Rails と Phoenix の扱い方を比較します。
この記事を読んで Elixir を勉強してみようという気になった方は以下を参照してみてください。
公式ガイド(英語多め) https://elixir-lang.org/getting-started/introduction.html
Elixir school(日本語多めなのでおすすめ) https://elixirschool.com/ja/
Rails と Phoenix の比較
新規アプリケーションの作成〜サーバーの立ち上げ
まずはアプリケーションの作成です。
$ rails new sample
$ mix phx.new sample
そしてサーバーを立ち上げます。
$ cd sample
$ rails server #サーバーの立ち上げ
$ cd sample
$ mix ecto.create #DBの作成
$ mix phx.server #サーバーの立ち上げ
Rails ではこの画面
Phoenix ではこの画面 が表示されれば成功です!
各ファイルの対応関係とその置き位置
ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ | Rails | Phoenix |
---|---|---|
controller | app/controllers | lib/{アプリケーション名}_web/controller |
view | app/views | lib/{アプリケーション名}_web/templates |
router | db/routes.rb | lib/{アプリケーション名}_web/router.ex |
model | app/models | lib/{アプリケーション名}/ 内 |
Controller
Controller に関する大きな違いは Phoenix では render
を記述する必要がある点です。
Rails でも部分テンプレートを用いる時に使う render という関数と働きは似ています。
class PageController < ApplicationController
def index
end
end
defmodule SampleWeb.PageController do
use SampleWeb, :controller
def index(conn, _params) do
render(conn, "index.html")
end
end
Rails では上記のように記述するだけで page/index.html がレンダリングされますが、Phoenix の場合は明示的に render
関数でレンダリングするものを指定する必要があります。
view
MVC の V ですね。
前述のように Rails の.html.erb のファイルは Phoenix では lib/{アプリケーション名}_web/template に格納されています。
そして、Phoenix の lib/{アプリケーション名}_web/views では Template の中で用いる関数が定義されます。
defmodule SampleWeb.PageView do
use SampleWeb, :view
#template/page内のTemplateで使用する関数を定義する
end
ここまでわかっておけば十分でここからの話は少しややこしいので飛ばしてもらっても大丈夫です。
前述のように、controller で render
を呼び出すことで指定の Template を呼び出すのですが、
これは例えば Sample(← アプリ名)の PageController から呼び出した場合、最終的に Sample.PageView 内に定義されている(実際は use SampleWeb, :view
で定義されている)render
関数を呼ぶことになります。
Sample.PageView 内に定義されている render
関数は lib/{アプリケーション名}_web/template/page 内に存在する指定の Template をレンダリングします。
このような流れになっているため、Template は Sample.PageView 内でレンダリングされる訳なので Sample.PageView に関数を定義すると、template/page 内の Template で使用できるわけですね。
この辺り詳しく知りたい方はこちらの記事が非常に参考になります。
Template 自体は Rails と同様に <%= %>
のなかで Elixir の関数/変数が使用できます。
model
model は DB をいじいじする部分です。
Rails では ActiveRecord がやってくれている部分を Phoenix では自分で記述しなければいけない部分があります。
Phoenix では AcriveRecord で言う create などのメソッドは全て自分で定義する必要があります。 また、DB の定義も Rails よりもしっかりと書かなければいけません。
実際は generator を使えばこれらを含めて作成してくれるので 1 から自分で作るということは少ないという印象です。
Rails の model は AcriveRecord のサブクラスとしてファイルを作成するだけで、これらの手間を省いているのだと思います。 (AcriveRecord 便利ですね)
以下はそれらのサンプルコードです
defmodule Sample.Accounts.User do
use Ecto.Schema
import Ecto.Changeset
alias Sample.Accounts.Credential
schema "users" do
field :name, :string
has_one :credential, Credential
has_many :boards, Trellu.Display.Board
has_many :activity_logs, Trellu.Activity.Activity_log
many_to_many :teams, Trellu.Group.Team, join_through: "users_teams",on_replace: :delete
timestamps()
end
@doc false
def changeset(user, attrs) do
user
|> cast(attrs, [:name])
|> validate_required([:name])
|> unique_constraint(:name)
end
end
スキーマ定義は field
でスキーマを記述します。
また、has_many
などでテーブル同士の紐付けを示します。(Rails の model に記述する has_many
と役割は同じです)
DB 定義のファイルにはスキーマ定義の他、changeset
と呼ばれる validation に用いる関数の定義を行う必要があります。
これは Rails で model 内に記述する validation と役割は同じです。
defmodule Sample.Accounts do
@moduledoc """
The Accounts context.
"""
import Ecto.Query, warn: false
alias Sample.Repo
alias Sample.Accounts.{User, Credential}
@doc """
Returns the list of users.
## Examples
iex> list_users()
[%User{}, ...]
"""
def list_users do
User
|> Repo.all()
|> Repo.preload(:credential)
end
@doc """
Gets a single user.
Raises `Ecto.NoResultsError` if the User does not exist.
## Examples
iex> get_user!(123)
%User{}
iex> get_user!(456)
** (Ecto.NoResultsError)
"""
def get_user!(id) do
User
|> Repo.get!(id)
|> Repo.preload(:credential)
end
@doc """
Creates a user.
## Examples
iex> create_user(%{field: value})
{:ok, %User{}}
iex> create_user(%{field: bad_value})
{:error, %Ecto.Changeset{}}
"""
def create_user(attrs \\ %{}) do
%User{}
|> User.changeset(attrs)
|> Ecto.Changeset.cast_assoc(:credential, with: &Credential.changeset/2)
|> Repo.insert()
end
@doc """
Updates a user.
## Examples
iex> update_user(user, %{field: new_value})
{:ok, %User{}}
iex> update_user(user, %{field: bad_value})
{:error, %Ecto.Changeset{}}
"""
def update_user(%User{} = user, attrs) do
user
|> User.changeset(attrs)
|> Ecto.Changeset.cast_assoc(:credential, with: &Credential.changeset/2)
|> Repo.update()
end
def update_for_slack_user(%User{} = user, attrs) do
user
|> User.changeset(attrs)
|> Ecto.Changeset.cast_assoc(:credential, with: &Credential.changeset_for_slack/2)
|> Repo.update()
end
@doc """
Deletes a User.
## Examples
iex> delete_user(user)
{:ok, %User{}}
iex> delete_user(user)
{:error, %Ecto.Changeset{}}
"""
def delete_user(%User{} = user) do
Repo.delete(user)
end
@doc """
Returns an `%Ecto.Changeset{}` for tracking user changes.
## Examples
iex> change_user(user)
%Ecto.Changeset{source: %User{}}
"""
def change_user(%User{} = user) do
User.changeset(user, %{})
end
これが Rails では ActiveRecord が行ってくれている部分の定義になります。
※以降の説明では DB 定義のファイルを DB 定義ファイル、DB 関連の関数の定義ファイルを DBUtil ファイルと呼ぶことにします。
各種 generator の働き ###全部一気に作成
$ rails generate scaffold Post title:string content:text
$ mix phx.gen.html Blog Post posts title:string content:string
諸々が全部一気に作成されます。
Rails は WebAPI 作成の用途でも上の rails generate scaffold
で対応できるようですが、
Phoenix は WebAPI 作成用だと
$ mix phx.gen.json Blog Post posts title:string content:string
を使う必要があります。
model
model に関しては、前述の違いのこともあってか厳密に同じ役割をする generator はありませんでした。
$ rails generate model User name:string age:integer
Rails に関しては model、migration が生成されます。
$ mix phx.gen.context Accounts User users name:string age:integer
Phoenix には phx.gen.model
なるものは現在ありません。
似たような働きをするものとしてこの phx.gen.context
があります。
migration と DB 定義ファイル、DBUtil ファイルが生成されます。
migration
$ rails generate migration マイグレのクラス名
...(マイグレファイルをいじる)
$ bundle exec rake db:migrate
$ mix ecto.gen.migration マイグレのクラス名
...(マイグレファイルをいじる)
$ mix ecto.migrate
これによって migration が生成&実行されます。
また、Rails を勉強していて驚いたのですが、Phoenix では Rails のようにマイグレのクラス名でコードを自動的に生成まではしてくれません。
$ rails generate migration CreateBlogs
$ mix ecto.gen.migration CreateBlogs
Rails ではこのような場合 Blogs テーブルを作成するような migration が生成されます。
しかし、Phoenix の場合このようにクラス名を指定しても、中身の書かれていない migration が作成されます。 そのため自分で migration ファイルを編集する必要があります。
Phoenix にはあるが対応するものが Rails に(多分)存在しないもの
$ mix phx.gen.schema Post posts title:string content:string
これでは migration と DB 定義ファイルが生成されます。
Rails でいう generate model
を行いたい時は phx.gen.schema
に加え。自分で DBUtil ファイルを作成する必要があります。
終わりに
どうだったでしょうか。 Rails には優秀な ActiveRecord があるので少し Phoenix では手間が増える部分がありましたが、かなり似ている部分が多かったと思います。
これを機に Elixir / Phoenix を学んでみてはいかがでしょうか。 ここまでしっかり読んでくださったあなたなら Elixir を少し学べばすぐに Phoenix をスタートできるはずです!