Phoenix Elixir – Make CRUD with PostgreSQL

Phoenix Elixir – Make CRUD with PostgreSQL

Sebelumnya perkenalkan Phoenix Framework.. adalah framework yang dibangun di atas bahasa pemrograman Elixir, lebih lengkap tentang Elixir dapat dilihat di http://elixir-lang.org/ dan Phoenix dapat dilihat di http://www.phoenixframework.org/. Code Elixir sendiri akan di compile ke Erlang byte code dan berjalan di atas Erlang VM, karena itu kita harus memiliki Erlang untuk menjalankan Elixir.

Proses installasi Phoenix Framework dapat dilihat pada tautan berikut http://www.phoenixframework.org/docs/installation , tautan tersebut akan memaparkan proses installasi Phoenix, Elixir, Erlang dan tools lain yang dibutuhkan untuk menjalankan Phoenix Framework.

Pada postingan ini saya tidak memaparkan bagaimana proses installasi dan proses membuat project baru pada Phoenix Framework (saya asumsikan sudah bisa) tapi jika anda masih belum tahu cara membuat project baru pada Phoenix, silahkan lihat pada tautan berikut http://www.phoenixframework.org/docs/up-and-running.

Setelah membuat project baru kita akan coba memahami bagaimana membuat operasi CRUD pada Phoenix. Saya akan menggunakan database postgreSQL (karena secara default di support Phoenix dan update terbaru dengan fitur yang bagus). Phoenix menyediakan fitur generate operasi CRUD dimana dapat menghemat waktu kita dalam mengembangkan aplikasi. Pada postingan ini kita akan generate CRUD tabel “users” (tabel “users” belum dibuat pada database postgreSQL, phoenix akan melakukannya).

Masukkan perintah berikut :

$ mix phoenix.gen.html User users name:string email:string birth:date age:integer
* creating web/controllers/user_controller.ex
* creating web/templates/user/edit.html.eex
* creating web/templates/user/form.html.eex
* creating web/templates/user/index.html.eex
* creating web/templates/user/new.html.eex
* creating web/templates/user/show.html.eex
* creating web/views/user_view.ex
* creating test/controllers/user_controller_test.exs
* creating web/models/user.ex
* creating test/models/user_test.exs
* creating priv/repo/migrations/20161224162452_create_user.exs

Add the resource to your browser scope in web/router.ex:
    resources "/users", UserController

Remember to update your repository by running migrations:
    $ mix ecto.migrate

Perintah di atas mendefinisikan data untuk tabel “users” yang akan disimpan dalam model “User”. Pada perintah kita harus mendefinisikan field yang didefinisikan pada tabel users beserta tipe datanya. Perintah di atas akan men-generate file eex elixir yang digunakan untuk operasi CRUD. Apakah tabel users pada database sudah dibuat? Belum.. kita akan memasukkan perintah lain untuk melakukannya..

Lakukan modifkasi pada web/route.ex :

scope "/", FirstApp do
    pipe_through :browser # Use the default browser stack
    get "/", PageController, :index
    resources "/users", UserController
  end

Setelah memodifikasi code pada web/route.ex masukkan perintah berikut pada terminal :

mix ecto.migrate
Compiling 11 files (.ex)
Generated first_app app
23:28:53.583 [info]  == Running FirstApp.Repo.Migrations.CreateUser.change/0 forward
23:28:53.583 [info]  create table users
23:28:53.616 [info]  == Migrated in 0.0s

Perintah di atas akan menjalankan fungsi ecto.migrate. Perintah tersebut akan menjalankan semua file yang ada pada direktori “priv/repo/migrations” dimana migrations yang dilakukan berkaitan dengan database. Saat ini kita memiliki sebuah file pada “priv/repo/migrations” yaitu file yang di generate dari proses sebelumnya. Perintah di atas secara otomatis akan membuat sebuah tabel pada database anda dengan schema seperti berikut :

screen-shot-2016-12-24-at-11-29-29-pm

Pada perintah sebelumnya kita hanya membuat kolom name, email, birth dan age. Phoenix secara otomatis akan menambahakan kolom id sebagai primary key dan kolom inserted_at dan updated_at dari fungsi timestamp yang dimiliki phoenix.

Setelah tabel “users” dibuat pada database, barulah kita dapat menjalankan proses CRUD yang telah di generate sebelumnya. Jalankan perintah berikut pada terminal :

$ mix phoenix.server

Masukkan url berikut : http://localhost:4000/users , akan tampil halaman pengelolaan data “users” seperti berikut :

screen-shot-2016-12-25-at-12-27-03-am

Jika memilih menu “New user” aplikasi akan mengarahkan anda pada halaman untuk menambah data user baru :

screen-shot-2016-12-25-at-12-27-11-am

Khusus input data dengan type “date” phoenix secara otomatis membuat form input berupa dropdown dengan pilihan tahun, bulan dan tanggal. Form yang digenerate juga memiliki validasi sebagai contoh jika anda klik “Submit” tanpa mengisi data akan muncul halaman dengan tampilan error seperti berikut :

screen-shot-2016-12-25-at-1-11-05-am

Berikut tampilan halaman user ketika sudah menambah data baru :

screen-shot-2016-12-25-at-12-27-59-am

Pada halaman tersebut disediakan fitur untuk melakukan show, edit dan delete data. Silahkan mencoba masing-masing fitur..

Bekerja dengan framework yang memiliki fitur generate sering kali membuat programmer pemula jadi tidak memahami code hasil proses generate yang dilakukan. Untuk itu mari kita pahami code yang ada di balik proses generate tersebut.

Phoenix Framework menggunakan konsep MVC (Model – View – Controller) yang artinya kebanyakan proses pada Phoenix Framework akan berputar-putar pada ketiga komponen tersebut (bagi anda yang belum memahami apa itu MVC silahkan googling ya…). Proses generate yang dilakukan oleh Phoenix akan membuat beberapa file pada folder:

  • web/controllers/ (controller)
  • web/models (models)
  • web/templates/user (views)
  • web/views
  • test/controllers
  • test/models
  • priv/repo/migrations

Pada priv/repo/migrations kita akan melihat file migration createuser :

defmodule FirstApp.Repo.Migrations.CreateUser do
  use Ecto.Migration
  def change do
    create table(:users) do
      add :name, :string
      add :email, :string
      add :birth, :date
      add :age, :integer
      timestamps()
    end
  end
end

Pada code di atas dapat dilihat bahwa migration akan membuat sebuah tabel user dengan kolom yang sudah kita definisikan pada terminal ditambah dengan “timestamps()” yang terdiri dari kolom inserted_at dan updated_at.

MODEL

Model digunakan untuk mendefinisikan data pada suatu tabel dalam database termasuk jenis data, validasi, query, dll. Proses generate oleh phoenix akan meletakkan file model dalam folder “web/models” pada kasus ini pada file “web/models/user.ex”. Code dalam model user adalah seperti berikut :

defmodule FirstApp.User do
  use FirstApp.Web, :model

  schema "users" do
    field :name, :string
    field :email, :string
    field :birth, Ecto.Date
    field :age, :integer
    timestamps()
  end

  @doc """
  Builds a changeset based on the `struct` and `params`.
  """

  def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, [:name, :email, :birth, :age])
    |> validate_required([:name, :email, :birth, :age])
  end
end

Code di atas merupakan class FirstApp.User merupakan sebuah model. Pada class tersebut terdiri dari definisi schema model Users yang mendefinisikan nama field dan type data masing-masing field. Field “inserted_at” dan “updated_at” didefinisikan pada fungsi “timestamps()”.

Model juga mendefinisikan changeset, changeset ke depannya akan mendefinisikan data yang digunakan untuk memanipulasi suatu data. Changeset pada code di atas terdiri dari “cast” yang mendefinisikan field yang akan menjadi parameter pada proses modifikasi data dan sebagai daftar field yang akan di update (cast hanya dapat berisi nama field yang telah didefinisikan pada schema. Changeset di atas juga terdiri dari “validate_required” yang berisi kumpulan field yang wajib diisi ketika proses modifikasi.

CONTROLLER DAN VIEW

Controller adalah komponen yang melakukan suatu action dan menterjemahkan request suatu user. Ketika user melakukan request suatu action maka controller akan menterjemahkan permintaan user dan menentukan UI (biasanya memanggil suatu view) dan data (diambil dari model) yang akan ditampilkan kepada user. File controller yang digenerate oleh phoenix ada pada “web/controller/user_controller.ex” sedangkan views ada pada folder “web/templates/user” dan “web/views/user_view.ex”. Baiklah pertama-tama kita akan memahami bagaimana url http://localhost:4000/users yang kita akses akan menampilkan kita halaman daftar user yang sudah ada pada database.

Ketika kita melakukan request suatu url, aplikasi akan menangkap request url dari file “web/router.ex” tepatnya pada code :

 scope "/", FirstApp do
    pipe_through :browser # Use the default browser stack
    get "/", PageController, :index
    resources "/users", UserController
  end

Code di atas telah mendefinisikan seluruh resources yang ada pada controller user agar dapat diakses dengan url “/users”, adapaun jenis fungsi yang dapat dijalankan pada url mengikuti nama masing-masing fungsi dalam UserController.

FUNGSI INDEX

Mengakses url http://localhost:4000/users secara otomatis akan mengarahkan anda mengakses fungsi “index” pada UserController. Perintah yang dijalankan adalah : 

def index(conn, _params) do
    users = Repo.all(User)
    render(conn, "index.html", users: users)
  end

Yang terjadi pada code di atas adalah :

  • Mendefinisikan variabel users yang diisi dengan Repo.all(User), fungsi Repo.all(User) akan menampilkan seluruh data yang ada pada tabel User. UserController telah mendefinisikan code “  alias FirstApp.User” yang artinya kita tidak perlu memanggil model User dengan memanggil FirstApp.User tetapi cukup “User” saja.
  • Me-render halaman index.html (pada folder “web/template/users”) dan mengirimkan variabel “users” dengan nama users ke halaman yang di-render.

Halaman index.html yang di-render pada fungsi index adalah halaman index.html pada direktori “web/template/users/“ dimana memiliki code sebagai berikut :

<h2>Listing users</h2>
<table class="table">
  <thead>
    <tr>
      <th>Name</th>
      <th>Email</th>
      <th>Birth</th>
      <th>Age</th>
      <th></th>
    </tr>
  </thead>

  <tbody>
<%= for user <- @users do %>
    <tr>
      <td><%= user.name %></td>
      <td><%= user.email %></td>
      <td><%= user.birth %></td>
      <td><%= user.age %></td>
      <td class="text-right">
        <%= link "Show", to: user_path(@conn, :show, user), class: "btn btn-default btn-xs" %>
        <%= link "Edit", to: user_path(@conn, :edit, user), class: "btn btn-default btn-xs" %>
        <%= link "Delete", to: user_path(@conn, :delete, user), method: :delete, data: [confirm: "Are you sure?"], class: "btn btn-danger btn-xs" %>
      </td>
    </tr>
<% end %>

  </tbody>
</table>
<%= link "New user", to: user_path(@conn, :new) %>

Code di atas akan menampilkan sebuah tabel dengan head Name, Email, Birth, Age dan sebuah head tanpa label. Isi dari tabel tersebut diisi dengan melakukan looping data user yang menampilkan field name, email, birth dan age. Sedangkan satu kolom lagi akan diisi dengan tombol Show, Edit dan Delete untuk masing-masing data.

MENAMBAH DATA BARU

Untuk menambah data baru kita akan melibatkan dua buah fungsi pada controller yaitu “new” (menampilkan form input data) dan “create” (memproses data yang telah diinput pada form). Code fungsi “new” adalah sebagai berikut :

  def new(conn, _params) do
    changeset = User.changeset(%User{})
    render(conn, "new.html", changeset: changeset)
  end

Yang terjadi pada code di atas adalah :

  • mendefinisikan variabel “changeset” dengan changeset pada model User dengan kondisi seluruh field yang masih kosong.
  • me-render halaman “new.html” dan mengirimkan variabel changeset.

Code “new.html” adalah sebagai berikut :

<h2>New user</h2>
<%= render "form.html", changeset: @changeset,
                        action: user_path(@conn, :create) %>
<%= link "Back", to: user_path(@conn, :index) %>

Code new.html me-render kembali halaman “form.html” (halaman ini juga digunakan ulang pada halaman update) dan mengirim variabel changeset yang berasal dari controller sebelumnya dan mengirimkan variabel “action” yang merupakan url dari fungsi “create” pada UserController. Silahkan lihat code “form.html.eex” anda. Code tersebut berupa form yang terdiri dari beberapa macam input data. Ketika tombol “Submit” dipilih, maka form di atas akan menjalankan fungsi “create” pada UserController. Fungsi “create” sendiri akan menjalankan code berikut :

  def create(conn, %{"user" => user_params}) do
    changeset = User.changeset(%User{}, user_params)

    case Repo.insert(changeset) do
      {:ok, _user} ->
        conn
        |> put_flash(:info, "User created successfully.")
        |> redirect(to: user_path(conn, :index))
      {:error, changeset} ->
        render(conn, "new.html", changeset: changeset)
    end
  end

Yang terjadi pada code di atas adalah :

  • “create” akan menangkap inputan dari form “new.html” melalui parameter “user”
  • mendefinisikan variabel “changeset” yang diisi oleh changeset pada model User dan field pada changeset akan diisi oleh parameter “user_params” yang didapat dari inputan pada form.
  • dilakukan proses input data changeset ke dalam database.
  • Jika data berhasil disimpan ke dalam database maka fungsi akan menampilkan pesan sukses (fungsi put_flash digunakan untuk menampilkan suatu pesan pada tampilan layout) dan me-redirect ke halaman index.
  • Jika data gagal disimpan maka aplikasi akan me-render halaman “new.html” kembali beserta changeset yang telah di definisikan sebelumnya. Changeset yang dikirim juga akan membawa serta pesan error yang terjadi pada inputan sebelumnya sehingga penguin dapat melihat validasi yang gagal dipenuhi ketika proses insert data.

MENAMPILKAN DETAIL DATA

Fungsi untuk menampilkan detail data ada pada code :

def show(conn, %{"id" => id}) do
    user = Repo.get!(User, id)
    render(conn, "show.html", user: user)
  end

Dimana code di atas akan melakukan tahapan sebagai berikut :

  • Mendefinisikan variabel user yang diisi dengan data dari tabel user yang memiliki primary key atau id sesuai dengan parameter yang di dapat dari url. Fungsi “Repo.get!(User,id)”  artinya data akan diambil melalui model User.
  • Me-render halaman “show.html” dan mengirimkan variabel user yang telah didefinisikan sebelumnya.

Halaman show.html akan menampilkan data kepada user dengan code sebagai berikut :

<h2>Show user</h2>
<ul>
  <li>
    <strong>Name:</strong>
    <%= @user.name %>
  </li>

  <li>
    <strong>Email:</strong>
    <%= @user.email %>
  </li>

  <li>
    <strong>Birth:</strong>
    <%= @user.birth %>
  </li>

  <li>
    <strong>Age:</strong>
    <%= @user.age %>
  </li>
</ul>

<%= link "Edit", to: user_path(@conn, :edit, @user) %>
<%= link "Back", to: user_path(@conn, :index) %>

Code di atas menampilkan masing-masing field pada tabel user ke dalam tag HTML yang telah disusun. Pada HTML tersebut juga ditampilkan tautan untuk melakukan fungsi edit dan fungsi back ke halaman index.

MEMPERBAHARUI DATA

Proses memperbaharui/update data akan ditampilkan pada fungsi “edit” dan “update. Cara kerja kedua fungsi ini sama dengan fungsi “new” dan “create” pada saat kita ingin menambahkan data baru. Perbedaan pada kedua proses ini adalah fungsi “edit” yang mendefinisikan changeset dengan mengambil data dari tabel user dengan id/primary key tertentu :

 def edit(conn, %{"id" => id}) do
    user = Repo.get!(User, id)
    changeset = User.changeset(user)
    render(conn, "edit.html", user: user, changeset: changeset)
  end

Pada code tersebut changeset akan mendefinisikan changeset dari model User dimana isi dari changeset akan mengambil data pada tabel user yang memiliki id tertentu.

MENGHAPUS DATA

Perintah delete data akan menjalankan code berikut :

def delete(conn, %{"id" => id}) do
    user = Repo.get!(User, id)
    # Here we use delete! (with a bang) because we expect
    # it to always work (and if it does not, it will raise).

    Repo.delete!(user)
    conn
    |> put_flash(:info, "User deleted successfully.")
    |> redirect(to: user_path(conn, :index))
  end

Code tersebut akan mendefinisikan variabel user yang berisi data user yang memiliki id sesuai request. Data user akan digunakan untuk perintah hapus data dimana setelah proses hapus data berjalan aplikasi akan menampilkan pesan sukses hapus data dan me-redirect pengguna ke halaman index awal.

MODIFIKASI APLIKASI

PENYEMPURNAAN FORM

Code yang telah di generate oleh Phoenix tidak selalu dapat memenuhi kebutuhan kita. Untuk itu diperlukan proses modifikasi code secara manual oleh programmer. Pertama-tama mari kita sempurnakan tampilan form input data. Form input data memiliki sebuah input khusus untuk input nilai date (tanggal) dimana inputan tersebut akan menampilkan 3 buah dropdown tahun, bulan dan tanggal. Dropdown tahun hanya menampilkan sepuluh tahun terakhir dari tahun saat ini, padahal kita ingin menyediakan opsi dari tahun 1960 sampai 2010. Lakukan modifikasi pada code “web/templates/user/form.html.eex” tepatnya pada baris code berikut :

<%= date_select f, :birth, class: "form-control" %>

lakukan perubahan menjadi seperti berikut :

    <%= date_select f, :birth, year: [options: 1960..2010], class: "form-control" %>

Jika anda jalankan aplikasi maka dropdown tahun akan menampilkan opsi tahun dari tahun 1960 hingga tahun 2010.

Untuk memberikan nilai default pada tahun dapat memasukkan code sebagai berikut :

    <%= date_select f, :birth, year: [value: 1989 ,options: 1960..2010], class: "form-control" %>

Jika anda ingin membuat rentang tahun dari tahun 1960 sampai dengan tahun saat ini dapat membuat code seperti berikut :

    <%= date_select f, :birth, year: [value: 1989 ,options: 1960..DateTime.utc_now.year], class: "form-control" %>

VALIDASI

Sebagai contoh kita ingin menambahkan validasi berikut :

  • Nama harus memiliki panjang minimal 3 huruf
  • Email memiliki panjang minimal 5 huruf
  • Email harus memiliki format email yang benar, jika format email salah maka akan mengirimkan pesan error “Please fix your email format”

Phoenix meletakkan segala hal yang berkaitan dengan data (termasuk validasi) pada Model. Untuk menambahkan validasi di atas kita dapat melakukan modifikasi model User tepatnya pada changeset menjadi seperti berikut :

def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, [:name, :email, :birth, :age])
    |> validate_required([:name, :email, :birth, :age])
    |> validate_length(:name, min: 3)
    |> validate_length(:email, min: 5)
    |> validate_format(:email, ~r/@/, message: "Please fix your email format")
  end
  • validate_length digunakan membatasi panjang suatu field. Pada code di atas kita memberikan validasi field name minimal terdiri dari 3 huruf dan field email minimal terdiri dari 5 huruf. Selain membatasi panjang minimal anda juga dapat membatasi panjang maksimal (gunakan “max”) dan membuat suatu field harus memiliki panjang tertentu (gunakan “is”).
  • validate_format digunakan untuk menentukan format suatu  inputan data. Pada code di atas validate_format mengharuskan field email memiliki karakter “@“ di dalamnya.
  • validate_format di atas juga akan mengeluarkan pesan error “Please fix your email format” jika pengguna tidak mengikuti format yang telah ditentukan.

Selesai.. itulah sedikit perkenalan dengan Phoenix Framework..

Advertisements

6 Responses to Phoenix Elixir – Make CRUD with PostgreSQL

  1. Pingback: Phoenix Elixir – Make Authenticate Page (Register, Login, Logout) | Sabitlabscode

  2. naldisaja says:

    mw tanya gan cara instal postgresqlnya gimna ya, saya blm bisa instalnya, mohon pencerahannya gan 😀

  3. naldisaja says:

    ok makasih info nya gan hehehe…

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: