2017-02-13 10 views
2

2つの値のうち1つが存在するか両方の値が存在するかを確認するORロジックの条件付き検証を行う方法を教えてください。Ectoでの条件付き検証 - 2つのフィールドのうちの1つが必要です

ので、例えば、私はemailまたはmobileフィールドが満たされていることを確認するチェックしたい場合は...私はの少なくとも1を検証するためにvalidate_required_inclusionfieldsにリストを渡すことができるようにしたいですリスト内のフィールドはnullではありません。

def changeset(struct, params \\ %{}) do 
    struct 
    |> cast(params, [:email, :first_name, :last_name, :password_hash, :role, :birthdate, :address1, :address2, :city, :state, :zip, :status, :mobile, :card, :sms_code, :status]) 
    |> validate_required_inclusion([:email , :mobile]) 
end 


def validate_required_inclusion(changeset, fields, options \\ []) do 

end 

どのようにこの条件付きOR検証を行うことができますか?

答えて

5

ここは簡単な方法です。

def validate_required_inclusion(changeset, fields) do 
    if Enum.any?(fields, &present?(changeset, &1)) do 
    changeset 
    else 
    # Add the error to the first field only since Ecto requires a field name for each error. 
    add_error(changeset, hd(fields), "One of these fields must be present: #{inspect fields}") 
    end 
end 

def present?(changeset, field) do 
    value = get_field(changeset, field) 
    value && value != "" 
end 

Postモデルを使用したテストと|> validate_required_inclusion([:title , :content]):あなたはそれがより良いエラーメッセージをサポートするためにカスタマイズすることができ、それは上にドロップすると動作しませんでしたが、それは他よりも短かったよう

iex(1)> Post.changeset(%Post{}, %{}) 
#Ecto.Changeset<action: nil, changes: %{}, 
errors: [title: {"One of these fields must be present: [:title, :content]", 
    []}], data: #MyApp.Post<>, valid?: false> 
iex(2)> Post.changeset(%Post{}, %{title: ""}) 
#Ecto.Changeset<action: nil, changes: %{}, 
errors: [title: {"One of these fields must be present: [:title, :content]", 
    []}], data: #MyApp.Post<>, valid?: false> 
iex(3)> Post.changeset(%Post{}, %{title: "foo"}) 
#Ecto.Changeset<action: nil, changes: %{title: "foo"}, errors: [], 
data: #MyApp.Post<>, valid?: true> 
iex(4)> Post.changeset(%Post{}, %{content: ""}) 
#Ecto.Changeset<action: nil, changes: %{}, 
errors: [title: {"One of these fields must be present: [:title, :content]", 
    []}], data: #MyApp.Post<>, valid?: false> 
iex(5)> Post.changeset(%Post{}, %{content: "foo"}) 
#Ecto.Changeset<action: nil, changes: %{content: "foo"}, errors: [], 
data: #MyApp.Post<>, valid?: true> 
1

方法について:

def validate_required_inclusion(changeset, fields, options \\ []) do 
    if Enum.any?(fields, fn(field) -> get_field(changeset, field) end), 
     do: changeset, 
     else: add_error(changeset, hd(fields), "One of these fields must be present: #{inspect fields}") 
    end 

get_fieldは(キャスト)に変更し、非変更の両方、あなたの変更セットで受け入れフィールドを与え、Enum.any?フィールドの少なくとも1つが確実にそこにあることを保証します。

+0

私はこれが好き...私はそれと遊ぶ必要があります。ありがとうございました! – DogEatDog

+1

フィールドが既にモデルにあり、変更されていない場合、これは機能しません。 'content'があっても' changes'が空であるため、Post.changeset(%Post {content: "foo"}、%{}) 'は失敗します。 – Dogbert

+0

ありがとう@Dogbert、良いキャッチ。私は答えを更新しましたが、空のフィールドはチェックしません。 – kmptkp

関連する問題