3 - 建立使用者
建立 User model: mix phoenix.gen.model User users username:string password:string encrypted_password:string email:string
修改一下 migration檔案, 讓 email欄位是唯一
#priv/repo/migration/XXXX_create_user.exs
def change do
create table(:users) do
add :username, :string, null: false
add :password, :string
add :encrypted_password, :string
add :email, :string, null: false
timestamps()
end
create unique_index(:users, [:email])
end
model 檔案, password
設定成 virtual
這樣資料庫就不會把它記下來,因為想記下來的是加密過的密碼
#web/models/user.ex
schema "users" do
field :username, :string
field :password, :string, virtual: true
field :encrypted_password, :string
field :email, :string
timestamps()
end
@doc """
Builds a changeset based on the `struct` and `params`.
"""
def changeset(struct, params \\ %{}) do
struct
|> cast(params, [:username, :email])
|> validate_required([:username, :email])
end
再來做密碼加密
我們會用到 comeonin 這個 mix library
#mix.exs
def application do
[mod: {Rumbl, []},
applications: [:phoenix, :phoenix_pubsub, :phoenix_html, :cowboy, :logger, :gettext,
:phoenix_ecto, :postgrex, :comeonin]]
end
defp deps do
[...,
{:comeonin, "~> 2.0"}]
end
輸入 mix deps.get
安裝 comeonin
再來呢,剛剛在 models/user.ex 定義過 changeset
不過那只有 validate 帳號和使用者名稱
現在要 pipe 這個 function 去驗證密碼
並把密碼用 Comeonin 加密
新增兩個 function
#models/user.ex
def registration_changeset(model, params) do
model
|> changeset(params)
|> cast(params, ~w(password), [])
|> validate_length(:password, mix: 6, max: 100)
|> put_pass_hash()
end
defp put_pass_hash(changeset) do
case changeset do
%Ecto.Chageset{valid?: true, changes: %{password: pass}} ->
put_change(changeset, :encrypted_password, Comeonin.Bcrypt.hashpwsalt(pass))
_ ->
chageset
end
end
新增 User controller
新增 controller 並加入 new, create
建立 User 的 index、show 頁面
#web/route.ex
scope "/", ShoppingSite do
pipe_through :browser # Use the default browser stack
resources "/users", UserController, only: [:new, :create]
get "/", PageController, :index
end
建立 UserController
#web/controller/user_controller.ex
defmodule ShoppingSite.UserController do
use ShoppingSite.Web, :controller
alias ShoppingSite.User
def new(conn, _params) do
changeset = User.changeset(%User{})
render conn, "new.html", changeset: changeset
end
def create(conn, %{"user" => user_params}) do
changeset = User.registration_changeset(%User{}, user_params)
case Repo.insert(changeset) do
{:ok, user} ->
conn
|> put_flash(:info, "#{user.username} created")
|> redirect(to: page_path(conn, :index))
{:error, changeset} ->
render conn, "new.html", changeset: changeset
end
end
end
#web/views/user_view.ex
defmodule ShoppingSite.UserView do
use ShoppingSite.Web, :view
end
新增 new.html頁面
#templates/user/new.html
<h1>Create an account</h1>
<%= if @changeset.action do %>
<div class="alert alert-danger">
<p>Oops! something went wrong!</p>
</div>
<% end %>
<%= form_for @changeset, user_path(@conn, :create) , fn f -> %>
<div class="form-group">
<%= text_input f, :email, placeholder: "E-mail", class: "form-control" %>
<%= error_tag f, :name %>
</div>
<div class="form-group">
<%= text_input f, :username, placeholder: "Name", class: "form-control" %>
<%= error_tag f, :username %>
</div>
<div class="form group">
<%= password_input f, :password, placeholder: "Password", class: "form-control" %>
<%= error_tag f, :password %>
</div>
<br>
<%= submit "Create account", class: "btn btn-primary" %>
<% end %>