Skip to content

EctoConditionals implements a flexibly functional find_or_create and upsert behavior for Ecto models.

License

Notifications You must be signed in to change notification settings

codeanpeace/ecto_conditionals

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

EctoConditionals

EctoConditionals implements a flexibly functional find_or_create and upsert behavior for Ecto models.

Installation

The package can be installed by adding ecto_conditionals in mix.exs:

def deps do
  [{:ecto_conditionals, "~> 0.1.0"}]
end

This package is available in Hex here,

Basic Usage

First specify the repo you're going to use:

use EctoConditionals, repo: MyApp.Repo

Then, pipe or pass the record struct to find_or_create/1 or upsert/1. These functions assume finding by id to determine whether to create or upsert. If an id field is present in the record struct, the id field will be used as the unique selector to determine whether to find, insert, or update. If not present, these functions will insert a new record.

%User{id: 1, name: "Flamel"} |> find_or_create
#=> {:ok, %User{id: 1, name: "Flamel"}}

%User{name: "Dumbledore"} |> upsert
#=> {:ok, %User{id: 2, name: "Dumbledore"}}

You can also specify selectors by instead using find_or_create_by/2 or upsert_by/2 and passing a selector or list of selectors as the second argument.

%User{name: "Slughorn"} |> find_or_create_by(:name)
#=> {:ok, %User{id: 3, name: "Slughorn"}}

%User{first_name: "Harry", last_name: "Potter"} |> upsert_by(:last_name)
#=> {:ok, %User{id: 4, first_name: "Harry", last_name: "Potter"}}

Implementation Note

find_or_create_by/2 is a thin wrapper piping through find_by/2 and then or_create/1 upsert_by/2 is a thin wrapper piping through find_by/2 and then update_or_insert/1. find_or_create/1 is a thin wrapper piping through find/1 and then or_create/1 and upsert/1 is a thin wrapper piping through find/1 and Ecto.Repo's insert_or_update/1. find/1 is also just a thin wrapper around find_by/1 that assumes :id is the selector. It's functions all the way down!

%User{first_name: "Harry", last_name: "Potter"} |> find_by([:first_name, :last_name])
#=> {:found, %User{id: 4, first_name: "Harry", last_name: "Potter"}}

%User{name: "Buckbeak"} |> find_by(:name)
#=> {:not_found, %User{name: "Buckbeak"}}

# the following is equivalent
%User{id: 1} |> find_by(:id)
%User{id: 1} |> find

Pro Tips

Individual functions are very flexible when used independently. For example, try pairing Elixir's with construct with find_by/2, which returns pattern match friendly tagged tuples such as {:found, record_struct} or {:not_found, record_struct}. These conditional helper functions also play well with Ecto.Multi.

Common Gotchas

An Ecto.MultipleResultsError means your selector or list of selectors does not uniquely identify a record aka it is not sufficiently specific.

About

EctoConditionals implements a flexibly functional find_or_create and upsert behavior for Ecto models.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages