Skip to content

Instantly share code, notes, and snippets.

@mattparker
Last active August 29, 2015 13:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mattparker/9177369 to your computer and use it in GitHub Desktop.
Save mattparker/9177369 to your computer and use it in GitHub Desktop.
Including SQL in Specifications
<?php
// This is another response to Mattias Verraes talk on unbreakable domain
// models (slides at http://verraes.net/2013/06/unbreakable-domain-models/)
// Near the end there's a bit where he suggests you implement business logic
// rules initially as code (i.e. an array filter) and secondly as sql.
// You can then test that these are consistent (though the tests in the talk
// don't show correctness, there needs to be a test against a known good result
// too). But with that, it seems like a nice way to have testable SQL.
// A problem for me with the example (which is obviously shortened for the slide example)
// is that it shows hard-coded SQL strings. SQL ought to be in one place. But also,
// I want these things to be chainable (see https://gist.github.com/mattparker/9177040).
// So whatever's returned from asSql wants to be not a string of SQL.
// There's some ideas here, but not quite sure about them...
final class CustomerWith3OrdersIsPremium implements CustomerSpecification {
/**
* @return Select
*/
public function asSql (Select $select) {
// I still don't think this is quite right.
// I don't really like that SQL string here.
$select->join(new CustomerTable)
->where('summary_order_count >= 3');
return $select;
}
}
// or perhaps
final class CustomerWith3OrdersIsPremium implements CustomerSpecification {
/**
* @return Select
*/
public function asSql (Select $select) {
// This still isn't quite right.
// It's better in that the SQL being written is now in
// some kind of Select object that belongs to the table.
// But instantiating the table here doesn't feel quite right.
$customers = new CustomerTable;
$customers->select()->orderCount(Select::GREATER_OR_EQUAL, 3);
$select->where($customers);
return $select;
}
}
// or perhaps
final class CustomerWith3OrdersIsPremium implements CustomerSpecification {
/**
* @return QueryBuilder
*/
public function asSql (QueryBuilder $builder) {
// The Select object returned from this first line
// knows about how to write SQL queries, join tables, etc.
$customerSelect = $builder->getSelect(QueryBuilder::CUSTOMER);
$customerSelect->hasOrderCount(Select::GREATER_OR_EQUAL, 3);
// The QueryBuilder provides access to these Select objects,
// and aggregates them and sends them off for execution
$builder->where($customerSelect);
return $builder;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment