Thursday, August 12, 2010

Refactoring Voltron query DSL

This is an area of the framework I have not had a need to look at in a while and from this latest inspection it was blatantly not very well thought out. Admitting your own fallibility, I think, is probably the first step to being able to refactor your own code. Previously I had decided I really wanted to avoid seeing stuff like:

$UserModel->getWhere(array('type' => 'and', value' => array(
array('type' => '=', 'field' => 'name', 'value' => 'shaun'),
array('type' => 'or', 'value' => array(
array('type' => 'between', 'field' => 'age', 'value' => array(69, 100)),
array('type' => 'like', 'field' => 'l_name', 'value' => '%name%'))))));

So I went with a half baked dsl

Q::is('name', 'shaun'),
Q::is('age', Q::between(array(69, 100))),
Q::is('l_name', Q::like('%name%')))));

On first inspection that is not too bad but it rapidly gets out of control if you need to actually do something more in depth (nested or statements etc.). Not only that but all of the :: is pretty ugly as is the fact I can't use words like 'and', 'or' '=' etc. as method names. I also had this idea that "is" should be used for all operators. I have now abandoned that in the name of consistency and to reduce the complexity of the code which actually generates the sql.

Taking a prefix notation approach and refactoring W (for where instead of Q for query, leaving room for say F for the from stuff for defining joins and S for select) to merely be a global function which returns arrays like in the first example:

W('=', 'name', 'shaun'),
W('between', 'age', 69, 100),
W('like', 'l_name', '%name%'))));

I pine for quasiquote:

(= name shaun)
(between age 69 100)
(like l_name %name%))));

No comments: