Phoenix 1.3

Design with intent

Biggest and the best change?

The Generator

Phoenix -> phx

1.2 1.3 Alias
iex -S mix phoenix.server iex -S mix phx.server ips
mix phoenix.routes mix phx.routes mpr
mix phoenix.gen.json mix phx.gen.json mpgj
mix phoenix.gen.channel mix phx.gen.channel mpgc

Alias FTW

1.2

1.3

 

Project structure change

├── README.md
├── _build
├── config
├── deps
├── lib
│   ├── granpals
│   └── granpals.ex
├── mix.exs
├── mix.lock
├── priv
├── test
│   ├── channels
│   ├── controllers
│   ├── models
│   ├── support
│   ├── test_helper.exs
│   └── views
└── web
    ├── channels
    ├── controllers
    ├── gettext.ex
    ├── models
    ├── router.ex
    ├── templates
    ├── views
    └── web.ex
├── README.md
├── _build
├── config
├── deps
├── lib
│   ├── granpals
│   ├── granpals.ex
│   ├── granpals_web
│   └── granpals_web.ex
├── mix.exs
├── mix.lock
├── priv
└── test
    ├── granpals
    ├── granpals_web
    ├── support
    └── test_helper.exs

Phoenix 1.3 Lib folder

├── granpals
│   ├── accounts
│   │   ├── accounts.ex
│   │   ├── credential.ex
│   │   └── user.ex
│   ├── application.ex
│   ├── jobs
│   │   ├── job.ex
│   │   └── jobs.ex
│   └── repo.ex
├── granpals.ex
├── granpals_web
│   ├── channels
│   │   └── user_socket.ex
│   ├── controllers
│   │   ├── fallback_controller.ex
│   │   ├── job_controller.ex
│   │   ├── page_controller.ex
│   │   ├── session_controller.ex
│   │   └── user_controller.ex
│   ├── endpoint.ex
│   ├── gettext.ex
│   ├── guardian_serializer.ex
│   ├── router.ex
│   ├── templates
│   │   ├── layout
│   │   │   └── app.html.eex
│   │   └── page
│   │       └── index.html.eex
│   └── views
│       ├── changeset_view.ex
│       ├── error_helpers.ex
│       ├── error_view.ex
│       ├── job_view.ex
│       ├── layout_view.ex
│       ├── page_view.ex
│       ├── session_view.ex
│       └── user_view.ex
└── granpals_web.ex

Context

For example, any time you call Elixir’s standard library, be it Logger.info/1 or Stream.map/2, you are accessing different contexts.

 

Internally, Elixir’s logger is made of multiple modules, such as Logger.Config and Logger.Backends, but we never interact with those modules directly.

Contexts are dedicated modules that expose and group related functionality.

When building a Phoenix project, we are first and foremost building an Elixir application.

Phoenix’s job is to provide a web interface into our Elixir application.

Context

Phoenix 1.2 Example

defmodule Granpals.UserController do
  use Granpals.Web, :controller

  alias Granpals.User

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


   def create(conn, %{"user" => user_params}) do
    changeset = %User{}
    |> User.changeset(user_params)
    
    case Repo.insert(changeset) do
      {:ok, user} ->
        conn
        |> put_status(:created)
        |> render("show.json", user: user)
      {:error, changeset} ->
        conn
        |> put_status(:unprocessable_entity)
        |> render(Granpals.ChangesetView, "error.json", changeset: changeset)
    end
  end

  
end

Phoenix 1.3 Example

defmodule GranpalsWeb.UserController do
  use GranpalsWeb, :controller

  alias Granpals.Accounts
  alias Granpals.Accounts.User

  action_fallback GranpalsWeb.FallbackController

  def index(conn, _params) do
    users = Accounts.list_users()
    render(conn, "index.json", users: users)
  end

  def create(conn, %{"user" => user_params}) do
    with {:ok, %User{} = user} <- Accounts.create_user(user_params) do
      conn
      |> put_status(:created)
      |> render("show.json", user: user)
    end
  end
end

Phoenix 1.3 Example

defmodule GranpalsWeb.UserController do
  use GranpalsWeb, :controller

  alias Granpals.Accounts
  alias Granpals.Accounts.User

  action_fallback GranpalsWeb.FallbackController


  def add_social_credential(conn, %{"social_credential" => social_params}) do
    user = Accounts.get_user! social_params["user_id"]
    with {:ok, %User{} = user} <- Accounts.add_social_credential_to_user(social_params) do
      conn
      |> put_status(:created)
      |> render("show.json", user: user)
    end
  end

end

Phoenix 1.3 Example

defmodule GranpalsWeb.UserController do
  use GranpalsWeb, :controller

  alias Granpals.Accounts
  alias Granpals.Accounts.User

  action_fallback GranpalsWeb.FallbackController


  def create_user_with_social_credential(conn, %{"social_credential" => social_params}) do
    with {:ok, %User{} = user} <- Accounts.create_user_with_social(social_params) do
      conn
      |> put_status(:created)
      |> render("show.json", user: user)
    end
  end

end

Phoenix 1.3 Example

defmodule Granpals.Accounts do
  import Ecto.Query, warn: false
  alias Granpals.Repo
  alias Granpals.Accounts.{User, Credential}

  def list_users do
    Repo.all(User)
  end

  def get_user!(id), do: Repo.get!(User, id)

  def create_user(attrs \\ %{}) do
    %User{}
    |> User.changeset(attrs)
    |> Ecto.Changeset.cast_assoc(:credential, with: &Credential.changeset/2)
    |> Repo.insert()
  end

  def add_social_credential_to_user(%User{} = user, attrs) do
    user
    |> User.changeset
    |> Ecto.Changeset.cast_assoc(:credential, with: &Credential.social_changeset/2)
    |> Repo.insert()
  end

  def create_user_with_social_credential(attrs \\ %{}) do
    create_user(attrs)
    |> add_social_credential_to_user()
  end
end

Phoenix 1.3 Example

defmodule Granpals.Notifications do

  def send_email(to, subject, body)
  def send_push(to, body)
  def send_sms(to, body)
  def send_all(to, subject, body)
 
end

Phoenix 1.3 Example

├── granpals
│   ├── accounts
│   │   ├── accounts.ex
│   │   ├── credential.ex
│   │   └── user.ex
│   ├── application.ex
│   ├── jobs
│   │   ├── job.ex
│   │   └── jobs.ex
|   ├── notifications
│   │   └── notifications.ex
│   └── repo.ex

The Future

The Future

The Future

Thank you

Phoenix 1.3

By Tianyi Wang