Skip to content

Instantly share code, notes, and snippets.

@aleph-naught2tog
Created January 1, 2019 18:13
Show Gist options
  • Select an option

  • Save aleph-naught2tog/a9f94d29675935a0f29a03800753ab5b to your computer and use it in GitHub Desktop.

Select an option

Save aleph-naught2tog/a9f94d29675935a0f29a03800753ab5b to your computer and use it in GitHub Desktop.
General phoenix notes

(NB: this was done initially for Phoenix 1.3 and I haven't updated it yet.)

phoenix

requirements

  • erlang
  • elixir
  • mix
  • phoenix

???

  • param maps ALWAYS have string keys

    def show(conn, %{:messenger => messenger}) do  # ⛔️
    def show(conn, %{"messenger" => messenger}) do # ✅

    the error is:

could not find a matching HelloWeb.HelloController.show clause to process request.

  • PARENTHESES MATTER WITH PIPES

**(FunctionClauseError) no function clause matching in Plug.Conn.get_resp_header/2

  • csrf

invalid CSRF (Cross Site Request Forgery) token, make sure all requests include a valid '_csrf_token' param or 'x-csrf-token' header

mvc notes

  • the view renders -- the template just displays
    • e.g., don't in the template do first <> last -- send in a full_name to be rendered from the view
  • templates belong to views belong to controllers

various

  • resources "/users", UserController in routes.ex -- creates the suite of stuff for a User
  • resources "/users", UserController, only: [:action_one, :action_two] # or except: [...]

getting going

setup

  1. mix phx.new <app_name>, e.g., mix phx.new hello
  2. say yes to stuff re Rebar3, dependencies, etc
  3. cd hello
  4. mix ecto.create -- role "postgres" does not exist

to add a new thing:

  1. go to <app>/lib/<app>_web/router.ex

    • pipeline :browser do
      • a bunch of plugs
    • pipeline :api do
      • more plugs
    • scope "/", HelloWeb do
      • a pipe_through :browser
      • get "/", PageController, :index ...
  2. add a new route:

    <method> "/<route>", <Controller>, :<action>
    get "/hello", HelloController, :index
    • <method>: the http route we are targeting
    • "/<route>: the route being targeted
    • <Controller>: controller whose action we will call
    • :<action>: the action we want to do

    "WHEN we METHOD to ROUTE, DO ACTION of the CONTROLLER"

  3. make out new controller

    1. <app>/lib/<app>_web/controllers/<Controller>.ex
    2. defmodule <App>Web.<Controller> do ...
    3. it needs to have the :<action> we gave it above
  4. make a corresponding view

    1. <app>/lib/<app>_web/views/<controller>_view.ex
    2. defmodule <App>Web.<App>View do ...
  5. make a folder if we need it for the new template

    1. <app>/lib/<app>_web/templates/<app>/index.html.eex

eex

  • <%= "boop" %> will echo
  • <% boop() %> executes but does not render/echo.
  • <%= @user %> -- implies that our controller sent along some variable under 'user', ie, render conn, "boop.html", user: user -- the @ is short for assigns.user

mix commands

  • start server: mix phx.server
  • inspect routes: mix phx.routes

routing

get "/route", ...

resources

  • defines a suite of routes based on actions

forward

  • forwards to a plug
  • can pop through pipe_through first -- ie java filters -- you pipe_through a pipeline, as defined

plugs

  • transforms to the connection

  • module plug: init, call

  • function plugs: defined in a controller, plug :authenticate then defp authenticate(conn, _) do ...

  • plug :assign_welcome_message, "Welcome back" makes that the default for anything in this controller -- we can override it manually via assign(:message, "Eff you")

    • defp assign_welcome_message(conn, msg), do: assign(conn, :message, msg) end
    • if only sometimes:
    plug :assign_welcome_message, "Hi!" when action in [:index, :show]

endpoints

  • entrance/exit for ALL requests of the application
  • THIS IS WHERE WE ENABLE AUTH, CORS, SUBDOMAIN
  • this is our web.xml
  • where we setup https

controllers

  • index - GET all

  • show - GET by id

  • new - GET form for new

  • create - POST data for new

  • edit - GET by id and display edit form

  • update - PUT params for 1 updated item

  • delete - DELETE by id

  • signature: def <action>(conn, %{"param_key" => param_key}) -- good to pattern match inside the signature itself to pass on simplest data

  • rendering

    • do NOT require a view or a template
      • text conn, "Showing id #{id} -- plain text
      • json conn, %{id: id} -- json (who knew)
      • html conn, """<html> ... </html>"""
    • render: render conn, "page.html", username: username
      • CONTROLLER MUST HAVE SAME ROOT NAME AS VIEW
      • VIEW MUST HAVE SAME ROOT NAME AS DIRECTORY OF TEMPLATE
      • HelloController requires HelloView
      • HelloView requires lib/hello_web/templates/hello
      • ...which must contain "page.html"
  • fyi: instead of

    # these are equivalent
    def index(conn, _params) do
        render conn, "index.html", message: "Welcome back!"
    end
     
    def index(conn, _params) do
        conn
        |> assign(:message, "Welcome back!")
        |> render("index.html"
    end
  • want a different response? no problem!

    def index(conn, _params) do
        conn
        |> send_resp(201, "")
    end
  • diff layout? eyyyyyyyy!

    def index(conn, _params) do
      conn
      |> put_layout("admin.html")
      |> render("index.html")
    end
  • diff status with a render?

    def index(conn, _params) do
        conn
        |> put_status(202)
        |> render("index.html"
    end
  • compose ALL THE THINGS

    def index(conn, _params) do
        conn
        |> put_status(:not_found)
        |> put_view(HelloWeb.ErrorView)
        |> render("404.html")
    end
  • redirect!

    def index(conn, _params) do
        redirect conn, to: "/redirect_test"
    end
    
    def redirect_test(conn, _params) do
        text conn, "Redirect!"
    end
  • default error

    • somewhere, MyFallbackController
    • in the control we want handled: action_fallback HelloWeb.MyFallbackController
  • PostFinder plug

defmodule HelloWeb.PostFinder do
    use Plug
    import Plug.Conn
    
    alias Hello.Blog
    
    def init(opts), do: opts
    
    def call(conn, _) do
        case Blog.get_post(conn.params["id"]) do
            {:ok, post} ->
                assign(conn, :post, post)
            {:error, :notfound} -> 
                conn
                |> send_resp(505, "not found"
                |> halt() # this prevents the rest of the pipeline
        end
    end
end

random

  • assign is setAttribute ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment