ModelBase.lua 6.75 KB
local ModelBase = class("ModelBase")
ModelBase.key = "key"
ModelBase.schema = {
    key = {"string"}
}
ModelBase.fields = {}   -- 数据库字段 field, update 是否立即更新

local string_format = string.format
local table_insert = table.insert
local table_unpack = table.unpack
local assert = assert
local next = next
local ipairs = ipairs
local pairs = pairs
local tostring = tostring
local tonumber = tonumber
local redisproxy = redisproxy

local function filterProperties(properties, filter)
    for i, field in ipairs(filter) do
        properties[field] = nil
    end
end

function ModelBase:ctor(properties)
    self.isModelBase_ = true
    -- self.dirtyFields = {}

    if type(properties) ~= "table" then properties = {} end
    self:setProperties(properties, true)  --缺少的域将设置默认值
end

--[[--

返回对象的 ID 值。

**Returns:**

-   ID 值

]]
function ModelBase:getKey()
    local id = self[self.class.key .. "_"]
    assert(id ~= nil, string_format("%s [%s:getKey()] Invalid key", tostring(self), self.class.__cname))
    return id
end

function ModelBase:load(properties)
    if not self:isValidKey() then
        print(string_format("%s [%s:id] should be set before load", tostring(self), self.class.__cname))
        return false
    end

    if not properties then
        properties = redisproxy:hgetall(self:getKey())
        properties = table.arrayToMap(properties)
    end
    if not next(properties) then return false end

    self:setProperties(properties, true)

    self:onLoad()

    return true
end

--创建model对应的redis数据, 必须已经设置了ID
function ModelBase:create()
    if not self:isValidKey() then
        print(string_format("%s [%s:key] should be set before create", tostring(self), self.class.__cname))
        return nil
    end

    --将所有的域都置为dirty, 存储到redis
    -- for fieldName, update in pairs(self.class.fields) do
    --     self.dirtyFields[fieldName] = true
    -- end
    self:save()
    self:onCreate()

    return self
end

function ModelBase:save()
    local redisProperties = self:getProperties()

    local params = {}
    for fieldName, value in pairs(redisProperties) do
        -- if self.dirtyFields[fieldName] then
            local propname = fieldName .. "_"
            table_insert(params, fieldName)
            table_insert(params, self[propname])
        -- end
    end
    if next(params) then
        redisproxy:hmset(self:getKey(), table_unpack(params))
    end
end

--[[--

确定对象是否设置了有效的 key。

]]
function ModelBase:isValidKey()
    local propname = self.class.key .. "_"
    local key = self[propname]
    return type(key) == "string" and key ~= ""
end

--[[--

修改对象的属性。
NOTE: 如果properties缺少schema中的域, 将用默认值来填充

**Parameters:**

-   properties: 包含属性值的数组

]]
function ModelBase:setProperties(properties, notWrite)
    assert(type(properties) == "table", "Invalid properties")
           -- string_format("%s [%s:setProperties()] Invalid properties", tostring(self), self.class.__cname))

    local params = {}
    for field, schema in pairs(self.class.schema) do
        local typ, def = table_unpack(schema)
        local propname = field .. "_"

        local val = properties[field] or def
        if val ~= nil then
            if typ == "number" then val = tonumber(val) end
            assert(type(val) == typ,
               string_format("%s [%s:setProperties()] Type mismatch, %s expected %s, actual is %s",
                                 tostring(self), self.class.__cname, field, typ, type(val)))
            self[propname] = val
            table_insert(params, field)
            table_insert(params, val)
        end
    end
    if not notWrite and next(params) then
        redisproxy:hmset(self:getKey(), table_unpack(params))
    end
end

--[[--

取得对象的属性值。

**Parameters:**

-   fields: 要取得哪些属性的值,如果未指定该参数,则返回 fields 中设定的属性
-   filter: 要从结果中过滤掉哪些属性,如果未指定则不过滤

**Returns:**

-   包含属性值的数组

]]
function ModelBase:getProperties(fields, filter)
    local schema = self.class.schema
    if type(fields) ~= "table" then fields = table.keys(self.class.fields) end

    local properties = {}
    for i, field in ipairs(fields) do
        local propname = field .. "_"
        local typ = schema[field][1]
        local val = self[propname]
        assert(type(val) == typ,
               string_format("%s [%s:getProperties()] Type mismatch, %s expected %s, actual is %s",
                                 tostring(self), self.class.__cname, field, typ, type(val)))
        properties[field] = val
    end

    if type(filter) == "table" then
        filterProperties(properties, filter)
    end

    return properties
end

function ModelBase:getProperty(property)
    if type(property) ~= "string" then return nil end
    if not self.class.schema[property] then return nil end
    return self:getProperties({property})[property]
end

function ModelBase:setProperty(property, value, update)
    if not self.class.schema[property] then
        print(string_format("%s [%s:setProperty()] Invalid property : %s",
            tostring(self), self.class.__cname, property))
        return
    end

    local typ, def = table_unpack(self.class.schema[property])
    local propname = property .. "_"

    if typ == "number" then value = tonumber(value) end
    assert(type(value) == typ,
       string_format("%s [%s:setProperties()] Type mismatch, %s expected %s, actual is %s",
         tostring(self), self.class.__cname, property, typ, type(value)))
    self[propname] = value

    if self.class.fields[property] or update then
        redisproxy:hset(self:getKey(), property, value)
    else
        -- self.dirtyFields[property] = true  -- record the fields been modified
    end
end

function ModelBase:incrProperty(property, value, update)
    if not self.class.schema[property] then
        print(string_format("%s [%s:setProperty()] Invalid property : %s",
            tostring(self), self.class.__cname, property))
        return
    end

    local typ, def = table_unpack(self.class.schema[property])
    local propname = property .. "_"

    if typ == "number" then value = tonumber(value) end
    assert(type(value) == typ,
       string_format("%s [%s:setProperties()] Type mismatch, %s expected %s, actual is %s",
         tostring(self), self.class.__cname, property, typ, type(value)))
    self[propname] = self[propname] + value

    if self.class.fields[property] or update then
        return redisproxy:hincrby(self:getKey(), property, value)
    else
        -- self.dirtyFields[property] = true  -- record the fields been modified
    end
end

function ModelBase:onLoad()
end

function ModelBase:onCreate()
end

return ModelBase