Skip to content

Instantly share code, notes, and snippets.

@ajshort
Created April 17, 2011 07:54
Show Gist options
  • Select an option

  • Save ajshort/923835 to your computer and use it in GitHub Desktop.

Select an option

Save ajshort/923835 to your computer and use it in GitHub Desktop.
diff --git a/core/model/DataList.php b/core/model/DataList.php
index c94250a..4305923 100644
--- a/core/model/DataList.php
+++ b/core/model/DataList.php
@@ -58,7 +58,16 @@ class DataList extends DataObjectSet {
public function sql() {
return $this->dataQuery->query()->sql();
}
-
+
+ /**
+ * @see DataQuery::filter()
+ * @returns DataList
+ */
+ public function filter($field, $value = null) {
+ $this->dataQuery->filter($field, $value);
+ return $this;
+ }
+
/**
* Add a WHERE clause to the query.
*
diff --git a/core/model/DataQuery.php b/core/model/DataQuery.php
index 3494ab2..85cd18a 100644
--- a/core/model/DataQuery.php
+++ b/core/model/DataQuery.php
@@ -6,6 +6,23 @@
* Used extensively by DataList.
*/
class DataQuery {
+
+ const DEFAULT_FILTER = 'equals';
+
+ public static $filters = array(
+ 'equals' => array('DefaultDataFilters', 'equals'),
+ 'in' => array('DefaultDataFilters', 'in'),
+ 'like' => array('DefaultDataFilters', 'like'),
+ 'startswith' => array('DefaultDataFilters', 'startswith'),
+ 'endswith' => array('DefaultDataFilters', 'endswith'),
+ 'contains' => array('DefaultDataFilters', 'contains'),
+ 'gt' => array('DefaultDataFilters', 'gt'),
+ 'gte' => array('DefaultDataFilters', 'gte'),
+ 'lt' => array('DefaultDataFilters', 'lt'),
+ 'lte' => array('DefaultDataFilters', 'lte'),
+ 'between' => array('DefaultDataFilters', 'between'),
+ );
+
protected $dataClass;
protected $query;
@@ -76,8 +93,8 @@ class DataQuery {
// Error checking
if(!$tableClasses) {
- if(!ManifestBuilder::has_been_included()) {
- user_error("DataObjects have been requested before the manifest is loaded. Please ensure you are not querying the database in _config.php.", E_USER_ERROR);
+ if(!DB::getConn()) {
+ user_error("DataObjects have been requested before the database is connected. Please ensure you are not querying the database in _config.php.", E_USER_ERROR);
} else {
user_error("DataObject::buildSQL: Can't find data classes (classes linked to tables) for $this->dataClass. Please ensure you run dev/build after creating a new DataObject.", E_USER_ERROR);
}
@@ -252,7 +269,109 @@ class DataQuery {
}
}
}
-
+
+ /**
+ *
+ *
+ * @param string|array $field
+ * @param mixed $value
+ * @return DataQuery
+ */
+ public function filter($field, $value = null) {
+ if (is_array($field)) {
+ foreach ($field as $field => $value) {
+ $this->filter($field, $value);
+ }
+
+ return $this;
+ }
+
+ $sqlFilters = array();
+ $negated = false;
+ $notted = false;
+
+ // Split the field up into a number of fields, and an optional negation
+ // followed by a filter then a not.
+ if (!$colPos = strpos($field, ':')) {
+ $fields = explode(',', $field);
+ $filter = self::DEFAULT_FILTER;
+ } else {
+ $fields = explode(',', substr($field, 0, $colPos));
+ $filter = substr($field, $colPos + 1);
+ $filter = explode(':', $filter);
+
+ foreach ($filter as $f) switch ($f) {
+ case 'not': $notted = !$notted; break;
+ case 'negate': $negated = !$negated; break;
+ default: $filter = strtolower($f); break;
+ }
+
+ if (!$filter) {
+ $filter = self::DEFAULT_FILTER;
+ }
+ }
+
+ // If we're only filtering on a single value, then collapse down a not
+ // wrapper.
+ if (!is_array($value) && $notted) {
+ $notted = false;
+ $negated = !$negated;
+ }
+
+ $value = $this->filterEscape($value);
+
+ if (!array_key_exists($filter, self::$filters)) {
+ throw new Exception("The filter '$filter' does not exist.");
+ }
+
+ foreach ($fields as $field) {
+ $table = $this->applyRelation($field);
+ $field = strtok($field, '.');
+
+ if ($field == 'ID') {
+ $table = ClassInfo::baseDataClass($table);
+ } else {
+ while (!singleton($table)->hasOwnTableDatabaseField($field)) {
+ if (($table = get_parent_class($table)) == 'DataObject') {
+ throw new Exception("Could not find field '$field'.");
+ }
+ }
+ }
+
+ $field = "\"$table\".\"$field\"";
+ $clause = call_user_func(self::$filters[$filter], $field, $value, $negated, $this);
+
+ if ($notted) {
+ $clause = "NOT($clause)";
+ }
+
+ $sqlFilters[] = $clause;
+ }
+
+ if (count($sqlFilters) > 1) {
+ $this->where('(' . implode(') OR (', $sqlFilters) . ')');
+ } else {
+ $this->where($sqlFilters[0]);
+ }
+
+ return $this;
+ }
+
+ protected function filterEscape($value) {
+ if (is_array($value)) {
+ return array_map(array($this, 'filterEscape'), $value);
+ } else {
+ switch (true) {
+ case is_int($value):
+ return (string) $value;
+ case is_bool($value):
+ return $value ? '1' : '0';
+ default:
+ return "'" . Convert::raw2sql($value) . "'";
+ }
+ }
+ }
+
/**
* Set the HAVING clause of this query
*/
diff --git a/core/model/DefaultDataFilters.php b/core/model/DefaultDataFilters.php
new file mode 100644
index 0000000..d1fa857
--- /dev/null
+++ b/core/model/DefaultDataFilters.php
@@ -0,0 +1,113 @@
+<?php
+/**
+ *
+ *
+ * @package sapphire
+ * @subpackage model
+ */
+class DefaultDataFilters {
+
+ public static function equals($field, $value, $negated) {
+ if (is_array($value)) {
+ return self::in($field, $value, $negated);
+ } else {
+ return !$negated ? "$field = $value" : "$field <> $value";
+ }
+ }
+
+ public static function in($field, $values, $negated) {
+ $value = '(' . implode(', ', $values) . ')';
+ $operator = !$negated ? 'IN' : 'NOT IN';
+
+ return "$field $operator $value";
+ }
+
+ public static function like($field, $value, $negated) {
+ return self::comparison($field, !$negated ? 'LIKE' : 'NOT LIKE', $value);
+ }
+
+ public static function startswith($field, $value, $negated) {
+ if (is_array($value)) {
+ foreach ($value as $k => $_value) {
+ $value[$k] = substr($_value, 0, -1) . "%'";
+ }
+ } else {
+ $value = substr($value, 0, -1) . "%'";
+ }
+
+ return self::like($field, $value, $negated);
+ }
+
+ public static function endswith($a, $b, $negated) {
+ if (is_array($value)) {
+ foreach ($value as $k => $_value) {
+ $value[$k] = "'%" . substr($_value, 1);
+ }
+ } else {
+ $value = "'%" . substr($value, 1);
+ }
+
+ return self::like($field, $value, $negated);
+ }
+
+ public static function contains($a, $b, $negated) {
+ if (is_array($value)) {
+ foreach ($value as $k => $_value) {
+ $value[$k] = "'%" . substr($_value, 1, -1) . "%'";
+ }
+ } else {
+ $value = "'%" . substr($value, 1, -1) . "%'";
+ }
+
+ return self::like($field, $value, $negated);
+ }
+
+ public static function gt($field, $value, $negated) {
+ return self::comparison($field, !$negated ? '>' : '<=', $value);
+ }
+
+ public static function gte($field, $value, $negated) {
+ return self::comparison($field, !$negated ? '>=' : '<', $value);
+ }
+
+ public static function lt($field, $value, $negated) {
+ return self::comparison($field, !$negated ? '<' : '>=', $value);
+ }
+
+ public static function lte($field, $value, $negated) {
+ return self::comparison($field, !$negated ? '<=' : '>', $value);
+ }
+
+ public static function between($field, $values, $negated) {
+ if (!is_array($values)) {
+ throw new Exception('The between filter requires an array of values.');
+ }
+
+ if (is_array($values[0])) {
+ $value = array();
+
+ foreach ($values as $val) {
+ $value[] = "{$val[0]} AND {$val[1]}";
+ }
+ } else {
+ $value = "{$values[0]} AND {$values[1]}";
+ }
+
+ return self::comparison($field, !$negated ? 'BETWEEN' : 'NOT BETWEEN', $value);
+ }
+
+ protected static function comparison($field, $operator, $values) {
+ if (is_array($values)) {
+ $ors = array();
+
+ foreach ($values as $value) {
+ $ors[] = "$field $operator $value";
+ }
+
+ return implode(' OR ', $ors);
+ } else {
+ return "$field $operator $values";
+ }
+ }
+
+}
\ No newline at end of file
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment