diff --git a/include/class.dynamic_forms.php b/include/class.dynamic_forms.php index ccbbfd2205a64ce64b526e6db37fddcf34677439..728846716e40e49336f8f33f6e4b2b616ffe033e 100644 --- a/include/class.dynamic_forms.php +++ b/include/class.dynamic_forms.php @@ -839,6 +839,10 @@ class DynamicFormField extends VerySimpleModel { && $this->hasFlag(self::FLAG_CLIENT_VIEW); } + function addToQuery($query, $name=false) { + return $query->values($name ?: $this->get('name')); + } + /** * Used when updating the form via the admin panel. This represents * validation on the form field template, not data entered into a form @@ -929,7 +933,7 @@ class DynamicFormEntry extends VerySimpleModel { 'constraint' => array('form_id' => 'DynamicForm.id'), ), 'answers' => array( - 'reverse' => 'DynamicFormEntryAnswer.entry' + 'reverse' => 'DynamicFormEntryAnswer.entry', ), ), ); diff --git a/include/class.organization.php b/include/class.organization.php index 2a95713671c7e2af9359dff5a6d31c3f599d4b28..eb572ed11e36a23a912dbbfdaa185aaca29fe887 100644 --- a/include/class.organization.php +++ b/include/class.organization.php @@ -29,7 +29,14 @@ class OrganizationModel extends VerySimpleModel { 'cdata' => array( 'constraint' => array('id' => 'OrganizationCdata.org_id'), ), - ) + 'entries' => array( + 'constraint' => array( + 'id' => 'DynamicFormEntry.object_id', + "'O'" => 'DynamicFormEntry.object_type', + ), + 'list' => true, + ), + ), ); const COLLAB_ALL_MEMBERS = 0x0001; diff --git a/include/class.orm.php b/include/class.orm.php index f67611ae98dd5af9b41ab53686d84e5534c1098d..a5b335dc6be42b0b53217a7d8739a34a81c2bf59 100644 --- a/include/class.orm.php +++ b/include/class.orm.php @@ -206,6 +206,21 @@ class ModelMeta implements ArrayAccess { $this->processJoin($this->meta['joins'][$name]); } + /** + * Fetch ModelMeta instance by following a join path from this model + */ + function getByPath($path) { + if (is_string($path)) + $path = explode('__', $path); + $root = $this; + foreach ($path as $P) { + if (!($root = $root['joins'][$P]['fkey'][0])) + break; + $root = $root::getMeta(); + } + return $root; + } + function offsetGet($field) { return $this->meta[$field]; } @@ -229,17 +244,6 @@ class ModelMeta implements ArrayAccess { return $this->fields; } - function getByPath($path) { - if (is_string($path)) - $path = explode('__', $path); - $root = $this; - foreach ($path as $P) { - list($root, ) = $root['joins'][$P]['fkey']; - $root = $root::getMeta(); - } - return $root; - } - /** * Create a new instance of the model, optionally hydrating it with the * given hash table. The constructor is not called, which leaves the diff --git a/include/class.queue.php b/include/class.queue.php index d028eef95a00b9544786b39573db3be5da379396..4206ca1dc4b9f45e0cc5537adc06f078b5574de5 100644 --- a/include/class.queue.php +++ b/include/class.queue.php @@ -171,6 +171,7 @@ class CustomQueue extends SavedSearch { if (isset($quick_filter) && ($qf = $this->getQuickFilterField($quick_filter)) ) { + $this->filter = @QueueColumn::getOrmPath($this->filter, $query); $query = $qf->applyQuickFilter($query, $quick_filter, $this->filter); } @@ -740,18 +741,29 @@ extends VerySimpleModel { } } + function addToQuery($query, $field, $path) { + if (preg_match('/__answers!\d+__/', $path)) { + // Ensure that only one record is returned from the join through + // the entry and answers joins + return $query->annotate(array( + $path => SqlAggregate::MAX($path) + )); + } + return $field->addToQuery($query, $path); + } + function mangleQuery($query) { // Basic data $fields = SavedSearch::getSearchableFields($this->getQueue()->getRoot()); if ($primary = $fields[$this->primary]) { list(,$field) = $primary; - $query = $field->addToQuery($query, - $this->getOrmPath($this->primary)); + $query = $this->addToQuery($query, $field, + $this->getOrmPath($this->primary, $query)); } if ($secondary = $fields[$this->secondary]) { list(,$field) = $secondary; - $query = $field->addToQuery($query, - $this->getOrmPath($this->secondary)); + $query = $this->addToQuery($query, $field, + $this->getOrmPath($this->secondary, $query)); } switch ($this->link) { @@ -785,7 +797,31 @@ extends VerySimpleModel { array('id' => $this->id)); } - function getOrmPath($name) { + function getOrmPath($name, $query=null) { + // Special case for custom data `__answers!id__value`. Only add the + // join and constraint on the query the first pass, when the query + // being mangled is received. + $path = array(); + if ($query && preg_match('/^(.+?)__(answers!(\d+))/', $name, $path)) { + // Add a join to the model of the queryset where the custom data + // is forked from — duplicate the 'answers' join and add the + // constraint to the query based on the field_id + // $path[1] - part before the answers (user__org__entries) + // $path[2] - answers!xx join part + // $path[3] - the `xx` part of the answers!xx join component + $root = $query->model; + $meta = $root::getMeta()->getByPath($path[1]); + $joins = $meta['joins']; + if (!isset($joins[$path[2]])) { + $meta->addJoin($path[2], $joins['answers']); + } + // Ensure that the query join through answers!xx is only for the + // records which match field_id=xx + $query->constrain(array("{$path[1]}__{$path[2]}" => + array("{$path[1]}__{$path[2]}__field_id" => (int) $path[3]) + )); + // Leave $name unchanged + } return $name; } diff --git a/include/class.user.php b/include/class.user.php index 22b996bc43864eb6c90eccdb00325e7de3a02d84..ce61aa838733778ac0aa54209d196e71d998967b 100644 --- a/include/class.user.php +++ b/include/class.user.php @@ -66,12 +66,12 @@ class UserModel extends VerySimpleModel { 'constraint' => array('id' => 'UserCdata.user_id'), 'null' => true, ), - 'cdata_entry' => array( + 'entries' => array( 'constraint' => array( 'id' => 'DynamicFormEntry.object_id', "'U'" => 'DynamicFormEntry.object_type', ), - 'null' => true, + 'list' => true, ), ) );