Category: ‘Phoenix Framework’

Phoenix Server をバックグラウンドにて動作させる方法

2020年4月20日 Posted by PURGE

$ elixir --erl "-detached" -S mix phx.server

Phoenix – React 連携

2019年12月2日 Posted by PURGE

Phoenix Project 作成

$ mix phx.new ProjectName --app project_name --no-ecto

npm モジュールインストール

$ cd ProjectName/assets/node_modules/
$ npm install --save @babel/preset-react
$ npm install --save @babel/polyfill
$ npm install --save react react-dom redux react-redux
$ npm install --save react-router-dom
$ npm install --save connected-react-router history
$ npm install --save redux-thunk
$ npm install --save axios
$ npm install --save redux-form react-hook-form
$ npm install --save react-bootstrap
$ npm install --save history
$ npm install redux-saga --save

.babelrc 設定

assets/.babelrc
{
"presets": [
"@babel/preset-env",
"@babel/react"
]
}

エントリポイント(index.js)の変更

$mv assets/js/app.js assets/js/index.js
index.js

import React from 'react';
import ReactDOM from 'react-dom';

ReactDOM.render(
<div>
Hello React!!
</div>,
document.getElementById('root')
);

webpack.config.js

entry: {
'./js/index.js': glob.sync('./vendor/**/*.js').concat(['./js/index.js'])
},
output: {
filename: 'index.js',
path: path.resolve(__dirname, '../priv/static/js')
},

index.eex の変更

lib/project_name_web/templates/page/index.eex

<section class="row">
<article class="column">
<div id="root"></div>
</article>
</section>

index.jsパスの変更

lib/project_name_web/templates/layout/app.eex

<script type="text/javascript" src="<%= Routes.static_path(@conn, "/js/index.js") %>"></script>

Phoenix でメールを送信する

2018年6月1日 Posted by PURGE

まずは、メール用のプロジェクトの依存ライブラリである Bumboo をインストールする。

■ mix.exs

  def application do
    [
      mod: {MyApp.Application, []},
      extra_applications: [:logger, :runtime_tools, :bamboo]
    ]
  end

  defp deps do
    [
      {:phoenix, "~> 1.3.2"},
      {:phoenix_pubsub, "~> 1.0"},
      {:phoenix_ecto, "~> 3.2"},
      {:mariaex, ">= 0.0.0"},
      {:phoenix_html, "~> 2.10"},
      {:phoenix_live_reload, "~> 1.0", only: :dev},
      {:gettext, "~> 0.11"},
      {:cowboy, "~> 1.0"},
      {:bamboo, "~> 0.7"},
      {:bamboo_smtp, "~> 1.2.1"}
    ]
  end

mix deps.get で追加した依存ライブラリをインストールする。

$ mix deps.get

プロジェクト設定ファイルに、メール設定情報を追記する。ここではSMTPサーバとしてGmailアカウントを使う。

■ config/config.exs

config :myapp, MyApp.Mailer,
       adapter: Bamboo.SMTPAdapter,
       server: "smtp.gmail.com",
       port: 587,
       username: "my@gmail.com",
       password: "password",
       tls: :if_available, # can be `:always` or `:never`
       ssl: false, # can be `true`
       retries: 1

メーラーモジュールを作成する。

■ lib/myapp/mailer.ex

defmodule MyApp.Mailer do
  use Bamboo.Mailer, otp_app: :myapp
end

メールモジュールを作成する。

■ lib/myapp/email.ex

defmodule MyApp.Email do
  use Bamboo.Phoenix, view: MyApp.EmailView

  def hello_email(email) do
    new_email
    |> to(email)
    |> from("my@gmail.com")
    |> subject("Welcome!")
    |> text_body("Welcome to My App!!")
  end
end

まずは、試しにコマンドラインからメールしてみる。

$ iex -S mix
Interactive Elixir (1.6.5) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> MyApp.Email.hello_email("tajima@whoocus.com") |> MyApp.Mailer.deliver_now
[debug] Sending email with Bamboo.SMTPAdapter:

%Bamboo.Email{assigns: %{}, bcc: [], cc: [], from: {nil, "my@gmail.com"}, headers: %{}, html_body: nil, private: %{}, subject: "Welcome!", text_body: "Welcome to My App!!", to: [nil: "tajima@whoocus.com"]}

%Bamboo.Email{
  assigns: %{},
  bcc: [],
  cc: [],
  from: {nil, "my@gmail.com"},
  headers: %{},
  html_body: nil,
  private: %{},
  subject: "Welcome!",
  text_body: "Welcome to My App!!",
  to: [nil: "tajima@whoocus.com"]
}

次に、Webアプリからメールを飛ばしてみる。コントローラを作成してルータに登録する。
ここでは、単純にページにアクセスしたら固定のメールが飛ぶ仕組み。

■ mail_controller.ex

defmodule MyApp.MailController do
  use MyApp, :controller

  alias MyApp.Mailer

  def index(conn, _params) do
    MyApp.Email.hello_email("tajima@whoocus.com") 
      |> Mailer.deliver_now
    render conn, "index.html"
  end
end

■ router.ex

    get "/mail", MailController, :index

このページにアクセスする都度にメールが飛ぶ。
ここでは、個人的な試作のためにGmailを使用しているので、SMTPサーバへのアクセスと送信はそこそこ遅い。
メールサーバを立てる方が実用的である。

Phoenix static file の追加

2018年5月30日 Posted by PURGE

uploads フォルダを、作成してブラウザから参照する場合。
priv/static/files を作成した場合の記述方法。

endpoint.ex

  plug Plug.Static,
       at: "/", from: :sample, gzip: false,
       only: ~w(css fonts images js uploads favicon.ico robots.txt) # 参照名を追記

  plug Plug.Static,
      at: "/uploads", from: "/files" # 参照名と、物理パス名を記述する。

Phoenix でsession を使用する

2018年5月22日 Posted by PURGE

■ PageController01 セッション設定

  def index(conn, _params) do
    conn = put_session(conn, :msg01, "Hello session 01!!")
    conn = put_session(conn, :msg02, "Hello session 02!!")

    render conn, "index.html"
  end

■ PageController02 セッション取得

  def index(conn, _params) do
    msg01 = get_session(conn, :msg01)
    msg02 = get_session(conn, :msg02)
    render(conn, "index.html", [msg01: msg01, msg02: msg02])
  end

■ index.html.eex セッション格納データ表示

<div>MSG01: <%= @msg01 %></div>
<div>MSG02: <%= @msg02 %></div>

Phoenix association belongs_to

2018年5月21日 Posted by PURGE

■ member.ex

defmodule Sample.Portal.Member do
  use Ecto.Schema
  import Ecto.Changeset


  schema "members" do
    field :birthday, :date
    field :email, :string
    field :first_name, :string
    field :last_name, :string
    field :passwd, :string
    field :gender_cd, :integer
    #    field :team_id, :id
    belongs_to :team, Sample.Portal.Team

    timestamps()

  end

  @doc false
  def changeset(member, attrs) do
    member
    |> cast(attrs, [:last_name, :first_name, :email, :passwd, :birthday, :team_id, :gender_cd])
    |> validate_required([:last_name, :first_name, :email, :passwd, :birthday, :team_id ])
  end
end

■ repo.ex

  def list_members do
    Repo.all(Member)
    |> Repo.preload(:team)
  end

  def get_member!(id), do: Repo.get!(Member, id) |> Repo.preload(:team)

■ index.html.eex

  <%= for member <- @members do %>
    <td><%= member.team.team_name %></td>
  <% end %>

■ show.html.eex

    <%= @member.team.team_name %>

Phoenix select と radio_button

2018年5月21日 Posted by PURGE

■ selectタグの場合

  <div class="form-group">
    <%= label f, :gender, class: "control-label" %>
    <%= select f, :gender_cd, ["男性": "1", "女性": "2", "その他": "0"], class: "control-label" %>
    <%= error_tag f, :team %>
  </div>

■ radio button タグの場合

  <div class="form-group">
    <%= label f, :gender, class: "control-label" %>
    <%= radio_button f, :gender_cd, "1", checked: true, class: "control-label" %>男性
    <%= radio_button f, :gender_cd, "2", class: "control-label" %>女性
    <%= radio_button f, :gender_cd, "0", class: "control-label" %>その他
    <%= error_tag f, :team %>
  </div>

Phoenix date_select フォーマット

2018年5月21日 Posted by PURGE

  <div class="form-group">
    <%= label f, :birthday, class: "control-label" %>
        <%= date_select f, :birthday, default: :calendar.local_time(), builder: fn b -> %>
          <%= b.(:month, [
                  options: [
                    {gettext("1月"), "1"},
                    {gettext("2月"), "2"},
                    {gettext("3月"), "3"},
                    {gettext("4月"), "4"},
                    {gettext("5月"), "5"},
                    {gettext("6月"), "6"},
                    {gettext("7月"), "7"},
                    {gettext("8月"), "8"},
                    {gettext("9月"), "9"},
                    {gettext("10月"), "10"},
                    {gettext("11月"), "11"},
                    {gettext("12月"), "12"},
                  ]
                ]) %>
        <% end %>
    <%= error_tag f, :birthday %>
  </div>

Phoenix date_select 期間とデフォルト(本日日付)の設定方法

2018年5月21日 Posted by PURGE

  <div class="form-group">
    <%= label f, :birthday, class: "control-label" %>
    <%= date_select f, :birthday, default: :calendar.local_time(), year: [options: 1950..2030], class: "form-control" %>
    <%= error_tag f, :birthday %>
  </div>

Phoenix Framwork select tag

2018年5月21日 Posted by PURGE

■member_controller.ex

def new(conn, _params) do
  changeset = Portal.change_member(%Member{})
  teams = Portal.list_teams()
  render(conn, "new.html", changeset: changeset, teams: teams)
end

def edit(conn, %{"id" => id}) do
  member = Portal.get_member!(id)
  teams = Portal.list_teams()
  changeset = Portal.change_member(member)

  render(conn, "edit.html", member: member, changeset: changeset, teams: teams)
end

■new.html.eex / edit.html.eex

<h2>New Member</h2>

<%= render "form.html", Map.put(assigns, :action, member_path(@conn, :create)) %>

<h2>Edit Member</h2>

<%= render "form.html", Map.put(assigns, :action, member_path(@conn, :update, @member)) %>

■form.html.eex

<div class="form-group">
  <%= label f, :teams, class: "control-label" %>
  <%= select f, :team_id, Enum.map(@teams, &{&1.team_name, &1.id}) ,  class: "control-label" %>
  <%= error_tag f, :team %>
</div>

<div class="form-group">
  <%= label f, :gender, class: "control-label" %>
  <%= select f, :gender_id, ["男性": "1", "女性": "2", "その他": "0"], class: "control-label" %>
  <%= error_tag f, :team %>
</div>