Ir para conteúdo
Lordfire

Object SQL

Recommended Posts

Lordfire    110
Lordfire

Como funciona?

Esta lib funciona como um façade para trabalhar com queries de um jeito mais "programador". A seguinte query, por exemplo:

(...)
tmp = db.getResult("SELECT `id`, `begin`, `end`, `payment` FROM `guild_wars` WHERE `guild_id` = " .. guild .. " AND `enemy_id` = " .. enemy .. " AND `status` = 0")
if(tmp:getID() == -1) then
(...)

Se transforma no seguinte código:

(...)
tmp = query:select('id', 'begin', 'end', 'payment'):from('guild_wars'):where('guild_id', guild):where('enemy_id', enemy):where('status', 0):execute()
if (tmp.id == -1) then
(...)

Quem já está acostumado com frameworks e outras facilidades de transformar queries em objetos vai sentir diferença na facilidade para escrever queries.

 

 

Em outro exemplo, executando um update:

db.executeQuery("UPDATE `guilds` SET `balance` = `balance` - " .. tmp:getDataInt("payment") .. " WHERE `id` = " .. guild)

Com a lib, se torna:

query:update('guilds'):set('balance', dec(tmp.payment)):where('id', guild):execute()

Esta é outra facilidade, adicionei os modificadores inc e dec para incrementar ou decrementar, respectivamente, os campos em questão, e levam um parâmetro opcional que indica a quantidade a ser alterada.

Nada o impede, também, de construir a query em vários passos:

query:update('guilds')
local payment = tmp.payment
query:set('balance', dec(payment))
local id = getPlayerGuild(cid) --não sei se essa função existe, é apenas um exemplo
query:where('id', id)

E também, você pode saber se sua query foi executada ou não:

if query:execute() then
(...)

 

 

Veja outros exemplos de código:

query:insert_into('players'):values({name = 'Lordfire\'Rook', account_id = 0, level = 39, vocation = 0}):execute()
query:update('players'):set({vocation = 4, access = inc(), level = inc(5)}):where('name', 'Lordfire\'Rook'):execute()
query:select():from('players', 'accounts'):where('players.level', '>=', 39):where('players.access', 1):execute()
query:delete_from('players'):where('level', '>=', 30):where('access', 0):execute()

Repare que, para inserir, você usa o método values e envia uma tabela com as informações. A query automaticamente ordena os campos a serem preenchidos com seus respectivos dados.

Você pode informar múltiplas tabelas no método from, mas deve indicar os campos com o nome da tabela para evitar redundância (por exemplo: level -> players.level)

O método where também leva um parâmetro opcional, no meio, caso você queira usar alguma comparação que seja diferente de =.

 

 

Estes códigos geram as seguintes queries, para efeito de consulta, que você pode pegar com o método query(), caso queira usar junto com outra query:

INSERT INTO `players`(`account_id`,`vocation`,`name`,`level`)VALUES(0,0,"Lordfire'Rook",39);
UPDATE players SET `level`=`level`+5,`vocation`=4,`access`=`access`+1 WHERE `name`="Lordfire'Rook";
SELECT * FROM `players`,`accounts` WHERE `players`.`level`>=39 AND `players`.`access`=1;
DELETE FROM players WHERE `level`>=30 AND `access`=0;

 

 

Lib:

Pastebin: http://pastebin.com/2bjuFBDD

Código:

function mod(key, _type, value)
   if value == nil then
       value = 1
   end
   return key .. _type .. value
end


function inc(value)
   return function(key)
       return mod(key, [[+]], value)
   end
end


function dec(value)
   return function(key)
       return mod(key, [[-]], value)
   end
end


Query = {}


function Query:new()
   return setmetatable({type = [[]], columns = [[]], tables = [[]], conditions = {}}, {__index = self})
end


-- C
function Query:insert_into(_table)
   self.type, self.columns, self.tables, self.conditions = [[insert]], [[]], [[`]] .. _table .. [[`]], {}
   return self
end


function Query:values(values)
   local _columns, _values = {}, {}
   for k, v in pairs(values) do
       table.insert(_columns, [[`]] .. k .. [[`]])
       if type(v) == "string" then
           v = [["]] .. v:gsub([[\"]], [[\"]]) .. [["]]
       end
       table.insert(_values, v)
   end
   self.columns, self.conditions = [[(]] .. table.concat(_columns, [[,]]) .. [[)]], _values
   return self
end


-- R
function Query:select(...)
   self.type, self.columns, self.tables, self.conditions = [[select]], [
[*]], [[]], {}
   local columns = {...}
   if #columns > 0 then
       for i, c in ipairs(columns) do
           columns[i] = [[`]] .. c .. [[`]]
       end
       self.columns = table.concat(columns, [[,]])
   end
   return self
end


-- U
function Query:update(_table)
   self.type, self.columns, self.tables, self.conditions = [[update]], [[]], _table, {}
   return self
end


function Query:set(columns)
   local _columns = {}
   for k, v in pairs(columns) do
       k = [[`]] .. k:gsub([[\.]], [[`.`]]) .. [[`]]
       if type(v) == "string" then
           v = [["]] .. v:gsub([[\"]], [[\"]]) .. [["]]
       elseif type(v) == "function" then
           v = v(k)
       end
       table.insert(_columns, k .. [[=]] .. v)
   end
   self.columns = table.concat(_columns, [[,]])
   return self
end


-- D
function Query:delete_from(_table)
   self.type, self.columns, self.tables, self.conditions = [[delete]], [[]], _table, {}
   return self
end


-- RU
function Query:where(...)
   local params = {...}
   local column, comparison, value = [[`]] .. params[1]:gsub([[%.]], [[`.`]]) .. [[`]], [[=]], nil
   if #params == 2 then
       value = params[2]
   elseif #params == 3 then
       if params[2]:find("[<>]=") then
           comparison, value = params[2], params[3]
       else
           comparison = [[ ]] .. params[2] .. [[ ]]
       end
   else
       return false
   end
   if type(value) == "string" then
       value = [["]] .. value:gsub([[\"]], [[\"]]) .. [["]]
   end
   table.insert(self.conditions, column .. comparison .. value)
   return self
end


-- RD
function Query:from(...)
   local tables = {...}
   for i, t in ipairs(tables) do
       tables[i] = [[`]] .. t .. [[`]]
   end
   self.tables = table.concat(tables, [[,]])
   return self
end


function Query:query()
   local query = [[]], {}
   if self.type == [[insert]] then
       query = [[iNSERT INTO ]] .. self.tables .. self.columns .. [[VALUES(]] .. table.concat(self.conditions, [[,]]) .. [[)]]
   elseif self.type == [[select]] then
       local where = [[]]
       if self.conditions ~= [[]] then
           where = [[ WHERE ]] .. table.concat(self.conditions, [[ AND ]])
       end
       query = [[sELECT ]] .. self.columns .. [[ FROM ]] .. self.tables .. where
   elseif self.type == [[update]] then
       local where = [[]]
       if self.conditions ~= [[]] then
           where = table.concat({[[ WHERE]], table.concat(self.conditions, [[ AND ]])}, [[ ]])
       end
       query = [[uPDATE ]] .. self.tables .. [[ SET ]] .. self.columns .. where
   elseif self.type == [[delete]] then
       local where = [[]]
       if self.conditions ~= [[]] then
           where = table.concat({[[ WHERE]], table.concat(self.conditions, [[ AND ]])}, [[ ]])
       end
       query = [[DELETE FROM ]] .. self.tables .. where
   end
   return query .. [[;]]
end


function Query:execute()
   local query, result = self:query()
   if self.type == [[insert]] then
       local result = {}
       query = db.getResult(query)
       for k, v in pairs(query.val) do
           result[k] = v
       end
       return result
   end
   return db.executeQuery(query)
end


query = Query:new()

 

 

Avisos:

Isso não é uma biblioteca completa de MVC ou qualquer coisa parecida. É apenas um "wrapper" para construir queries de um jeito mais dinâmico, organizado e sem se preocupar com concatenação e afins. Não há sanitização de strings, testes para verificar se as funções existem ou coisa do tipo, portanto, tome cuidado.

 

 

A biblioteca ainda está em fase de testes, não cheguei a usar ela num servidor com muitos players online. Se alguém puder fazer isso, ficarei agradecido. Posso transformar as queries para a sintaxe da biblioteca, se necessário.

Compartilhar este post


Link para o post
Luck Oake    16
Luck Oake

Deveras sexy essa lib. Adorei e provavelmente vamos aderi-la num futuro projeto de TFS reformado.

 

Só falta agora um sistema de REP mais bonitinho pra eu te dar REP+.

Compartilhar este post


Link para o post
BrunoCamara    1
BrunoCamara

Muito bom hehe

Compartilhar este post


Link para o post
Conde2    0
Conde2

conde2

Editado por Conde2

Compartilhar este post


Link para o post
gpedro    47
gpedro

Curti!

Me lembra bastante Active Record para PHP.

Compartilhar este post


Link para o post
Visitante
Este tópico está impedido de receber novos posts.
  • Quem Está Navegando   0 membros estão online

    Nenhum usuário registrado visualizando esta página.

×