使用关联更改缓存模型

时间:2016-08-07 05:59:29

标签: elixir phoenix-framework ecto

我有一个从Repo获取的模型,并预先加载了一些关联。我在GenServer中维护这个大型结构,但仍然保留对它的更改。我怎样才能坚持这些变化?例如:

query = from e in Empire,
        where: e.user_id == ^user.id,
        preload: [:board, {:board, [{:tiles, [:system]}]}]
empire = Repo.one(query)
#=> %Empire{
  id: 1,
  currency: 1000,
  board: %Board{
    empire_id: 1,
    tiles: [
      %Tile{
        id: 1,
        system: %System{
          id: 1,
          tile_id: 1,
          ore: 10
        }
      },
      %Tile{
        id: 2,
        system: nil
      }
    ]
  }
}

changed_empire = Empire.move_system(empire)
#=> %Empire{
  id: 1,
  currency: 500, #changed
  board: %Board{
    empire_id: 1,
    tiles: [
      %Tile{
        id: 1,
        system: nil #changed
        }
      },
      %Tile{
        id: 2,
        system: %System{
          id: 1,
          tile_id: 2, #changed
          ore: 5 #changed
      }
    ]
  }
}

请注意,系统已从一个平铺移动到另一个平铺,并且Empires货币已更改。更改可以包括添加或删除关联。所有这些都是在没有来自客户端的任何数据的情况下完成的,所以不必担心添加不良数据,因为服务器是如何操纵模型的唯一权威。

我不能依赖正常的

changeset(model, params \\ %{}) do
  model
  |> cast(params, [])
end

因为params将是结构。我可以尝试使用

建议的更改
update_change(struct) do
  struct
  |> change()
  |> validate()
end

但现在我遇到了需要手动发送到自己的update_change函数的关联结构的问题。

有没有更好的方法来实现这一点,而不会增加我似乎遇到的所有额外的复杂性?

1 个答案:

答案 0 :(得分:0)

在评论的帮助下,我能够解决我的问题。我创建了一个小助手模块,它遍历所有数据并在其上调用Map.from_struct。这与常规演员/ 3相结合,给了我预期的结果。

这是帮助者:

defmodule RepoHelper do
  def from_struct(struct) when is_map(struct) do
    from(struct)
  end

  defp from(struct = %{__struct__: _}) do
    Enum.reduce(Map.to_list(struct), struct, fn {key, val}, acc ->
      Map.put(acc, key, from(val))
    end)
    |> Map.from_struct()
  end

  defp from(map) when is_map(map) do
    Enum.reduce Map.to_list(map), map, fn {key, val}, acc ->
      Map.put(acc, key, from(val))
    end
  end

  defp from(list) when is_list(list) do
    Enum.map list, fn item -> from(item) end
  end

  defp from(value), do: value
end