How to write a custom model in elixir / phoenix without being object-oriented?

时间:2015-11-12 12:06:59

标签: elixir phoenix-framework

I am building a little test app with the Phoenix Framework. I cannot use Ecto since my DB is not supported. But I still want the MVC pattern. So I need a way to implement custom models. Since I'm coming from RoR I always catch myself thinking in Object Oriented Programming patterns where you have a class definition and instances with attributes that you can get and set. So that something like this is possible:

@mike = User.new
@mike.name = "Mike Power"
@mike.save

# ect

But this is clearly not how Elixir works.

I'm having a hard time wrapping my head around how to approach this. The implementation should allow me to:

  • Have attributes
  • Store values
  • Manipulate values
  • Validate values
  • Save them to my database

The best way I have come up with so far is using structs. So something like this:

defmodule User do
  defstruct name: "", age: 0

  def sayhello do
    IO.puts "Hi, I am #{name}"
    # This is not working, I just put it here to make a point
  end
end 

tim = %User{}
# -> %User{age: 0, name: ""}
tim = %User{name: "Tim Turbo", age: 25}
# -> %User{age: 25, name: "Tim Turbo"}
tim.name
# -> "Tim Turbo"

# We can also change the values 
tim = %User{name: "Michael"}
# -> %User{age: 25, name: "Michael"}

# It also makes sure that only the values defined in the struct can be set (which is nice)

tom = %User{unknown: "something"}
# ** (CompileError) iex:11: unknown key :unknown for struct User

Using vex I would even be able to implement validations quite easily

defmodule User do
  defstruct username: nil, password: nil, password_confirmation: nil
  use Vex.Struct

  validates :username, presence: true,
                       length: [min: 4],
                       format: ~r/^[[:alpha:]][[:alnum:]]+$/
  validates :password, length: [min: 4],
                       confirmation: true
end

So I am basically asking:

  • Is this the way to go? Or is there a better one?
  • And would I implement the function to actually save the user to the database also in my User module? Where I just enter the parameters

Something like this:

user = %User{params}
User.create user

2 个答案:

答案 0 :(得分:2)

我对恢复OO秘密思维方式的想法:

尝试围绕数据构建逻辑以及您需要对数据执行的操作。不是您需要创建和修改的对象。

关于“Say Hello”的例子:

defmodule UserWelcomer do
  def sayhello(user) do
    IO.puts "Hi, I am #{user.name}"
  end
end 

基本上,您试图通过不同的功能一直实现功能并使用“用户”数据。

对不起,这不是答案。只是评论限制之外的评论。

答案 1 :(得分:1)

这已经超过一年了,但我认为一个好的答案可以帮助那些在谷歌上偶然发现这个问题的人...

  

这是要走的路吗?还是有一个更好的?

你走在正确的轨道上!数据存储在结构中,而不是对象中。查看关于模型验证的Ecto.Changeset docs和关于在结构中存储数据的Ecto.Schema docs。它与您正在做的事情非常相似。

同样重要的是要注意,您可以使用您想要的Ecto组件(例如ChangesetSchema),而不必使用不适用于您的东西应用程序,如Ecto.Repo

  

我是否会在我的用户模块中实现将用户实际保存到数据库的功能?我只需输入参数

在这一点上,这几乎是一个哲学问题。同时使用Ecto作为例子:

  • A"型号"使用Ecto(至少在Phoenix中使用,可能是最常见的情况)实际上只是Ecto.Schema,它具有一些用于变更集和验证的额外功能。
  • 保存或更新(持久性)由使用Ecto.Repo的单独模块处理。

将这些结果组合成更像Ruby on Rails'人们发现ActiveRecord更难以测试,而这种分离有助于使您的模型更容易测试。