From 3911a41acfb5cf594e0ae8a9f82806c2753438dc Mon Sep 17 00:00:00 2001 From: "Rico Sta. Cruz" Date: Sun, 12 Mar 2017 05:18:38 +0800 Subject: [PATCH] Update --- phoenix-ecto.md | 3 +- phoenix-ecto@1.3.md | 177 ++++++++++++++++++++++++++++++++++++++++++++ redux.md | 48 +++++++++++- 3 files changed, 223 insertions(+), 5 deletions(-) create mode 100644 phoenix-ecto@1.3.md diff --git a/phoenix-ecto.md b/phoenix-ecto.md index 5abaa07d6..09b04c7e1 100644 --- a/phoenix-ecto.md +++ b/phoenix-ecto.md @@ -3,11 +3,12 @@ title: "Phoenix: Ecto models" category: Elixir --- +This is for Phoenix 1.2 and below. [Phoenix 1.3 has a new API.](phoenix-ecto@1.3.html). + ## Generating ``` $ mix phoenix.gen.html Profile profiles email:string age:integer - $ mix phoenix.gen.html User users email:string hashed_password:string ``` diff --git a/phoenix-ecto@1.3.md b/phoenix-ecto@1.3.md new file mode 100644 index 000000000..38cfb0087 --- /dev/null +++ b/phoenix-ecto@1.3.md @@ -0,0 +1,177 @@ +--- +title: "Phoenix: Ecto models" +category: Elixir +--- + +## Generating + +``` +$ mix phx.gen.html Accounts Profile profiles email:string age:integer +$ mix phx.gen.html Accounts User users email:string hashed_password:string +``` + +## Schema + +```elixir +defmodule Myapp.Accounts.User do + use Ecto.Schema + + schema "users" do + field :name + field :age, :integer + # :id :binary :integer :float :boolean :string :binary + # {:array, inner_type} :decimal :map + + field :password, virtual: true + + timestamps() + end +end +``` + +## Changesets + +```elixir +def changeset(user, params \\ :empty) do + %User{} + |> Ecto.Changeset.change # basic casting to changeset + + user + |> cast(params, ~w(name email), ~w(age)) # params to Changeset + + |> validate_format(:email, ~r/@/) + + |> validate_inclusion(:age, 18..100) + |> validate_exclusion(:role, ~w(admin superadmin)) + |> validate_subset(:pets, ~w(cat dog parrot whale)) + + |> validate_length(:body, min: 1) + |> validate_length(:body, min: 1, max: 160) + |> validate_length(:partners, is: 2) + + |> validate_number(:pi, greater_than: 3) + |> validate_number(:pi, less_than: 4) + |> validate_number(:pi, equal_to: 42) + + |> validate_change(:title, fn _, _ -> []) + |> validate_confirmation(:password, message: "does not match") + + |> unique_constraint(:email) + |> foreign_key_constraint(:post_id) + |> assoc_constraint(:post) # ensure post_id exists + |> no_assoc_constraint(:post) # negative (useful for deletions) +end +``` + +```elixir +changeset.valid? +changeset.errors #=> [title: "empty"] + +changeset.changes #=> %{} +changeset.params[:title] + +changeset.required #=> [:title] +changeset.optional #=> [:body] +``` + +### Updating + +```elixir +changeset #(or model) +|> change(title: "New title") +|> change(%{ title: "New title" }) +|> put_change(:title, "New title") +|> force_change(:title, "New title") +|> update_change(:title, &(&1 <> "...")) + +|> delete_change(:title) +|> merge(other_changeset) + +|> add_error(:title, "empty") +``` + +### Getting + +```elixir +get_change(changeset, :title) #=> "hi" (if changed) +get_field(changeset, :title) #=> "hi" (even if unchanged) + +fetch_change(changeset, :title) #=> {:ok, "hi"} | :error +fetch_field(changeset, :title) #=> {:changes | :model, "value"} | :error +``` + +## Ecto + +### Get one + +```elixir +Repo.get(User, id) +Repo.get_by(User, email: "john@hello.com") #=> %User{} | nil + +# also get! get_by! +``` + +### Create/update + +```elixir +changeset |> Repo.update +changeset |> Repo.insert +changeset |> Repo.insert_or_update +``` + +``` +User +|> Ecto.Changeset.change(%{name: "hi"}) +|> Repo.insert +``` + +## Many + +### Queries + +```elixir +from p in Post, + where: p.title == "Hello", + where: [state: "Sweden"], + + limit: 1, + offset: 10, + + order_by: c.name, + order_by: [c.name, c.title], + order_by: [asc: c.name, desc: c.title], + + preload: [:comments], + preload: [comments: {c, likes: l}], + + join: c in assoc(c, :comments), + join: p in Post, on: c.post_id == p.id, + group_by: p, + + select: p, + select: {p.title, p.description}, + select: [p.title, p.description], +``` + +### Get many + +```elixir +Repo.all(User) +``` + +### Update many + +```elixir +Repo.update_all(Post, set: [title: "Title"]) +Repo.update_all(Post, inc: [views: 1]) +``` + +### Chaining `_all` with queries + +```elixir +from(p in Post, where: p.id < 10) +|> Repo.update_all(...) + +from(p in Post, where: p.id < 10) +|> Repo.all() +``` diff --git a/redux.md b/redux.md index ce0d66004..089df85a4 100644 --- a/redux.md +++ b/redux.md @@ -35,20 +35,60 @@ store.dispatch({ type: 'DECREMENT' }); // 10 ```js React.render( - {() => } + , mountNode) ``` ```js class App extends React.Component { - render () { return
{this.props.message}
} + render () { + return +
this.props.onMessageClick('hello')}> + {this.props.message} +
+ } } -function select (state) { +function mapStateToProps (state) { return { message: state.message } } -export default connect(select)(App); +function mapDispatchToProps (dispatch) { + return { + onMessageClick (message) { + dispatch({ type: 'click', message }) + } + } +} + +export default connect(mapStateToProps, mapDispatchToProps)(App); +``` + +## Middleware + +```js +// noop middleware +const logger = store => dispatch => action { dispatch(action) } + +const logger = store => { + // This function runs on createStore(). + // It returns a decorator for dispatch(). + + return dispatch => { + // Runs on createStore(), too. + // It returns a new dispatch() function + + return action => { + // Runs on every dispatch() + } + } +} +``` + +### Applying middleware + +```js +const store = createStore(reducer, {}, applyMiddleware(logger, thunk, ...)) ``` ## Reference