Example #1
0
 /**
  * Implementation of core get() logic
  *
  * @param string $ps_field bundle specifier
  * @param null|array $pa_options options array
  * @return array|null|string
  */
 private function _get($ps_field, $pa_options = null)
 {
     if (!is_array($pa_options)) {
         $pa_options = array();
     }
     $vb_return_as_array = isset($pa_options['returnAsArray']) ? (bool) $pa_options['returnAsArray'] : false;
     $vb_return_all_locales = isset($pa_options['returnAllLocales']) ? (bool) $pa_options['returnAllLocales'] : false;
     $vb_return_with_structure = isset($pa_options['returnWithStructure']) ? (bool) $pa_options['returnWithStructure'] : false;
     if ($vb_return_with_structure) {
         $pa_options['returnAsArray'] = $vb_return_as_array = true;
     }
     // returnWithStructure implies returnAsArray
     $vs_delimiter = isset($pa_options['delimiter']) ? $pa_options['delimiter'] : ';';
     $vb_unserialize = isset($pa_options['unserialize']) ? (bool) $pa_options['unserialize'] : false;
     $vb_return_url = isset($pa_options['returnURL']) ? (bool) $pa_options['returnURL'] : false;
     $vb_return_path = isset($pa_options['returnPath']) ? (bool) $pa_options['returnPAth'] : false;
     $vb_convert_codes_to_display_text = isset($pa_options['convertCodesToDisplayText']) ? (bool) $pa_options['convertCodesToDisplayText'] : false;
     $vb_convert_codes_to_idno = isset($pa_options['convertCodesToIdno']) ? (bool) $pa_options['convertCodesToIdno'] : false;
     $vb_use_locale_codes = isset($pa_options['useLocaleCodes']) ? (bool) $pa_options['useLocaleCodes'] : false;
     if (!($vs_output = isset($pa_options['output']) ? (string) $pa_options['output'] : null)) {
         if ($vb_convert_codes_to_display_text) {
             $vs_output = "text";
         }
         if (!$vs_output && $vb_convert_codes_to_idno) {
             $vs_output = "idno";
         }
     }
     if (!in_array($vs_output, array('text', 'idno', 'value'))) {
         $vs_output = 'value';
     }
     $pa_options['output'] = $vs_output;
     if (!($vb_return_as_link = isset($pa_options['makeLink']) ? (bool) $pa_options['makeLink'] : false)) {
         $vb_return_as_link = isset($pa_options['returnAsLink']) ? (bool) $pa_options['returnAsLink'] : false;
     }
     $pa_options['makeLink'] = $vb_return_as_link;
     $vn_max_levels_from_top = isset($pa_options['maxLevelsFromTop']) ? (int) $pa_options['maxLevelsFromTop'] : null;
     $vn_max_levels_from_bottom = isset($pa_options['maxLevelsFromBottom']) ? (int) $pa_options['maxLevelsFromBottom'] : null;
     $vn_remove_first_items = isset($pa_options['removeFirstItems']) ? (int) $pa_options['removeFirstItems'] : 0;
     $va_check_access = isset($pa_options['checkAccess']) ? is_array($pa_options['checkAccess']) ? $pa_options['checkAccess'] : array($pa_options['checkAccess']) : null;
     $vs_template = isset($pa_options['template']) ? (string) $pa_options['template'] : null;
     $va_path_components = isset(SearchResult::$s_parsed_field_component_cache[$this->ops_table_name . '/' . $ps_field]) ? SearchResult::$s_parsed_field_component_cache[$this->ops_table_name . '/' . $ps_field] : $this->parseFieldPathComponents($ps_field);
     $va_val_opts = array_merge($pa_options, array('returnAsArray' => $vb_return_as_array, 'returnAllLocales' => $vb_return_all_locales, 'returnWithStructure' => $vb_return_with_structure, 'pathComponents' => $va_path_components, 'delimiter' => $vs_delimiter, 'makeLink' => $vb_return_as_link, 'returnURL' => $vb_return_url, 'returnPath' => $vb_return_path, 'unserialize' => $vb_unserialize, 'convertCodesToDisplayText' => $vb_convert_codes_to_display_text, 'convertCodesToIdno' => $vb_convert_codes_to_idno, 'checkAccess' => $va_check_access, 'template' => $vs_template, 'useLocaleCodes' => $vb_use_locale_codes));
     if ($va_path_components['table_name'] != $this->ops_table_name) {
         $vs_access_chk_key = $va_path_components['table_name'] . ($va_path_components['field_name'] ? '.' . $va_path_components['field_name'] : '');
     } else {
         $vs_access_chk_key = $va_path_components['field_name'];
     }
     if ($va_path_components['field_name'] !== 'access' && caGetBundleAccessLevel($va_path_components['table_name'], $vs_access_chk_key) == __CA_BUNDLE_ACCESS_NONE__) {
         return null;
     }
     if (!(($vs_value = $this->opo_engine_result->get($ps_field, $pa_options)) === false)) {
         if ($vb_return_as_array) {
             if ($vb_return_all_locales) {
                 return array(1 => $vs_value);
             } else {
                 return array($vs_value);
             }
         } else {
             return $vs_value;
         }
     }
     if (!($t_instance = SearchResult::$s_instance_cache[$va_path_components['table_name']])) {
         $t_instance = SearchResult::$s_instance_cache[$va_path_components['table_name']] = $this->opo_datamodel->getInstanceByTableName($va_path_components['table_name'], true);
     }
     if (!$t_instance) {
         return null;
     }
     // Bad table
     $vn_row_id = $this->opo_engine_result->get($this->ops_table_pk);
     $va_val_opts['primaryKey'] = $t_instance->primaryKey();
     if ($va_path_components['hierarchical_modifier']) {
         if (in_array($va_path_components['field_name'], array('preferred_labels', 'nonpreferred_labels')) && !$va_path_components['subfield_name']) {
             $va_path_components['subfield_name'] = $va_path_components['components'][2] = $t_instance->getLabelDisplayField();
         }
         switch ($va_path_components['hierarchical_modifier']) {
             case 'parent':
                 if ($va_path_components['related']) {
                     // [RELATED TABLE PARENT]
                     if (!isset(SearchResult::$opa_hierarchy_parent_prefetch_cache_index[$this->ops_table_name][$vn_row_id][$va_path_components['table_name']])) {
                         $this->prefetchHierarchyParents($va_path_components['table_name'], $this->opo_engine_result->currentRow(), $this->getOption('prefetch'), $pa_options);
                     }
                     $va_ids = SearchResult::$opa_hierarchy_parent_prefetch_cache_index[$this->ops_table_name][$vn_row_id][$va_path_components['table_name']];
                 } else {
                     // [PRIMARY TABLE PARENT]
                     if (!isset(SearchResult::$opa_hierarchy_parent_prefetch_cache[$va_path_components['table_name']][$vn_row_id])) {
                         $this->prefetchHierarchyParents($va_path_components['table_name'], $this->opo_engine_result->currentRow(), $this->getOption('prefetch'), $pa_options);
                     }
                     $va_ids = array($vn_row_id);
                 }
                 if (!sizeof($va_ids)) {
                     return $pa_options['returnAsArray'] ? array() : null;
                 }
                 $va_hiers = array();
                 foreach ($va_ids as $vn_id) {
                     $va_parent_ids = array();
                     if (isset(SearchResult::$opa_hierarchy_parent_prefetch_cache[$va_path_components['table_name']][$vn_id]) && is_array(SearchResult::$opa_hierarchy_parent_prefetch_cache[$va_path_components['table_name']][$vn_id])) {
                         if (!is_array($va_parent_ids = SearchResult::$opa_hierarchy_parent_prefetch_cache[$va_path_components['table_name']][$vn_id])) {
                             return $pa_options['returnAsArray'] ? array() : null;
                         }
                     }
                     $va_parent_ids = array_slice($va_parent_ids, 0, 1);
                     if (!($qr_hier = $t_instance->makeSearchResult($va_path_components['table_name'], $va_parent_ids))) {
                         return $pa_options['returnAsArray'] ? array() : null;
                     }
                     $va_tmp = array($va_path_components['table_name']);
                     if ($va_path_components['field_name']) {
                         $va_tmp[] = $va_path_components['field_name'];
                     }
                     if ($va_path_components['subfield_name']) {
                         $va_tmp[] = $va_path_components['subfield_name'];
                     }
                     $vs_hier_fld_name = join(".", $va_tmp);
                     $vs_pk = $t_instance->primaryKey();
                     $vm_val = null;
                     if ($qr_hier->nextHit()) {
                         $vm_val = $qr_hier->get($vs_hier_fld_name, $pa_options);
                     }
                     if ($vm_val) {
                         $va_hiers[] = $vb_return_as_array ? array_shift($vm_val) : $vm_val;
                     }
                 }
                 return $vb_return_as_array ? $va_hiers : join($vs_delimiter, $va_hiers);
                 break;
             case 'hierarchy':
                 // generate the hierarchy
                 if ($va_path_components['related']) {
                     // [RELATED TABLE HIERARCHY]
                     if (!isset(SearchResult::$opa_hierarchy_parent_prefetch_cache_index[$this->ops_table_name][$vn_row_id][$va_path_components['table_name']])) {
                         $this->prefetchHierarchyParents($va_path_components['table_name'], $this->opo_engine_result->currentRow(), $this->getOption('prefetch'), $pa_options);
                     }
                     // ids of related items
                     $va_ids = array_values(SearchResult::$opa_hierarchy_parent_prefetch_cache_index[$this->ops_table_name][$vn_row_id][$va_path_components['table_name']]);
                 } else {
                     // [PRIMARY TABLE HIERARCHY]
                     if (!isset(SearchResult::$opa_hierarchy_parent_prefetch_cache[$va_path_components['table_name']][$vn_row_id])) {
                         $this->prefetchHierarchyParents($va_path_components['table_name'], $this->opo_engine_result->currentRow(), $this->getOption('prefetch'), $pa_options);
                     }
                     $va_ids = array($vn_row_id);
                 }
                 if (!sizeof($va_ids)) {
                     return $pa_options['returnAsArray'] ? array() : null;
                 }
                 $vs_hier_pk_fld = $t_instance->primaryKey();
                 $va_hiers = $va_hier_ids = array();
                 $vs_hierarchy_direction = isset($pa_options['hierarchyDirection']) ? strtolower($pa_options['hierarchyDirection']) : 'asc';
                 if ($t_instance->isHierarchical()) {
                     if ($va_path_components['field_name'] === $vs_hier_pk_fld) {
                         if ($va_path_components['related']) {
                             foreach ($va_ids as $vn_id) {
                                 if (is_array(SearchResult::$opa_hierarchy_parent_prefetch_cache[$va_path_components['table_name']][$vn_id])) {
                                     $va_hier_id_list = array_merge(array($vn_id), SearchResult::$opa_hierarchy_parent_prefetch_cache[$va_path_components['table_name']][$vn_id]);
                                     $va_hier_id_list = array_filter($va_hier_id_list, function ($v) {
                                         return $v > 0;
                                     });
                                     if ($vs_hierarchy_direction === 'asc') {
                                         $va_hier_id_list = array_reverse($va_hier_id_list);
                                     }
                                     if (!is_null($vn_max_levels_from_top)) {
                                         $va_hier_id_list = array_slice($va_hier_id_list, 0, $vn_max_levels_from_top, true);
                                     } elseif (!is_null($vn_max_levels_from_bottom)) {
                                         if (($vn_start = sizeof($va_hier_id_list) - $vn_max_levels_from_bottom) < 0) {
                                             $vn_start = 0;
                                         }
                                         $va_hier_id_list = array_slice($va_hier_id_list, $vn_start, $vn_max_levels_from_bottom, true);
                                     }
                                     $va_hier_ids[] = $va_hier_id_list;
                                 }
                             }
                         } else {
                             // Return ids from hierarchy in order
                             if (is_array(SearchResult::$opa_hierarchy_parent_prefetch_cache[$va_path_components['table_name']][$vn_row_id])) {
                                 $va_hier_ids = array_merge(array($vn_row_id), SearchResult::$opa_hierarchy_parent_prefetch_cache[$va_path_components['table_name']][$vn_row_id]);
                             } else {
                                 $va_hier_ids = array($vn_row_id);
                             }
                             if (!is_null($vn_max_levels_from_top)) {
                                 $va_hier_ids = array_slice($va_hier_ids, 0, $vn_max_levels_from_top, true);
                             } elseif (!is_null($vn_max_levels_from_bottom)) {
                                 if (($vn_start = sizeof($va_hier_ids) - $vn_max_levels_from_bottom) < 0) {
                                     $vn_start = 0;
                                 }
                                 $va_hier_ids = array_slice($va_hier_ids, $vn_start, $vn_max_levels_from_bottom, true);
                             }
                             if ($vs_hierarchy_direction === 'asc') {
                                 $va_hier_ids = array_reverse($va_hier_ids);
                             }
                         }
                         return $vb_return_as_array ? $va_hier_ids : join($vs_delimiter, $va_hier_ids);
                     } else {
                         $vs_field_spec = join('.', array_values($va_path_components['components']));
                         $va_ancestor_id_list = $this->get($va_path_components['table_name'] . '.hierarchy.' . $vs_hier_pk_fld, array_merge($pa_options, array('returnAsArray' => true, 'returnAsLink' => false, 'returnAllLocales' => false)));
                         if (!is_array($va_ancestor_id_list)) {
                             return $vb_return_as_array ? array() : null;
                         }
                         if (!$va_path_components['related']) {
                             $va_ancestor_id_list = array($va_ancestor_id_list);
                         }
                         $va_hier_list = array();
                         foreach ($va_ancestor_id_list as $va_ancestor_ids) {
                             if ($vn_remove_first_items > 0) {
                                 $va_ancestor_ids = array_slice($va_ancestor_ids, $vn_remove_first_items);
                             }
                             $va_hier_item = array();
                             if ($qr_hier = caMakeSearchResult($va_path_components['table_name'], $va_ancestor_ids)) {
                                 while ($qr_hier->nextHit()) {
                                     $va_hier_item += $qr_hier->get($vs_field_spec, array('returnWithStructure' => true, 'returnAllLocales' => true, 'useLocaleCodes' => $pa_options['useLocaleCodes']));
                                 }
                                 if (!is_null($vn_max_levels_from_top)) {
                                     $va_hier_item = array_slice($va_hier_item, 0, $vn_max_levels_from_top, true);
                                 } elseif (!is_null($vn_max_levels_from_bottom)) {
                                     if (($vn_start = sizeof($va_hier_item) - $vn_max_levels_from_bottom) < 0) {
                                         $vn_start = 0;
                                     }
                                     $va_hier_item = array_slice($va_hier_item, $vn_start, $vn_max_levels_from_bottom, true);
                                 }
                                 $va_hier_list[] = $va_hier_item;
                             }
                         }
                     }
                 }
                 $va_acc = array();
                 foreach ($va_hier_list as $vn_h => $va_hier_item) {
                     if (!$vb_return_all_locales) {
                         $va_hier_item = caExtractValuesByUserLocale($va_hier_item);
                     }
                     if ($vb_return_with_structure) {
                         $va_acc[] = $va_hier_item;
                     } else {
                         $va_acc = $this->_flattenArray($va_hier_item, $pa_options);
                     }
                 }
                 return $pa_options['returnAsArray'] ? $va_acc : join($vs_delimiter, $va_acc);
                 break;
             case 'children':
                 // grab children
                 if ($va_path_components['related']) {
                     // [RELATED TABLE CHILDREN]
                     if (!isset(SearchResult::$opa_hierarchy_children_prefetch_cache_index[$this->ops_table_name][$vn_row_id][$va_path_components['table_name']])) {
                         $this->prefetchHierarchyChildren($va_path_components['table_name'], $this->opo_engine_result->currentRow(), $this->getOption('prefetch'), $pa_options);
                     }
                     $va_ids = SearchResult::$opa_hierarchy_children_prefetch_cache_index[$this->ops_table_name][$vn_row_id][$va_path_components['table_name']];
                 } else {
                     // [PRIMARY TABLE CHILDREN]
                     if (!isset(SearchResult::$opa_hierarchy_children_prefetch_cache[$this->ops_table_name][$vn_row_id])) {
                         $this->prefetchHierarchyChildren($va_path_components['table_name'], $this->opo_engine_result->currentRow(), $this->getOption('prefetch'), $pa_options);
                     }
                     $va_ids = array($vn_row_id);
                 }
                 $va_hier_list = array();
                 foreach ($va_ids as $vn_id) {
                     if (!is_array(SearchResult::$opa_hierarchy_children_prefetch_cache[$va_path_components['table_name']][$vn_id]) || !sizeof(SearchResult::$opa_hierarchy_children_prefetch_cache[$va_path_components['table_name']][$vn_id])) {
                         continue;
                     }
                     $qr_hier = $t_instance->makeSearchResult($va_path_components['table_name'], SearchResult::$opa_hierarchy_children_prefetch_cache[$va_path_components['table_name']][$vn_id]);
                     $va_tmp = array($va_path_components['table_name']);
                     if ($va_path_components['field_name']) {
                         $va_tmp[] = $va_path_components['field_name'];
                     }
                     if ($va_path_components['subfield_name']) {
                         $va_tmp[] = $va_path_components['subfield_name'];
                     }
                     $vs_hier_fld_name = join(".", $va_tmp);
                     $vs_pk = $t_instance->primaryKey();
                     while ($qr_hier->nextHit()) {
                         $vm_val = $qr_hier->get($vs_hier_fld_name, $pa_options);
                         $va_hier_list[$qr_hier->get($va_path_components['table_name'] . '.' . $vs_pk)] = $vb_return_as_array ? array_shift($vm_val) : $vm_val;
                     }
                 }
                 if (!$vb_return_as_array) {
                     return join($vs_delimiter, $va_hier_list);
                 }
                 return $va_hier_list;
                 break;
             case 'siblings':
                 // grab siblings
                 if ($va_path_components['related']) {
                     // [RELATED TABLE SIBLINGS]
                     if (!isset(SearchResult::$opa_hierarchy_siblings_prefetch_cache_index[$this->ops_table_name][$vn_row_id][$va_path_components['table_name']])) {
                         $this->prefetchHierarchySiblings($va_path_components['table_name'], $this->opo_engine_result->currentRow(), $this->getOption('prefetch'), $pa_options);
                     }
                     $va_ids = SearchResult::$opa_hierarchy_siblings_prefetch_cache_index[$this->ops_table_name][$vn_row_id][$va_path_components['table_name']];
                 } else {
                     // [PRIMARY TABLE SIBLINGS]
                     if (!isset(SearchResult::$opa_hierarchy_siblings_prefetch_cache[$this->ops_table_name][$vn_row_id])) {
                         $this->prefetchHierarchySiblings($va_path_components['table_name'], $this->opo_engine_result->currentRow(), $this->getOption('prefetch'), $pa_options);
                     }
                     $va_ids = array($vn_row_id);
                 }
                 $va_hier_list = array();
                 foreach ($va_ids as $vn_id) {
                     if (!is_array(SearchResult::$opa_hierarchy_siblings_prefetch_cache[$va_path_components['table_name']][$vn_id]) || !sizeof(SearchResult::$opa_hierarchy_siblings_prefetch_cache[$va_path_components['table_name']][$vn_id])) {
                         continue;
                     }
                     $qr_hier = $t_instance->makeSearchResult($va_path_components['table_name'], SearchResult::$opa_hierarchy_siblings_prefetch_cache[$va_path_components['table_name']][$vn_id]);
                     $va_tmp = array($va_path_components['table_name']);
                     if ($va_path_components['field_name']) {
                         $va_tmp[] = $va_path_components['field_name'];
                     }
                     if ($va_path_components['subfield_name']) {
                         $va_tmp[] = $va_path_components['subfield_name'];
                     }
                     $vs_hier_fld_name = join(".", $va_tmp);
                     $vs_pk = $t_instance->primaryKey();
                     while ($qr_hier->nextHit()) {
                         $vm_val = $qr_hier->get($vs_hier_fld_name, $pa_options);
                         $va_hier_list[$qr_hier->get($va_path_components['table_name'] . '.' . $vs_pk)] = $vb_return_as_array ? array_shift($vm_val) : $vm_val;
                     }
                 }
                 if (!$vb_return_as_array) {
                     return join($vs_delimiter, $va_hier_list);
                 }
                 return $va_hier_list;
                 break;
         }
         return;
     }
     if ($va_path_components['related']) {
         //
         // [RELATED TABLE]
         //
         $vs_opt_md5 = caMakeCacheKeyFromOptions(array_merge($pa_options, array('dontReturnLabels' => false)));
         if (!isset(self::$s_rel_prefetch_cache[$this->ops_table_name][$vn_row_id][$va_path_components['table_name']][$vs_opt_md5])) {
             $this->prefetchRelated($va_path_components['table_name'], $this->opo_engine_result->currentRow(), $this->getOption('prefetch'), array_merge($pa_options, array('dontReturnLabels' => false)));
         }
         $va_related_items = self::$s_rel_prefetch_cache[$this->ops_table_name][$vn_row_id][$va_path_components['table_name']][$vs_opt_md5];
         if (!is_array($va_related_items)) {
             return $vb_return_with_structure || $vb_return_as_array ? array() : null;
         }
         return $this->_getRelatedValue($va_related_items, $va_val_opts);
     } else {
         if (!$va_path_components['hierarchical_modifier']) {
             //
             // [PRIMARY TABLE] Created on
             //
             if ($va_path_components['field_name'] == 'created') {
                 if (!isset(self::$s_timestamp_cache['created_on'][$this->ops_table_name][$vn_row_id])) {
                     $this->prefetchChangeLogData($this->ops_table_name, $this->opo_engine_result->currentRow(), $this->getOption('prefetch'));
                 }
                 if ($vb_return_as_array) {
                     return self::$s_timestamp_cache['created_on'][$this->ops_table_name][$vn_row_id];
                 } else {
                     $vs_subfield = $va_path_components['subfield_name'] ? $va_path_components['subfield_name'] : 'timestamp';
                     $vm_val = self::$s_timestamp_cache['created_on'][$this->ops_table_name][$vn_row_id][$vs_subfield];
                     if ($vs_subfield == 'timestamp') {
                         $this->opo_tep->init();
                         $this->opo_tep->setUnixTimestamps($vm_val, $vm_val);
                         $vm_val = $this->opo_tep->getText($pa_options);
                     }
                     return $vm_val;
                 }
             }
             //
             // [PRIMARY TABLE] Last modified on
             //
             if ($va_path_components['field_name'] == 'lastModified') {
                 if (!isset(self::$s_timestamp_cache['last_changed'][$this->ops_table_name][$vn_row_id])) {
                     $this->prefetchChangeLogData($this->ops_table_name, $this->opo_engine_result->currentRow(), $this->getOption('prefetch'));
                 }
                 if ($vb_return_as_array) {
                     return self::$s_timestamp_cache['last_changed'][$this->ops_table_name][$vn_row_id];
                 } else {
                     $vs_subfield = $va_path_components['subfield_name'] ? $va_path_components['subfield_name'] : 'timestamp';
                     $vm_val = self::$s_timestamp_cache['last_changed'][$this->ops_table_name][$vn_row_id][$vs_subfield];
                     if ($vs_subfield == 'timestamp') {
                         $this->opo_tep->init();
                         $this->opo_tep->setUnixTimestamps($vm_val, $vm_val);
                         $vm_val = $this->opo_tep->getText($pa_options);
                     }
                     return $vm_val;
                 }
             }
             $vs_opt_md5 = caMakeCacheKeyFromOptions($pa_options);
             //
             // [PRIMARY TABLE] Preferred/nonpreferred labels
             //
             if (in_array($va_path_components['field_name'], array('preferred_labels', 'nonpreferred_labels')) && $t_instance instanceof LabelableBaseModelWithAttributes) {
                 $vs_label_table_name = $t_instance->getLabelTableName();
                 if (!isset(self::$s_prefetch_cache[$vs_label_table_name][$vn_row_id][$vs_opt_md5])) {
                     $this->prefetchLabels($va_path_components['table_name'], $this->opo_engine_result->currentRow(), $this->getOption('prefetch'), $pa_options);
                 }
                 return $this->_getLabelValue(self::$s_prefetch_cache[$vs_label_table_name][$vn_row_id][$vs_opt_md5], $t_instance, $va_val_opts);
             }
             if ($t_instance->hasField($va_path_components['field_name'])) {
                 $va_val_opts['fieldInfo'] = $t_instance->getFieldInfo($va_path_components['field_name']);
                 //
                 // [PRIMARY TABLE] Plain old intrinsic
                 //
                 if (!isset(self::$s_prefetch_cache[$va_path_components['table_name']][$vn_row_id][$vs_opt_md5])) {
                     $this->prefetch($va_path_components['table_name'], $this->opo_engine_result->currentRow(), $this->getOption('prefetch'), $pa_options);
                 }
                 return $this->_getIntrinsicValue(self::$s_prefetch_cache[$va_path_components['table_name']][$vn_row_id][$vs_opt_md5], $t_instance, $va_val_opts);
             } elseif (method_exists($t_instance, 'isValidBundle') && !$t_instance->hasElement($va_path_components['field_name']) && $t_instance->isValidBundle($va_path_components['field_name'])) {
                 //
                 // [PRIMARY TABLE] Special bundle
                 //
                 return $t_instance->renderBundleForDisplay($va_path_components['field_name'], $vn_row_id, self::$s_prefetch_cache[$va_path_components['table_name']][$vn_row_id][$vs_opt_md5], $va_val_opts);
             } else {
                 //
                 // [PRIMARY TABLE] Metadata attribute
                 //
                 if ($t_instance instanceof BaseModelWithAttributes && isset($va_path_components['field_name']) && $va_path_components['field_name'] && ($t_element = $t_instance->_getElementInstance($va_path_components['field_name']))) {
                     $vn_element_id = $t_element->getPrimaryKey();
                 } else {
                     return $pa_options['returnAsArray'] ? array() : null;
                 }
                 if (!isset(ca_attributes::$s_get_attributes_cache[(int) $this->opn_table_num . '/' . (int) $vn_row_id][(int) $vn_element_id])) {
                     ca_attributes::prefetchAttributes($this->opo_subject_instance->getDb(), $this->opn_table_num, $this->getRowIDsToPrefetch($this->opo_engine_result->currentRow(), $this->getOption('prefetch')), $vn_element_id ? array($vn_element_id) : null, array('dontFetchAlreadyCachedValues' => true));
                 }
                 $va_attributes = ca_attributes::getAttributes($this->opo_subject_instance->getDb(), $this->opn_table_num, $vn_row_id, array($vn_element_id), array());
                 return $this->_getAttributeValue($va_attributes[$vn_element_id], $t_instance, $va_val_opts);
             }
         }
     }
     return null;
 }
    /**
     * Indexes single row in a table; this is the public call when one needs to index content.
     * indexRow() will analyze the dependencies of the row being indexed and automatically
     * apply the indexing of the row to all dependent rows in other tables.  (Note that while I call this
     * a "public" call in fact you shouldn't need to call this directly. BaseModel.php does this for you
     * during insert() and update().)
     *
     * For example, if you are indexing a row in table 'entities', then indexRow()
     * will automatically apply the indexing not just to the entities record, but also
     * to all objects, place_names, occurrences, lots, etc. that reference the entity.
     * The dependencies are configured in the search_indices.conf configuration file.
     *
     * "subject" tablenum/row_id refer to the row **to which the indexing is being applied**. This may be the row being indexed
     * or it may be a dependent row. The "content" tablenum/fieldnum/row_id parameters define the specific row and field being indexed.
     * This is always the actual row being indexed. $pm_content is the content to be indexed and $pa_options is an optional associative
     * array of indexing options passed through from the search_indices.conf (no options are defined yet - but will be soon)
     */
    public function indexRow($pn_subject_tablenum, $pn_subject_row_id, $pa_field_data, $pb_reindex_mode = false, $pa_exclusion_list = null, $pa_changed_fields = null, $pa_old_values = null, $pa_options = null)
    {
        $vb_initial_reindex_mode = $pb_reindex_mode;
        if (!$pb_reindex_mode && is_array($pa_changed_fields) && !sizeof($pa_changed_fields)) {
            return;
        }
        // don't bother indexing if there are no changed fields
        $vs_subject_tablename = $this->opo_datamodel->getTableName($pn_subject_tablenum);
        $t_subject = $this->opo_datamodel->getInstanceByTableName($vs_subject_tablename, true);
        $t_subject->setDb($this->getDb());
        // force the subject instance to use the same db connection as the indexer, in case we're operating in a transaction
        // Prevent endless recursive reindexing
        if (is_array($pa_exclusion_list[$pn_subject_tablenum]) && isset($pa_exclusion_list[$pn_subject_tablenum][$pn_subject_row_id])) {
            return;
        }
        $pb_is_new_row = (int) caGetOption('isNewRow', $pa_options, false);
        $vb_reindex_children = false;
        $vs_subject_pk = $t_subject->primaryKey();
        if (!is_array($pa_changed_fields)) {
            $pa_changed_fields = array();
        }
        foreach ($pa_changed_fields as $vs_k => $vb_bool) {
            if (!isset($pa_field_data[$vs_k])) {
                $pa_field_data[$vs_k] = null;
            }
        }
        $vb_can_do_incremental_indexing = $this->opo_engine->can('incremental_reindexing') ? true : false;
        // can the engine do incremental indexing? Or do we need to reindex the entire row every time?
        if (!$pa_exclusion_list) {
            $pa_exclusion_list = array();
        }
        $pa_exclusion_list[$pn_subject_tablenum][$pn_subject_row_id] = true;
        //
        // index fields in subject table itself
        //
        $va_fields_to_index = $this->getFieldsToIndex($pn_subject_tablenum);
        if (is_array($va_fields_to_index)) {
            foreach ($va_fields_to_index as $vs_k => $va_data) {
                if (preg_match('!^ca_attribute_(.*)$!', $vs_k, $va_matches)) {
                    unset($va_fields_to_index[$vs_k]);
                    if ($va_data['DONT_INDEX']) {
                        // remove attribute from indexing list
                        unset($va_fields_to_index['_ca_attribute_' . $va_matches[1]]);
                    } else {
                        if ($vn_element_id = $this->_getElementID($va_matches[1])) {
                            $va_fields_to_index['_ca_attribute_' . $vn_element_id] = $va_data;
                        }
                    }
                }
            }
            // always index type id if applicable
            if (method_exists($t_subject, 'getTypeFieldName') && ($vs_type_field = $t_subject->getTypeFieldName()) && !isset($va_fields_to_index[$vs_type_field])) {
                $va_fields_to_index[$vs_type_field] = array('STORE', 'DONT_TOKENIZE');
            }
        }
        //
        // If location in hierarchy has changed we need to reindex this record and all of its children
        //
        if ($t_subject->isHierarchical() && isset($pa_changed_fields['parent_id']) && $pa_changed_fields['parent_id'] && method_exists($t_subject, "makeSearchResult")) {
            $pb_reindex_mode = true;
            $vb_reindex_children = true;
        }
        $vb_started_indexing = false;
        if (is_array($va_fields_to_index)) {
            $this->opo_engine->startRowIndexing($pn_subject_tablenum, $pn_subject_row_id);
            $vb_started_indexing = true;
            foreach ($va_fields_to_index as $vs_field => $va_data) {
                if (substr($vs_field, 0, 14) === '_ca_attribute_') {
                    //
                    // Is attribute
                    //
                    if (!preg_match('!^_ca_attribute_(.*)$!', $vs_field, $va_matches)) {
                        continue;
                    }
                    if ($vb_can_do_incremental_indexing && !$pb_is_new_row && !$pb_reindex_mode && (!isset($pa_changed_fields[$vs_field]) || !$pa_changed_fields[$vs_field])) {
                        continue;
                        // skip unchanged attribute value
                    }
                    if ($va_data['DONT_INDEX'] && is_array($va_data['DONT_INDEX'])) {
                        $vb_cont = false;
                        foreach ($va_data["DONT_INDEX"] as $vs_exclude_type) {
                            if ($this->_getElementID($vs_exclude_type) == intval($va_matches[1])) {
                                $vb_cont = true;
                                break;
                            }
                        }
                        if ($vb_cont) {
                            continue;
                        }
                        // skip excluded attribute type
                    }
                    $va_data['datatype'] = (int) $this->_getElementDataType($va_matches[1]);
                    $this->_indexAttribute($t_subject, $pn_subject_row_id, $va_matches[1], $va_data);
                } else {
                    //
                    // Plain old field
                    //
                    if ($vb_can_do_incremental_indexing && !$pb_is_new_row && !$pb_reindex_mode && !isset($pa_changed_fields[$vs_field]) && $vs_field != $vs_subject_pk) {
                        // skip unchanged
                        continue;
                    }
                    if (!($vn_fld_num = $t_subject->fieldNum($vs_field))) {
                        continue;
                    }
                    //
                    // Hierarchical indexing in primary table
                    //
                    if (isset($va_data['INDEX_ANCESTORS']) && $va_data['INDEX_ANCESTORS'] || in_array('INDEX_ANCESTORS', $va_data)) {
                        if ($t_subject && $t_subject->isHierarchical()) {
                            $vn_fld_num = $t_subject->fieldNum($vs_field);
                            if ($va_hier_values = $this->_genHierarchicalPath($pn_subject_row_id, $vs_field, $t_subject, $va_data)) {
                                $this->opo_engine->indexField($pn_subject_tablenum, 'I' . $vn_fld_num, $pn_subject_row_id, join(" ", $va_hier_values['values']), $va_data);
                                if (caGetOption('INDEX_ANCESTORS_AS_PATH_WITH_DELIMITER', $va_data, false) !== false) {
                                    $this->opo_engine->indexField($pn_subject_tablenum, 'I' . $vn_fld_num, $pn_subject_row_id, $va_hier_values['path'], array_merge($va_data, array('DONT_TOKENIZE' => 1)));
                                }
                            }
                            $va_children_ids = $t_subject->getHierarchyAsList($pn_subject_row_id, array('idsOnly' => true));
                            if (!$pb_reindex_mode && is_array($va_children_ids) && sizeof($va_children_ids) > 0) {
                                // trigger reindexing of children
                                $o_indexer = new SearchIndexer($this->opo_db);
                                $qr_children_res = $t_subject->makeSearchResult($vs_subject_tablename, $va_children_ids, array('db' => $this->getDb()));
                                while ($qr_children_res->nextHit()) {
                                    $o_indexer->indexRow($pn_subject_tablenum, $qr_children_res->get($vs_subject_pk), array('parent_id' => $qr_children_res->get('parent_id'), $vs_field => $qr_children_res->get($vs_field)), false, $pa_exclusion_list, array($vs_field => true), null);
                                }
                            }
                            continue;
                        }
                    }
                    // specialized identifier (idno) processing; used IDNumbering plugin to generate searchable permutations of identifier
                    if ((isset($va_data['INDEX_AS_IDNO']) && $va_data['INDEX_AS_IDNO'] || in_array('INDEX_AS_IDNO', $va_data)) && method_exists($t_subject, "getIDNoPlugInInstance") && ($o_idno = $t_subject->getIDNoPlugInInstance())) {
                        $va_values = $o_idno->getIndexValues($pa_field_data[$vs_field]);
                        $vn_fld_num = $t_subject->fieldNum($vs_field);
                        $this->opo_engine->indexField($pn_subject_tablenum, 'I' . $vn_fld_num, $pn_subject_row_id, join(" ", $va_values), $va_data);
                        continue;
                    }
                    $va_field_list = $t_subject->getFieldsArray();
                    if (in_array($va_field_list[$vs_field]['FIELD_TYPE'], array(FT_DATERANGE, FT_HISTORIC_DATERANGE))) {
                        // if the field is a daterange type get content from start and end fields
                        $start_field = $va_field_list[$vs_field]['START'];
                        $end_field = $va_field_list[$vs_field]['END'];
                        if (!$pa_field_data[$start_field] || !$pa_field_data[$start_field]) {
                            continue;
                        }
                        $pn_content = $pa_field_data[$start_field] . " - " . $pa_field_data[$end_field];
                    } else {
                        $va_content = array();
                        if (isset($va_field_list[$vs_field]['LIST_CODE']) && $va_field_list[$vs_field]['LIST_CODE']) {
                            // Is reference to list item so index preferred label values
                            $t_item = new ca_list_items((int) $pa_field_data[$vs_field]);
                            $va_labels = $t_item->getPreferredDisplayLabelsForIDs(array((int) $pa_field_data[$vs_field]), array('returnAllLocales' => true));
                            foreach ($va_labels as $vn_label_row_id => $va_labels_per_row) {
                                foreach ($va_labels_per_row as $vn_locale_id => $va_label_list) {
                                    foreach ($va_label_list as $vs_label) {
                                        $va_content[$vs_label] = true;
                                    }
                                }
                            }
                            $va_content[$t_item->get('idno')] = true;
                        } else {
                            // is this field related to something?
                            if (is_array($va_rels = $this->opo_datamodel->getManyToOneRelations($vs_subject_tablename)) && $va_rels[$vs_field]) {
                                if (isset($va_rels[$vs_field])) {
                                    if ($pa_changed_fields[$vs_field]) {
                                        $pb_reindex_mode = true;
                                        // trigger full reindex of record so it reflects text of related item (if so indexed)
                                    }
                                }
                                $this->opo_engine->indexField($pn_subject_tablenum, 'I' . $vn_fld_num, $pn_subject_row_id, $pn_content, $va_data);
                            }
                        }
                        $va_content[$pa_field_data[$vs_field]] = true;
                        $this->opo_engine->indexField($pn_subject_tablenum, 'I' . $vn_fld_num, $pn_subject_row_id, join(" ", array_keys($va_content)), $va_data);
                        continue;
                    }
                    $this->opo_engine->indexField($pn_subject_tablenum, 'I' . $vn_fld_num, $pn_subject_row_id, $pn_content, $va_data);
                }
            }
        }
        // -------------------------------------
        //
        // index related fields
        //
        // Here's where we generate indexing on the subject from content in related rows (data stored externally to the subject row)
        // If the underlying engine doesn't support incremental indexing (if it can't change existing indexing for a row in-place, in other words)
        // then we need to do this every time we update the indexing for a row; if the engine *does* support incremental indexing then
        // we can just update the existing indexing with content from the changed fields.
        //
        // We also do this indexing if we're in "reindexing" mode. When reindexing is indicated it means that we need to act as if
        // we're indexing this row for the first time, and all indexing should be performed.
        if (!$vb_can_do_incremental_indexing || $pb_reindex_mode) {
            if (is_array($va_related_tables = $this->getRelatedIndexingTables($pn_subject_tablenum))) {
                if (!$vb_started_indexing) {
                    $this->opo_engine->startRowIndexing($pn_subject_tablenum, $pn_subject_row_id);
                    $vb_started_indexing = true;
                }
                // Needs self-indexing?
                $va_self_info = $this->getTableIndexingInfo($vs_subject_tablename, $vs_subject_tablename);
                if (is_array($va_self_info['related']['fields']) && sizeof($va_self_info['related']['fields'])) {
                    $va_related_tables[] = $vs_subject_tablename;
                }
                foreach ($va_related_tables as $vs_related_table) {
                    $va_queries = array();
                    $vn_related_tablenum = $this->opo_datamodel->getTableNum($vs_related_table);
                    $vs_related_pk = $this->opo_datamodel->getTablePrimaryKeyName($vn_related_tablenum);
                    $t_rel = $this->opo_datamodel->getInstanceByTableNum($vn_related_tablenum, true);
                    $t_rel->setDb($this->getDb());
                    $va_params = null;
                    if ($vs_subject_tablename == $vs_related_table) {
                        // self-relation
                        if (!($vs_self_rel_table_name = $t_rel->getSelfRelationTableName())) {
                            continue;
                        }
                        $t_self_rel = $this->opo_datamodel->getInstanceByTableName($vs_self_rel_table_name, true);
                        $va_proc_field_list = array();
                        $va_fields_to_index = $va_self_info['related']['fields'];
                        $va_field_list = array_keys($va_fields_to_index);
                        $vn_field_list_count = sizeof($va_field_list);
                        for ($vn_i = 0; $vn_i < $vn_field_list_count; $vn_i++) {
                            if ($va_field_list[$vn_i] == '_count') {
                                continue;
                            }
                            if (substr($va_field_list[$vn_i], 0, 14) === '_ca_attribute_') {
                                continue;
                            }
                            if (!trim($va_field_list[$vn_i])) {
                                continue;
                            }
                            $va_proc_field_list[$vn_i] = $vs_related_table . '.' . $va_field_list[$vn_i];
                        }
                        $va_proc_field_list[] = $vs_related_table . '.' . $vs_related_pk;
                        if ($vs_self_rel_table_name) {
                            $va_proc_field_list[] = $vs_self_rel_table_name . '.type_id rel_type_id';
                        }
                        $vs_sql = "\n\t\t\t\t\t\tSELECT " . join(",", $va_proc_field_list) . "\n\t\t\t\t\t\tFROM {$vs_related_table}\n\t\t\t\t\t\tINNER JOIN {$vs_self_rel_table_name} ON {$vs_self_rel_table_name}." . $t_self_rel->getLeftTableFieldName() . " = {$vs_related_table}.{$vs_related_pk}\n\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t(" . $vs_self_rel_table_name . '.' . $t_self_rel->getRightTableFieldName() . ' = ?)
						
						UNION
						
						SELECT ' . join(",", $va_proc_field_list) . "\n\t\t\t\t\t\tFROM {$vs_related_table}\n\t\t\t\t\t\tINNER JOIN {$vs_self_rel_table_name} ON {$vs_self_rel_table_name}." . $t_self_rel->getRightTableFieldName() . " = {$vs_related_table}.{$vs_related_pk}\n\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t(" . $vs_self_rel_table_name . '.' . $t_self_rel->getLeftTableFieldName() . ' = ?)
					';
                        $va_params = array($pn_subject_row_id, $pn_subject_row_id);
                        $va_queries[] = array('sql' => $vs_sql, 'params' => $va_params);
                    } else {
                        // related table
                        $va_fields_to_index = $this->getFieldsToIndex($vs_subject_tablename, $vs_related_table);
                        $va_table_info = $this->getTableIndexingInfo($vs_subject_tablename, $vs_related_table);
                        $va_field_list = array_keys($va_fields_to_index);
                        $va_table_list_list = $va_table_key_list = array();
                        if (isset($va_table_info['key']) && $va_table_info['key']) {
                            $va_table_list_list = array('key' => array($vs_related_table));
                            $va_table_key_list = array();
                        } else {
                            if ($pb_reindex_mode || !$vb_can_do_incremental_indexing) {
                                $va_table_list_list = isset($va_table_info['tables']) ? $va_table_info['tables'] : null;
                                $va_table_key_list = isset($va_table_info['keys']) ? $va_table_info['keys'] : null;
                            }
                        }
                        if (!is_array($va_table_list_list) || !sizeof($va_table_list_list)) {
                            continue;
                        }
                        foreach ($va_table_list_list as $vs_list_name => $va_linking_tables) {
                            array_push($va_linking_tables, $vs_related_table);
                            $vs_left_table = $vs_subject_tablename;
                            $va_joins = array();
                            $vs_rel_type_id_fld = null;
                            foreach ($va_linking_tables as $vs_right_table) {
                                if (is_array($va_table_key_list) && (isset($va_table_key_list[$vs_list_name][$vs_right_table][$vs_left_table]) || isset($va_table_key_list[$vs_list_name][$vs_left_table][$vs_right_table]))) {
                                    // are the keys for this join specified in the indexing config?
                                    if (isset($va_table_key_list[$vs_list_name][$vs_left_table][$vs_right_table])) {
                                        $va_key_spec = $va_table_key_list[$vs_list_name][$vs_left_table][$vs_right_table];
                                        $vs_join = 'INNER JOIN ' . $vs_right_table . ' ON (' . $vs_right_table . '.' . $va_key_spec['right_key'] . ' = ' . $vs_left_table . '.' . $va_key_spec['left_key'];
                                        if ($va_key_spec['left_table_num'] || $va_key_spec['right_table_num']) {
                                            if ($va_key_spec['right_table_num']) {
                                                $vs_join .= ' AND ' . $vs_right_table . '.' . $va_key_spec['right_table_num'] . ' = ' . $this->opo_datamodel->getTableNum($vs_left_table);
                                            } else {
                                                $vs_join .= ' AND ' . $vs_left_table . '.' . $va_key_spec['left_table_num'] . ' = ' . $this->opo_datamodel->getTableNum($vs_right_table);
                                            }
                                        }
                                        $vs_join .= ")";
                                    } else {
                                        $va_key_spec = $va_table_key_list[$vs_list_name][$vs_right_table][$vs_left_table];
                                        $vs_join = 'INNER JOIN ' . $vs_right_table . ' ON (' . $vs_right_table . '.' . $va_key_spec['left_key'] . ' = ' . $vs_left_table . '.' . $va_key_spec['right_key'];
                                        if ($va_key_spec['left_table_num'] || $va_key_spec['right_table_num']) {
                                            if ($va_key_spec['right_table_num']) {
                                                $vs_join .= ' AND ' . $vs_left_table . '.' . $va_key_spec['right_table_num'] . ' = ' . $this->opo_datamodel->getTableNum($vs_right_table);
                                            } else {
                                                $vs_join .= ' AND ' . $vs_right_table . '.' . $va_key_spec['left_table_num'] . ' = ' . $this->opo_datamodel->getTableNum($vs_left_table);
                                            }
                                        }
                                        $vs_join .= ")";
                                    }
                                    if (($t_rel_instance = $this->opo_datamodel->getInstanceByTableName($vs_right_table, true)) && method_exists($t_rel_instance, "isRelationship") && $t_rel_instance->isRelationship() && $t_rel_instance->hasField('type_id')) {
                                        $vs_rel_type_id_fld = "{$vs_right_table}.type_id";
                                    }
                                    $va_joins[] = $vs_join;
                                } else {
                                    if ($va_rel = $this->opo_datamodel->getOneToManyRelations($vs_left_table, $vs_right_table)) {
                                        $va_joins[] = 'INNER JOIN ' . $va_rel['many_table'] . ' ON ' . $va_rel['one_table'] . '.' . $va_rel['one_table_field'] . ' = ' . $va_rel['many_table'] . '.' . $va_rel['many_table_field'];
                                        if (($t_rel_instance = $this->opo_datamodel->getInstanceByTableName($va_rel['many_table'], true)) && method_exists($t_rel_instance, "isRelationship") && $t_rel_instance->isRelationship() && $t_rel_instance->hasField('type_id')) {
                                            $vs_rel_type_id_fld = "{$vs_right_table}.type_id";
                                        }
                                    } else {
                                        if ($va_rel = $this->opo_datamodel->getOneToManyRelations($vs_right_table, $vs_left_table)) {
                                            $va_joins[] = 'INNER JOIN ' . $va_rel['one_table'] . ' ON ' . $va_rel['one_table'] . '.' . $va_rel['one_table_field'] . ' = ' . $va_rel['many_table'] . '.' . $va_rel['many_table_field'];
                                            if (($t_rel_instance = $this->opo_datamodel->getInstanceByTableName($va_rel['one_table'], true)) && method_exists($t_rel_instance, "isRelationship") && $t_rel_instance->isRelationship() && $t_rel_instance->hasField('type_id')) {
                                                $vs_rel_type_id_fld = "{$vs_right_table}.type_id";
                                            }
                                        }
                                    }
                                }
                                $vs_left_table = $vs_right_table;
                            }
                            $va_proc_field_list = array();
                            $vn_field_list_count = sizeof($va_field_list);
                            for ($vn_i = 0; $vn_i < $vn_field_list_count; $vn_i++) {
                                if ($va_field_list[$vn_i] == '_count') {
                                    continue;
                                }
                                if (substr($va_field_list[$vn_i], 0, 14) === '_ca_attribute_') {
                                    continue;
                                }
                                if (!trim($va_field_list[$vn_i])) {
                                    continue;
                                }
                                $va_proc_field_list[$vn_i] = $vs_related_table . '.' . $va_field_list[$vn_i];
                            }
                            $va_proc_field_list[] = $vs_related_table . '.' . $vs_related_pk;
                            if ($vs_rel_type_id_fld) {
                                $va_proc_field_list[] = $vs_rel_type_id_fld . ' rel_type_id';
                            }
                            if (isset($va_rel['many_table']) && $va_rel['many_table']) {
                                $va_proc_field_list[] = $va_rel['many_table'] . '.' . $va_rel['many_table_field'];
                            }
                            $vs_sql = "\n\t\t\t\t\t\t\tSELECT " . join(",", $va_proc_field_list) . "\n\t\t\t\t\t\t\tFROM " . $vs_subject_tablename . "\n\t\t\t\t\t\t\t" . join("\n", $va_joins) . "\n\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t(" . $vs_subject_tablename . '.' . $vs_subject_pk . ' = ?)
						';
                            $va_params = array($pn_subject_row_id);
                            $va_queries[] = array('sql' => $vs_sql, 'params' => $va_params);
                        }
                    }
                    foreach ($va_queries as $va_query) {
                        $vs_sql = $va_query['sql'];
                        $va_params = $va_query['params'];
                        $qr_res = $this->opo_db->query($vs_sql, $va_params);
                        if ($this->opo_db->numErrors()) {
                            // Shouldn't ever happen
                            throw new Exception(_t("SQL error while getting content for index of related fields: %1; SQL was %2", $this->opo_db->getErrors(), $vs_sql));
                        }
                        if (method_exists($t_rel, "getApplicableElementCodes")) {
                            if (is_array($va_element_ids = array_keys($t_rel->getApplicableElementCodes(null, false, false))) && sizeof($va_element_ids)) {
                                $va_rel_row_ids = $qr_res->getAllFieldValues($vs_related_pk);
                                if (sizeof($va_rel_row_ids) > 0) {
                                    ca_attributes::prefetchAttributes($this->opo_db, $vn_related_tablenum, $va_rel_row_ids, $va_element_ids);
                                }
                            }
                        }
                        if (!$qr_res->seek(0)) {
                            $qr_res = $this->opo_db->query($vs_sql, $va_params);
                        }
                        while ($qr_res->nextRow()) {
                            $va_field_data = $qr_res->getRow();
                            $vn_row_id = $qr_res->get($vs_related_pk);
                            $vn_rel_type_id = $qr_res->get('rel_type_id');
                            foreach ($va_fields_to_index as $vs_rel_field => $va_rel_field_info) {
                                //
                                // BEGIN: Index attributes in related tables
                                //
                                $vb_is_attr = false;
                                if (substr($vs_rel_field, 0, 14) === '_ca_attribute_') {
                                    if (!preg_match('!^_ca_attribute_(.*)$!', $vs_rel_field, $va_matches)) {
                                        continue;
                                    }
                                    if ($va_rel_field_info['DONT_INDEX'] && is_array($va_rel_field_info['DONT_INDEX'])) {
                                        $vb_cont = false;
                                        foreach ($va_rel_field_info["DONT_INDEX"] as $vs_exclude_type) {
                                            if ($this->_getElementID($vs_exclude_type) == intval($va_matches[1])) {
                                                $vb_cont = true;
                                                break;
                                            }
                                        }
                                        if ($vb_cont) {
                                            continue;
                                        }
                                        // skip excluded attribute type
                                    }
                                    $vb_is_attr = true;
                                    $va_rel_field_info['datatype'] = (int) $this->_getElementDataType($va_matches[1]);
                                    $this->_indexAttribute($t_rel, $vn_row_id, $va_matches[1], array_merge($va_rel_field_info, array('relationship_type_id' => $vn_rel_type_id)));
                                }
                                $vs_fld_data = trim($va_field_data[$vs_rel_field]);
                                //
                                // Hierarchical indexing in related tables
                                //
                                if (isset($va_rel_field_info['INDEX_ANCESTORS']) && $va_rel_field_info['INDEX_ANCESTORS'] || in_array('INDEX_ANCESTORS', $va_rel_field_info)) {
                                    // is this current field a label?
                                    $t_hier_rel = $t_rel;
                                    $vn_fld_num = $t_rel->fieldNum($vs_rel_field);
                                    $vn_id = $vn_row_id;
                                    if ($t_hier_rel && ($t_hier_rel->isHierarchical() || is_subclass_of($t_hier_rel, "BaseLabel"))) {
                                        // get hierarchy
                                        if ($va_hier_values = $this->_genHierarchicalPath($vn_id, $vs_rel_field, $t_hier_rel, $va_rel_field_info)) {
                                            $this->opo_engine->indexField($vn_related_tablenum, 'I' . $vn_fld_num, $vn_id, $vs_fld_data . ' ' . join(" ", $va_hier_values['values']), array_merge($va_rel_field_info, array('relationship_type_id' => $vn_rel_type_id)));
                                            if (caGetOption('INDEX_ANCESTORS_AS_PATH_WITH_DELIMITER', $va_rel_field_info, false) !== false) {
                                                $this->opo_engine->indexField($vn_related_tablenum, 'I' . $vn_fld_num, $vn_id, $va_hier_values['path'], array_merge($va_rel_field_info, array('DONT_TOKENIZE' => 1, 'relationship_type_id' => $vn_rel_type_id)));
                                            }
                                        }
                                        continue;
                                    }
                                }
                                switch ($vs_rel_field) {
                                    case '_count':
                                        // noop
                                        break;
                                    default:
                                        if ($vb_is_attr) {
                                            $this->opo_engine->indexField($vn_related_tablenum, 'A' . $va_matches[1], $qr_res->get($vs_related_pk), $vs_fld_data, array_merge($va_rel_field_info, array('relationship_type_id' => $vn_rel_type_id)));
                                        } else {
                                            if ((isset($va_rel_field_info['INDEX_AS_IDNO']) && $va_rel_field_info['INDEX_AS_IDNO'] || in_array('INDEX_AS_IDNO', $va_rel_field_info)) && method_exists($t_rel, "getIDNoPlugInInstance") && ($o_idno = $t_rel->getIDNoPlugInInstance())) {
                                                // specialized identifier (idno) processing; used IDNumbering plugin to generate searchable permutations of identifier
                                                $va_values = $o_idno->getIndexValues($vs_fld_data);
                                                $this->opo_engine->indexField($vn_related_tablenum, 'I' . $this->opo_datamodel->getFieldNum($vs_related_table, $vs_rel_field), $qr_res->get($vs_related_pk), join(" ", $va_values), array_merge($va_rel_field_info, array('relationship_type_id' => $vn_rel_type_id)));
                                            } else {
                                                // regular intrinsic
                                                $this->opo_engine->indexField($vn_related_tablenum, 'I' . $this->opo_datamodel->getFieldNum($vs_related_table, $vs_rel_field), $qr_res->get($vs_related_pk), $vs_fld_data, array_merge($va_rel_field_info, array('relationship_type_id' => $vn_rel_type_id)));
                                            }
                                        }
                                        break;
                                }
                                //
                                // END: Index attributes in related tables
                                //							}
                            }
                            // index label for self-relation?
                            if ($vs_subject_tablename == $vs_related_table) {
                                if ($t_label = $t_rel->getLabelTableInstance()) {
                                    $t_label->setDb($this->getDb());
                                    $va_label_info = $this->getTableIndexingInfo($vs_subject_tablename, $t_label->tableName());
                                    if (is_array($va_label_info['related']['fields']) && sizeof($va_label_info['related']['fields'])) {
                                        $vn_label_table_num = $t_label->tableNum();
                                        if (is_array($va_labels = $t_rel->getPreferredLabels(null, false, array('row_id' => $vn_row_id)))) {
                                            foreach ($va_labels as $vn_label_id => $va_labels_by_locale) {
                                                foreach ($va_labels_by_locale as $vn_locale_id => $va_label_list) {
                                                    foreach ($va_label_list as $va_label) {
                                                        foreach ($va_label_info['related']['fields'] as $vs_label_field => $va_config) {
                                                            $this->opo_engine->indexField($vn_label_table_num, 'I' . $this->opo_datamodel->getFieldNum($vn_label_table_num, $vs_label_field), $vn_row_id, $va_label[$vs_label_field], array_merge($va_config, array('relationship_type_id' => $vn_rel_type_id)));
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        // save indexing on subject
        if ($vb_started_indexing) {
            $this->opo_engine->commitRowIndexing();
        }
        if (!$vb_initial_reindex_mode && sizeof($pa_changed_fields) > 0) {
            //
            // When not reindexing then we consider the effect of the change on this row upon related rows that use it
            // in their indexing. This means figuring out which related tables have indexing that depend upon the subject row.
            //
            // We deal with this by pulling up a dependency map generated from the search_indexing.conf file and then reindexing
            // those rows
            //
            $va_deps = $this->getDependencies($vs_subject_tablename);
            $va_changed_field_nums = array();
            foreach (array_keys($pa_changed_fields) as $vs_f) {
                if ($t_subject->hasField($vs_f)) {
                    $va_changed_field_nums[$vs_f] = 'I' . $t_subject->fieldNum($vs_f);
                } else {
                    if (preg_match('!^_ca_attribute_([\\d]+)$!', $vs_f, $va_matches)) {
                        $va_changed_field_nums[$vs_f] = 'A' . $this->_getElementListCode($va_matches[1]);
                    }
                }
            }
            //
            // reindex rows in dependent tables that use the subject_row_id
            //
            $va_rows_to_reindex = $this->_getDependentRowsForSubject($pn_subject_tablenum, $pn_subject_row_id, $va_deps, $va_changed_field_nums);
            if ($vb_can_do_incremental_indexing) {
                $va_rows_to_reindex_by_row_id = array();
                $va_row_ids_to_reindex_by_table = array();
                foreach ($va_rows_to_reindex as $vs_key => $va_row_to_reindex) {
                    foreach ($va_row_to_reindex['field_nums'] as $vs_fld_name => $vn_fld_num) {
                        $vs_new_key = $va_row_to_reindex['table_num'] . '/' . $va_row_to_reindex['field_table_num'] . '/' . $vn_fld_num . '/' . $va_row_to_reindex['field_row_id'];
                        if (!isset($va_rows_to_reindex_by_row_id[$vs_new_key])) {
                            $va_rows_to_reindex_by_row_id[$vs_new_key] = array('table_num' => $va_row_to_reindex['table_num'], 'row_ids' => array(), 'field_table_num' => $va_row_to_reindex['field_table_num'], 'field_num' => $vn_fld_num, 'field_name' => $vs_fld_name, 'field_row_id' => $va_row_to_reindex['field_row_id'], 'field_values' => $va_row_to_reindex['field_values'], 'relationship_type_id' => $va_row_to_reindex['relationship_type_id'], 'indexing_info' => $va_row_to_reindex['indexing_info'][$vs_fld_name]);
                        }
                        $va_rows_to_reindex_by_row_id[$vs_new_key]['row_ids'][] = $va_row_to_reindex['row_id'];
                        $va_row_ids_to_reindex_by_table[$va_row_to_reindex['field_table_num']][] = $va_row_to_reindex['field_row_id'];
                    }
                }
                foreach ($va_row_ids_to_reindex_by_table as $vn_rel_tablenum => $va_rel_row_ids) {
                    $va_rel_row_ids = array_unique($va_rel_row_ids);
                    if ($t_rel = $this->opo_datamodel->getInstanceByTableNum($vn_rel_tablenum, true)) {
                        $t_rel->setDb($this->getDb());
                        if (method_exists($t_rel, "getApplicableElementCodes")) {
                            if (is_array($va_element_ids = array_keys($t_rel->getApplicableElementCodes(null, false, false))) && sizeof($va_element_ids)) {
                                ca_attributes::prefetchAttributes($this->opo_db, $vn_rel_tablenum, $va_rel_row_ids, $va_element_ids);
                            }
                        }
                    }
                }
                $o_indexer = new SearchIndexer($this->opo_db);
                foreach ($va_rows_to_reindex_by_row_id as $va_row_to_reindex) {
                    $vn_rel_type_id = $va_row_to_reindex['relationship_type_id'];
                    $t_rel = $this->opo_datamodel->getInstanceByTableNum($va_row_to_reindex['field_table_num'], true);
                    $t_rel->setDb($this->getDb());
                    if (substr($va_row_to_reindex['field_name'], 0, 14) == '_ca_attribute_') {
                        // is attribute
                        $va_row_to_reindex['indexing_info']['datatype'] = $this->_getElementDataType(substr($va_row_to_reindex['field_name'], 14));
                    }
                    if (isset($va_row_to_reindex['indexing_info']['INDEX_ANCESTORS']) && $va_row_to_reindex['indexing_info']['INDEX_ANCESTORS'] || in_array('INDEX_ANCESTORS', $va_row_to_reindex['indexing_info'])) {
                        if (!is_array($va_row_to_reindex['row_ids'])) {
                            continue;
                        }
                        $t_label = $this->opo_datamodel->getInstanceByTableNum($va_row_to_reindex['field_table_num'], true);
                        $t_label->setDb($this->getDb());
                        foreach ($va_row_to_reindex['row_ids'] as $vn_row_id) {
                            $va_content = $this->_genHierarchicalPath($va_row_to_reindex['field_row_id'], $va_row_to_reindex['field_name'], $t_label, $va_row_to_reindex['indexing_info']);
                            $vs_content = is_array($va_content['values']) ? join(" ", $va_content['values']) : "";
                            $this->opo_engine->updateIndexingInPlace($va_row_to_reindex['table_num'], array($vn_row_id), $va_row_to_reindex['field_table_num'], $va_row_to_reindex['field_num'], $va_row_to_reindex['field_row_id'], $vs_content, array_merge($va_row_to_reindex['indexing_info'], array('relationship_type_id' => $vn_rel_type_id, 'literalContent' => $va_content['path'])));
                        }
                    } else {
                        $vs_element_code = substr($va_row_to_reindex['field_name'], 14);
                        if (isset($va_row_to_reindex['indexing_info']['datatype'])) {
                            $vs_v = '';
                            switch ($va_row_to_reindex['indexing_info']['datatype']) {
                                case __CA_ATTRIBUTE_VALUE_CONTAINER__:
                                    // container
                                    // index components of complex multi-value attributes
                                    foreach ($va_row_to_reindex['row_ids'] as $vn_rel_row_id) {
                                        $this->opo_engine->startRowIndexing($va_row_to_reindex['table_num'], $vn_rel_row_id);
                                        $va_attributes = $t_rel->getAttributesByElement($vs_element_code, array('row_id' => $va_row_to_reindex['field_row_id']));
                                        if (sizeof($va_attributes)) {
                                            foreach ($va_attributes as $vo_attribute) {
                                                foreach ($vo_attribute->getValues() as $vo_value) {
                                                    $vn_list_id = $this->_getElementListID($vo_value->getElementID());
                                                    $vs_value_to_index = $vo_value->getDisplayValue($vn_list_id);
                                                    $va_additional_indexing = $vo_value->getDataForSearchIndexing();
                                                    if (is_array($va_additional_indexing) && sizeof($va_additional_indexing) > 0) {
                                                        foreach ($va_additional_indexing as $vs_additional_value) {
                                                            $vs_value_to_index .= " ; " . $vs_additional_value;
                                                        }
                                                    }
                                                    $this->opo_engine->indexField($va_row_to_reindex['table_num'], 'A' . $vo_value->getElementID(), $va_row_to_reindex['field_row_id'], $vs_value_to_index, array_merge($va_row_to_reindex['indexing_info'], array('relationship_type_id' => $vn_rel_type_id)));
                                                }
                                            }
                                        } else {
                                            // we are deleting a container so cleanup existing sub-values
                                            $va_sub_elements = $this->opo_metadata_element->getElementsInSet($vs_element_code);
                                            foreach ($va_sub_elements as $vn_i => $va_element_info) {
                                                $this->opo_engine->indexField($va_row_to_reindex['table_num'], 'A' . $va_element_info['element_id'], $va_row_to_reindex['field_row_id'], '', array_merge($va_row_to_reindex['indexing_info'], array('relationship_type_id' => $vn_rel_type_id)));
                                            }
                                        }
                                        $this->opo_engine->commitRowIndexing();
                                    }
                                    break;
                                case __CA_ATTRIBUTE_VALUE_LIST__:
                                    // list
                                    $va_tmp = array();
                                    if (is_array($va_attributes = $t_rel->getAttributesByElement($vs_element_code, array('row_id' => $va_row_to_reindex['field_row_id'])))) {
                                        foreach ($va_attributes as $vo_attribute) {
                                            foreach ($vo_attribute->getValues() as $vo_value) {
                                                $va_tmp[$vo_attribute->getAttributeID()] = $vo_value->getDisplayValue();
                                            }
                                        }
                                    }
                                    $va_new_values = array();
                                    $t_item = new ca_list_items();
                                    $va_labels = $t_item->getPreferredDisplayLabelsForIDs($va_tmp, array('returnAllLocales' => true));
                                    foreach ($va_labels as $vn_label_row_id => $va_labels_per_row) {
                                        foreach ($va_labels_per_row as $vn_locale_id => $va_label_list) {
                                            foreach ($va_label_list as $vs_label) {
                                                $va_new_values[$vn_label_row_id][$vs_label] = true;
                                            }
                                        }
                                    }
                                    foreach ($va_tmp as $vn_attribute_id => $vn_item_id) {
                                        if (!$vn_item_id) {
                                            continue;
                                        }
                                        if (!isset($va_new_values[$vn_item_id]) || !is_array($va_new_values[$vn_item_id])) {
                                            continue;
                                        }
                                        $vs_v = join(' ;  ', array_merge(array($vn_item_id), array_keys($va_new_values[$vn_item_id])));
                                    }
                                    $this->opo_engine->updateIndexingInPlace($va_row_to_reindex['table_num'], $va_row_to_reindex['row_ids'], $va_row_to_reindex['field_table_num'], $va_row_to_reindex['field_num'], $va_row_to_reindex['field_row_id'], $vs_v, array_merge($va_row_to_reindex['indexing_info'], array('relationship_type_id' => $vn_rel_type_id)));
                                    break;
                                default:
                                    $va_tmp = array();
                                    if (is_array($va_attributes = $t_rel->getAttributesByElement($vs_element_code, array('row_id' => $va_row_to_reindex['field_row_id'])))) {
                                        foreach ($va_attributes as $vo_attribute) {
                                            foreach ($vo_attribute->getValues() as $vo_value) {
                                                $vs_value_to_index = $vo_value->getDisplayValue($vn_list_id);
                                                $va_additional_indexing = $vo_value->getDataForSearchIndexing();
                                                if (is_array($va_additional_indexing) && sizeof($va_additional_indexing) > 0) {
                                                    foreach ($va_additional_indexing as $vs_additional_value) {
                                                        $vs_value_to_index .= " ; " . $vs_additional_value;
                                                    }
                                                }
                                                $va_tmp[$vo_attribute->getAttributeID()] = $vs_value_to_index;
                                            }
                                        }
                                    }
                                    foreach ($va_tmp as $vn_attribute_id => $vn_item_id) {
                                        if (!$vn_item_id) {
                                            continue;
                                        }
                                        $vs_v = join(' ;  ', $va_tmp);
                                    }
                                    $this->opo_engine->updateIndexingInPlace($va_row_to_reindex['table_num'], $va_row_to_reindex['row_ids'], $va_row_to_reindex['field_table_num'], $va_row_to_reindex['field_num'], $va_row_to_reindex['field_row_id'], $vs_v, array_merge($va_row_to_reindex['indexing_info'], array('relationship_type_id' => $vn_rel_type_id)));
                                    break;
                            }
                        } else {
                            $this->opo_engine->updateIndexingInPlace($va_row_to_reindex['table_num'], $va_row_to_reindex['row_ids'], $va_row_to_reindex['field_table_num'], $va_row_to_reindex['field_num'], $va_row_to_reindex['field_row_id'], $va_row_to_reindex['field_values'][$va_row_to_reindex['field_name']], array_merge($va_row_to_reindex['indexing_info'], array('relationship_type_id' => $vn_rel_type_id)));
                        }
                    }
                }
            } else {
                //
                // If the underlying engine doesn't support incremental indexing then
                // we fall back to reindexing each dependenting row completely and independently.
                // This can be *really* slow for subjects with many dependent rows (for example, a ca_list_item value used as a type for many ca_objects rows)
                // and we need to think about how to optimize this for such engines; ultimately since no matter how you slice it in such
                // engines you're going to have a lot of reindexing going on, we may just have to construct a facility to handle large
                // indexing tasks in a separate process when the number of dependent rows exceeds a certain threshold
                //
                $o_indexer = new SearchIndexer($this->opo_db);
                $t_dep = null;
                $va_rows_seen = array();
                foreach ($va_rows_to_reindex as $va_row_to_reindex) {
                    if (isset($va_rows_seen[$va_row_to_reindex['table_num']][$va_row_to_reindex['row_id']])) {
                        continue;
                    }
                    if (!$t_dep || $t_dep->tableNum() != $va_row_to_reindex['table_num']) {
                        $t_dep = $this->opo_datamodel->getInstanceByTableNum($va_row_to_reindex['table_num']);
                    }
                    $vb_support_attributes = is_subclass_of($t_dep, 'BaseModelWithAttributes') ? true : false;
                    if (is_array($pa_exclusion_list[$va_row_to_reindex['table_num']]) && isset($pa_exclusion_list[$va_row_to_reindex['table_num']][$va_row_to_reindex['row_id']])) {
                        continue;
                    }
                    // trigger reindexing
                    $this->opo_engine->removeRowIndexing($va_row_to_reindex['table_num'], $va_row_to_reindex['row_id']);
                    if ($vb_support_attributes) {
                        if ($t_dep->load($va_row_to_reindex['row_id'])) {
                            //
                            $o_indexer->indexRow($va_row_to_reindex['table_num'], $va_row_to_reindex['row_id'], $t_dep->getFieldValuesArray(), true, $pa_exclusion_list);
                        }
                    } else {
                        $o_indexer->indexRow($va_row_to_reindex['table_num'], $va_row_to_reindex['row_id'], $va_row_to_reindex['field_values'], true, $pa_exclusion_list);
                    }
                    $va_rows_seen[$va_row_to_reindex['table_num']][$va_row_to_reindex['row_id']] = true;
                }
                $o_indexer = null;
            }
        }
        if ($vb_reindex_children && method_exists($t_subject, "makeSearchResult")) {
            //
            // Force reindexing of children of this record, typically because the record has shifted location in the hierarchy and is hierarchically indexed
            //
            $va_children_ids = $t_subject->getHierarchyAsList($pn_subject_row_id, array('idsOnly' => true));
            if (is_array($va_children_ids) && sizeof($va_children_ids) > 0) {
                // trigger reindexing of children
                $o_indexer = new SearchIndexer($this->opo_db);
                $qr_children_res = $t_subject->makeSearchResult($vs_subject_tablename, $va_children_ids, array('db' => $this->getDb()));
                while ($qr_children_res->nextHit()) {
                    $o_indexer->indexRow($pn_subject_tablenum, $vn_id = $qr_children_res->get($vs_subject_pk), array($vs_subject_pk => $vn_id, 'parent_id' => $qr_children_res->get('parent_id')), true, $pa_exclusion_list, array(), null);
                }
            }
        }
    }
 $vn_c = 0;
 $qr_res->seek($vn_start);
 $va_ids = array();
 while ($qr_res->nextHit() && $vn_c < $vn_hits_per_block) {
     $va_ids[] = $qr_res->get("{$vs_table}.{$vs_pk}");
     $vn_c++;
 }
 $qr_res->seek($vn_start);
 $vn_c = 0;
 if ($vs_table != 'ca_objects') {
     $va_images = caGetDisplayImagesForAuthorityItems($vs_table, $va_ids, array('version' => 'small', 'relationshipTypes' => caGetOption('selectMediaUsingRelationshipTypes', $va_options, null), 'checkAccess' => $va_access_values));
 } else {
     $va_images = null;
 }
 $va_element_ids = array_keys($t_instance->getApplicableElementCodes(null, false, false));
 ca_attributes::prefetchAttributes($t_instance->getDb(), $t_instance->tableNum(), $va_ids, $va_element_ids);
 $vs_add_to_lightbox_msg = addslashes(_t('Add to lightbox'));
 $t_list_item = new ca_list_items();
 $va_locality_cache = $va_taxonomy_cache = array();
 while ($qr_res->nextHit() && $vn_c < $vn_hits_per_block) {
     $vn_id = $qr_res->get("{$vs_table}.{$vs_pk}");
     $vs_idno = $qr_res->get('ca_objects.idno');
     $vn_type_id = $qr_res->get('ca_objects.type_id');
     $vs_image = $vs_table === 'ca_objects' ? $qr_res->getMediaTag("ca_object_representations.media", 'small', array("checkAccess" => $va_access_values)) : $va_images[$vn_id];
     if (!$vs_image && !in_array($vn_type_id, $va_vertebrate_type_ids)) {
         $vs_image = $vs_placeholder_tag;
     }
     if ($vs_image) {
         $vs_rep_detail_link = caDetailLink($this->request, $vs_image, '', $vs_table, $vn_id, array("subsite" => $this->request->session->getVar("coloradoSubSite")));
     }
     print "<div class='bResultListItemCol col-xs-12 col-sm-12 col-md-12'>";
Example #4
0
 /**
  * Retrieves attribute value for a list of rows
  *
  * @return array Array of values indexed on row_id, then locale_id and finally an index (to accommodate repeating values)
  */
 public static function getAttributeValueForIDs($po_db, $pn_table_num, $pa_row_ids, $pn_element_id, $pa_options = null)
 {
     $vb_is_cached = true;
     foreach ($pa_row_ids as $vn_row_id) {
         if (!is_array(ca_attributes::$s_get_attributes_cache[$pn_table_num . '/' . $vn_row_id][$pn_element_id])) {
             $vb_is_cached = false;
             break;
         }
     }
     if (!$vb_is_cached) {
         if (!ca_attributes::prefetchAttributes($po_db, $pn_table_num, $pa_row_ids, array($pn_element_id))) {
             return null;
         }
     }
     $va_values = array();
     foreach ($pa_row_ids as $vn_i => $vn_row_id) {
         foreach (ca_attributes::$s_get_attributes_cache[$pn_table_num . '/' . $vn_row_id] as $va_elements) {
             foreach ($va_elements as $vn_j => $o_attr) {
                 if ((int) $o_attr->getElementID() === (int) $pn_element_id) {
                     $va_attr_values = $o_attr->getValues();
                     $vn_locale_id = $o_attr->getLocaleID();
                     foreach ($va_attr_values as $va_attr_value) {
                         $va_values[$vn_row_id][$vn_locale_id][] = $va_attr_value->getDisplayValue();
                     }
                 }
             }
         }
     }
     return $va_values;
 }
Example #5
0
 /**
  * Implementation of primary get() functionality
  */
 private function _get($ps_field, $pa_options = null)
 {
     if (!is_array($pa_options)) {
         $pa_options = array();
     }
     if (isset($pa_options['restrictToType']) && (!isset($pa_options['restrict_to_type']) || !$pa_options['restrict_to_type'])) {
         $pa_options['restrict_to_type'] = $pa_options['restrictToType'];
     }
     if (isset($pa_options['restrictToTypes']) && (!isset($pa_options['restrict_to_types']) || !$pa_options['restrict_to_types'])) {
         $pa_options['restrict_to_types'] = $pa_options['restrictToTypes'];
     }
     if (isset($pa_options['restrictToRelationshipTypes']) && (!isset($pa_options['restrict_to_relationship_types']) || !$pa_options['restrict_to_relationship_types'])) {
         $pa_options['restrict_to_relationship_types'] = $pa_options['restrictToRelationshipTypes'];
     }
     if (isset($pa_options['excludeType']) && (!isset($pa_options['exclude_type']) || !$pa_options['exclude_type'])) {
         $pa_options['exclude_type'] = $pa_options['excludeType'];
     }
     if (isset($pa_options['excludeTypes']) && (!isset($pa_options['exclude_types']) || !$pa_options['exclude_types'])) {
         $pa_options['exclude_types'] = $pa_options['excludeTypes'];
     }
     if (isset($pa_options['excludeRelationshipTypes']) && (!isset($pa_options['exclude_relationship_types']) || !$pa_options['exclude_relationship_types'])) {
         $pa_options['exclude_relationship_types'] = $pa_options['excludeRelationshipTypes'];
     }
     $vb_return_as_array = caGetOption('returnAsArray', $pa_options, false, array('castTo' => 'bool'));
     $vb_return_all_locales = caGetOption('returnAllLocales', $pa_options, false, array('castTo' => 'bool'));
     $vb_return_as_link = caGetOption('returnAsLink', $pa_options, false, array('castTo' => 'bool'));
     $vs_return_as_link_text = caGetOption('returnAsLinkText', $pa_options, '');
     $vs_return_as_link_target = caGetOption('returnAsLinkTarget', $pa_options, '');
     $vs_return_as_link_attributes = caGetOption('returnAsLinkAttributes', $pa_options, array(), array('castTo' => 'array'));
     $va_original_path_components = $va_path_components = $this->getFieldPathComponents($ps_field);
     if ($va_path_components['table_name'] != $this->ops_table_name) {
         $vs_access_chk_key = $va_path_components['table_name'] . ($va_path_components['field_name'] ? '.' . $va_path_components['field_name'] : '');
     } else {
         $vs_access_chk_key = $va_path_components['field_name'];
     }
     if (caGetBundleAccessLevel($this->ops_table_name, $vs_access_chk_key) == __CA_BUNDLE_ACCESS_NONE__) {
         return null;
     }
     $vo_request = caGetOption('request', $pa_options, null);
     unset($pa_options['request']);
     // first see if the search engine can provide the field value directly (fastest)
     if (!(($vs_value = $this->opo_engine_result->get($ps_field, $pa_options)) === false)) {
         if ($vb_return_as_array) {
             if ($vb_return_all_locales) {
                 return array(1 => $vs_value);
             } else {
                 return array($vs_value);
             }
         } else {
             return $vs_value;
         }
     }
     $vs_template = caGetOption('template', $pa_options, null);
     $vs_delimiter = caGetOption('delimiter', $pa_options, ' ');
     $vs_hierarchical_delimiter = caGetOption('hierarchicalDelimiter', $pa_options, ' ');
     if ($vb_return_all_locales && !$vb_return_as_array) {
         $vb_return_as_array = true;
     }
     if (isset($pa_options['sort']) && !is_array($pa_options['sort'])) {
         $pa_options['sort'] = array($pa_options['sort']);
     }
     if (is_array($va_sort_fields = isset($pa_options['sort']) && is_array($pa_options['sort']) ? $pa_options['sort'] : null)) {
         foreach ($va_sort_fields as $vn_i => $vs_sort_fld) {
             if (!trim($vs_sort_fld)) {
                 unset($va_sort_fields[$vn_i]);
             }
         }
     }
     $vn_row_id = $this->opo_engine_result->get($this->ops_table_pk);
     // try to lazy load (slower)...
     //
     // Are we getting timestamp (created on or last modified) info?
     //
     if ($va_path_components['table_name'] == $this->ops_table_name && $va_path_components['field_name'] == 'created') {
         if (!isset($this->opa_timestamp_cache['created_on'][$this->ops_table_name][$vn_row_id])) {
             $this->prefetchChangeLogData($this->ops_table_name, $this->opo_engine_result->currentRow(), $this->getOption('prefetch'));
         }
         if ($vb_return_as_array) {
             return $this->opa_timestamp_cache['created_on'][$this->ops_table_name][$vn_row_id];
         } else {
             $vs_subfield = $va_path_components['subfield_name'] ? $va_path_components['subfield_name'] : 'timestamp';
             $vm_val = $this->opa_timestamp_cache['created_on'][$this->ops_table_name][$vn_row_id][$vs_subfield];
             if ($vs_subfield == 'timestamp') {
                 $o_tep = new TimeExpressionParser();
                 $o_tep->setUnixTimestamps($vm_val, $vm_val);
                 $vm_val = $o_tep->getText($pa_options);
             }
             return $vm_val;
         }
     }
     if ($va_path_components['table_name'] == $this->ops_table_name && $va_path_components['field_name'] == 'lastModified') {
         if (!isset($this->opa_timestamp_cache['last_changed'][$this->ops_table_name][$vn_row_id])) {
             $this->prefetchChangeLogData($this->ops_table_name, $this->opo_engine_result->currentRow(), $this->getOption('prefetch'));
         }
         if ($vb_return_as_array) {
             return $this->opa_timestamp_cache['last_changed'][$this->ops_table_name][$vn_row_id];
         } else {
             $vs_subfield = $va_path_components['subfield_name'] ? $va_path_components['subfield_name'] : 'timestamp';
             $vm_val = $this->opa_timestamp_cache['last_changed'][$this->ops_table_name][$vn_row_id][$vs_subfield];
             if ($vs_subfield == 'timestamp') {
                 $o_tep = new TimeExpressionParser();
                 $o_tep->setUnixTimestamps($vm_val, $vm_val);
                 $vm_val = $o_tep->getText($pa_options);
             }
             return $vm_val;
         }
     }
     if (!($t_instance = $this->opo_datamodel->getInstanceByTableName($va_path_components['table_name'], true))) {
         return null;
     }
     // Bad table
     $t_original_instance = $t_instance;
     // $t_original_instance will always be the as-called subject; optimizations may results in $t_instance being transformed into a different model
     //
     // Simple related table get:
     //			<table>
     //			<table>.related
     //			<table>.hierarchy
     //			<table>.related.hierarchy
     //
     if ($va_path_components['num_components'] == 1 && $va_path_components['table_name'] !== $this->ops_table_name || $va_path_components['num_components'] == 2 && $va_path_components['field_name'] == 'related' || $va_path_components['num_components'] == 2 && $va_path_components['field_name'] == 'hierarchy' || $va_path_components['num_components'] == 3 && $va_path_components['field_name'] == 'related' && $va_path_components['subfield_name'] == 'hierarchy') {
         if (!($t_table = $this->opo_datamodel->getInstanceByTableName($this->ops_table_name, true))) {
             return null;
         }
         $vb_show_hierarachy = (bool) ($va_path_components['field_name'] == 'hierarchy' && $t_instance->isHierarchical());
         if ($va_path_components['num_components'] == 2) {
             $va_path_components['num_components'] = 1;
             $va_path_components['field_name'] = null;
         }
         $vs_opt_md5 = caMakeCacheKeyFromOptions($pa_options);
         if (!isset($this->opa_rel_prefetch_cache[$va_path_components['table_name']][$vn_row_id][$vs_opt_md5])) {
             $this->prefetchRelated($va_path_components['table_name'], $this->opo_engine_result->currentRow(), $this->getOption('prefetch'), $pa_options);
         }
         $va_related_items = $this->opa_rel_prefetch_cache[$va_path_components['table_name']][$vn_row_id][$vs_opt_md5];
         if (!is_array($va_related_items)) {
             return null;
         }
         if (is_array($va_sort_fields) && sizeof($va_sort_fields)) {
             $va_related_items = caSortArrayByKeyInValue($va_related_items, $va_sort_fields);
         }
         // Return as array
         if ($vs_template) {
             return caProcessTemplateForIDs($vs_template, $this->opo_subject_instance->tableName(), array($vn_row_id), array_merge($pa_options, array('placeholderPrefix' => $va_path_components['field_name'])));
         }
         if ($vb_return_as_array || $vb_return_all_locales) {
             if ($vb_return_all_locales) {
                 $va_related_tmp = array();
                 foreach ($va_related_items as $vn_i => $va_related_item) {
                     $va_related_tmp[$vn_i][$va_related_item['locale_id']] = $va_related_item;
                 }
                 return $va_related_tmp;
             } else {
                 if (!$vs_template && !$va_path_components['field_name']) {
                     return $va_related_items;
                 }
                 $vs_pk = $t_instance->primaryKey();
                 $va_links = array();
                 foreach ($va_related_items as $vn_relation_id => $va_relation_info) {
                     $va_relation_info['labels'] = caExtractValuesByUserLocale(array(0 => $va_relation_info['labels']));
                     if ($vb_return_as_link) {
                         $va_template_opts = array();
                         $va_template_opts['relationshipValues'][$va_relation_info[$vs_pk]][$va_relation_info['relation_id']]['relationship_typename'] = $va_relation_info['relationship_typename'];
                         $vs_text = $vs_template ? caProcessTemplateForIDs($vs_template, $t_instance->tableName(), array($va_relation_info[$vs_pk]), $va_template_opts) : join("; ", $va_relation_info['labels']);
                         $va_link = caCreateLinksFromText(array($vs_text), $va_original_path_components['table_name'], array($va_relation_info[$vs_pk]), $vs_return_as_link_class, $vs_return_as_link_target);
                         $va_links[$vn_relation_id] = array_pop($va_link);
                     } else {
                         $va_related_items[$vn_relation_id]['labels'] = $va_relation_info['labels'];
                     }
                 }
                 if ($vb_return_as_link) {
                     return $va_links;
                 }
                 return $va_related_items;
             }
         } else {
             // Return scalar
             $va_proc_labels = array();
             $va_row_ids = array();
             $vs_rel_pk = $t_instance->primaryKey();
             $va_relationship_values = array();
             foreach ($va_related_items as $vn_relation_id => $va_relation_info) {
                 $va_row_ids[] = $va_relation_info[$vs_rel_pk];
                 $va_relationship_values[$va_relation_info[$vs_rel_pk]][$vn_relation_id] = array('relationship_typename' => $va_relation_info['relationship_typename'], 'relationship_type_id' => $va_relation_info['relationship_type_id'], 'relationship_type_code' => $va_relation_info['relationship_type_code'], 'relationship_typecode' => $va_relation_info['relationship_type_code'], 'label' => $va_relation_info['label']);
             }
             if (!sizeof($va_row_ids)) {
                 return '';
             }
             if (!$vs_template) {
                 $vs_template = "^label";
             }
             $va_template_opts = $pa_options;
             unset($va_template_opts['request']);
             unset($va_template_opts['template']);
             $va_template_opts['returnAsLink'] = false;
             $va_template_opts['returnAsArray'] = true;
             $va_text = caProcessTemplateForIDs($vs_template, $t_instance->tableNum(), $va_row_ids, array_merge($va_template_opts, array('relationshipValues' => $va_relationship_values, 'showHierarchicalLabels' => $vb_show_hierarachy)));
             if ($vb_return_as_link) {
                 $va_links = caCreateLinksFromText($va_text, $va_original_path_components['table_name'], $va_row_ids, $vs_return_as_link_class, $vs_return_as_link_target);
                 return join($vs_delimiter, $va_links);
             }
             return join($vs_delimiter, $va_text);
         }
     }
     $vb_need_parent = false;
     $vb_need_children = false;
     //
     // Transform "preferred_labels" into tables for pre-fetching
     //
     $vb_is_get_for_labels = $vb_return_all_label_values = $vb_get_preferred_labels_only = $vb_get_nonpreferred_labels_only = false;
     if (in_array($va_path_components['field_name'], array('preferred_labels', 'nonpreferred_labels'))) {
         if ($t_instance->getProperty('LABEL_TABLE_NAME')) {
             $vb_get_preferred_labels_only = $va_path_components['field_name'] == 'preferred_labels' ? true : false;
             $vb_get_nonpreferred_labels_only = $va_path_components['field_name'] == 'nonpreferred_labels' ? true : false;
             if ($va_path_components['num_components'] == 2) {
                 // if it's just <table_name>.preferred_labels then return an array of fields from the label table
                 $vb_return_all_label_values = true;
             }
             $va_path_components['table_name'] = $t_instance->getLabelTableName();
             $t_label_instance = $t_instance->getLabelTableInstance();
             if (!$va_path_components['subfield_name'] || !$t_label_instance->hasField($va_path_components['subfield_name'])) {
                 $va_path_components['field_name'] = $t_instance->getLabelDisplayField();
             } else {
                 $va_path_components['field_name'] = $va_path_components['subfield_name'];
             }
             $va_path_components['subfield_name'] = null;
             $va_path_components = $this->getFieldPathComponents($va_path_components['table_name'] . '.' . $va_path_components['field_name']);
             // Ok, convert the table instance to the label table since that's the table we'll be grabbing data from
             $t_instance = $t_label_instance;
             $vb_is_get_for_labels = true;
         }
     }
     //
     // Handle modifiers (parent, children, related, hierarchy) with and without fields
     //
     if ($va_path_components['num_components'] >= 2) {
         switch ($va_path_components['field_name']) {
             case 'parent':
                 if ($t_instance->isHierarchical() && ($vn_parent_id = $this->get($va_path_components['table_name'] . '.' . $t_instance->getProperty('HIERARCHY_PARENT_ID_FLD')))) {
                     //
                     // TODO: support some kind of prefetching of parents?
                     //
                     unset($va_path_components['components'][1]);
                     if ($t_instance->load($vn_parent_id)) {
                         return $t_instance->get(join('.', array_values($va_path_components['components'])), $pa_options);
                     }
                     return null;
                 }
                 break;
             case 'children':
                 if ($t_instance->isHierarchical()) {
                     //unset($va_path_components['components'][1]);	// remove 'children' from field path
                     $vs_field_spec = join('.', array_values($va_path_components['components']));
                     if ($vn_id = $this->get($va_path_components['table_name'] . '.' . $t_instance->primaryKey(), array('returnAsArray' => false))) {
                         if ($t_instance->load($vn_id)) {
                             return $t_instance->get($vs_field_spec, $pa_options);
                         }
                     }
                     return null;
                 }
                 break;
             case 'related':
                 // Regular related table call
                 if ($va_path_components['table_name'] != $this->ops_table_name) {
                     // just remove "related" from name and be on our way
                     $va_tmp = $va_path_components['components'];
                     array_splice($va_tmp, 1, 1);
                     return $this->get(join('.', $va_tmp), $pa_options);
                 }
                 // Self-relations need special handling
                 $vs_opt_md5 = caMakeCacheKeyFromOptions($pa_options);
                 if (!isset($this->opa_rel_prefetch_cache[$va_path_components['table_name']][$vn_row_id][$vs_opt_md5])) {
                     $this->prefetchRelated($va_path_components['table_name'], $this->opo_engine_result->currentRow(), $this->getOption('prefetch'), $pa_options);
                 }
                 $va_related_items = $this->opa_rel_prefetch_cache[$va_path_components['table_name']][$vn_row_id][$vs_opt_md5];
                 if (!($t_table = $this->opo_datamodel->getInstanceByTableName($va_path_components['table_name'], true))) {
                     return null;
                 }
                 $va_ids = array();
                 foreach ($va_related_items as $vn_relation_id => $va_item) {
                     $va_ids[] = $va_item[$t_table->primaryKey()];
                 }
                 $va_vals = array();
                 if ($qr_res = $t_table->makeSearchResult($va_path_components['table_name'], $va_ids)) {
                     $va_tmp = $va_path_components['components'];
                     unset($va_tmp[1]);
                     $vs_rel_field = join('.', $va_tmp);
                     while ($qr_res->nextHit()) {
                         if ($vb_return_as_array) {
                             $va_vals = array_merge($va_vals, $qr_res->get($vs_rel_field, $pa_options));
                         } else {
                             $va_vals[] = $qr_res->get($vs_rel_field, $pa_options);
                         }
                     }
                 }
                 //if (is_array($va_sort_fields) && sizeof($va_sort_fields)) {
                 //	$va_vals = caSortArrayByKeyInValue($va_vals, $va_sort_fields);
                 //}
                 if ($vb_return_as_link) {
                     if (!$vb_return_all_locales) {
                         $va_vals = caCreateLinksFromText($va_vals, $va_original_path_components['table_name'], $va_ids, $vs_return_as_link_class, $vs_return_as_link_target);
                     }
                 }
                 if ($vb_return_as_array) {
                     return $va_vals;
                 } else {
                     return join($vs_delimiter, $va_vals);
                 }
                 break;
             case 'hierarchy':
                 $vn_max_levels_from_bottom = caGetOption('maxLevelsFromBottom', $pa_options, caGetOption('maxLevels', $pa_options, null));
                 $vn_max_levels_from_top = caGetOption('maxLevelsFromTop', $pa_options, null);
                 if ($t_instance->isHierarchical()) {
                     $vs_field_spec = join('.', array_values($va_path_components['components']));
                     $vs_hier_pk_fld = $t_instance->primaryKey();
                     if ($va_ids = $this->get($va_path_components['table_name'] . '.' . $vs_hier_pk_fld, array_merge($pa_options, array('returnAsArray' => true, 'returnAsLink' => false, 'returnAllLocales' => false)))) {
                         $va_vals = array();
                         if ($va_path_components['subfield_name'] == $vs_hier_pk_fld) {
                             foreach ($va_ids as $vn_id) {
                                 // TODO: This is too slow
                                 if ($t_instance->load($vn_id)) {
                                     $va_vals = array_merge($va_vals, $t_instance->get($va_path_components['table_name'] . ".hierarchy." . $vs_hier_pk_fld, array_merge($pa_options, array('returnAsArray' => true))));
                                 }
                             }
                         } else {
                             foreach ($va_ids as $vn_id) {
                                 // TODO: This is too slow
                                 if ($t_instance->load($vn_id)) {
                                     $va_vals = $t_instance->get($vs_field_spec, array_merge($pa_options, array('returnAsArray' => true)));
                                     if (is_array($va_vals)) {
                                         $va_vals = array_reverse($va_vals);
                                     }
                                     // Add/replace hierarchy name
                                     if ($t_instance->getProperty('HIERARCHY_TYPE') == __CA_HIER_TYPE_MULTI_MONO__ && $t_instance->getHierarchyName()) {
                                         $vn_first_key = array_shift(array_keys($va_vals));
                                         if ($vb_return_all_locales) {
                                             $va_vals[$vn_first_key] = array(0 => array($t_instance->getHierarchyName()));
                                         } else {
                                             $va_vals[$vn_first_key] = $t_instance->getHierarchyName();
                                         }
                                     }
                                     if ($vn_max_levels_from_bottom > 0) {
                                         if (($vn_start = sizeof($va_vals) - $vn_max_levels_from_bottom) < 0) {
                                             $vn_start = 0;
                                         }
                                         $va_vals = array_slice($va_vals, $vn_start, $vn_max_levels_from_bottom, true);
                                     } elseif ($vn_max_levels_from_top > 0) {
                                         $va_vals = array_slice($va_vals, 0, $vn_max_levels_from_top, true);
                                     }
                                 }
                             }
                         }
                         if ($vb_return_as_array) {
                             return $va_vals;
                         } else {
                             return join($vs_hierarchical_delimiter, $va_vals);
                         }
                     }
                     return null;
                 }
                 break;
         }
     }
     // If the requested table was not added to the query via SearchEngine::addTable()
     // then auto-add it here. It's better to explicitly add it with addTables() as that call
     // gives you precise control over which fields are autoloaded and also lets you specify limiting criteria
     // for selection of related field data; and it also lets you explicitly define the tables used to join the
     // related table. Autoloading guesses and usually does what you want, but not always.
     if (!isset($this->opa_tables[$va_path_components['table_name']]) || !$this->opa_tables[$va_path_components['table_name']]) {
         $va_join_tables = $this->opo_datamodel->getPath($this->ops_table_name, $va_path_components['table_name']);
         array_shift($va_join_tables);
         // remove subject table
         array_pop($va_join_tables);
         // remove content table (we only need linking tables here)
         $va_join_criteria = array();
         if (is_array($va_primary_ids)) {
             foreach ($va_primary_ids as $vs_t => $va_t_ids) {
                 if (isset($va_join_tables[$vs_t]) && sizeof($va_t_ids) > 0) {
                     $vs_t_pk = $this->opo_datamodel->getTablePrimaryKeyName($vs_t);
                     $va_join_criteria[] = "{$vs_t}.{$vs_t_pk} NOT IN (" . join(",", $va_t_ids) . ")";
                 }
             }
         }
         $this->opa_tables[$va_path_components['table_name']] = array('fieldList' => array($va_path_components['table_name'] . '.*'), 'joinTables' => array_keys($va_join_tables), 'criteria' => $va_join_criteria);
     }
     if ($va_path_components['table_name'] === $this->ops_table_name && !$t_instance->hasField($va_path_components['field_name']) && method_exists($t_instance, 'getAttributes')) {
         //
         // Return attribute values for primary table
         //
         if ($va_path_components['field_name'] && ($t_element = $t_instance->_getElementInstance($va_path_components['field_name']))) {
             $vn_element_id = $t_element->getPrimaryKey();
         } else {
             $vn_element_id = null;
         }
         if (!isset(ca_attributes::$s_get_attributes_cache[$this->opn_table_num . '/' . $vn_row_id][$vn_element_id])) {
             ca_attributes::prefetchAttributes($this->opo_db, $this->opn_table_num, $this->getRowIDsToPrefetch($va_path_components['table_name'], $this->opo_engine_result->currentRow(), $this->getOption('prefetch')), $vn_element_id ? array($vn_element_id) : null, array('dontFetchAlreadyCachedValues' => true));
         }
         if (!$vb_return_as_array && !$vb_return_all_locales) {
             // return scalar
             //
             // Handle "hierarchy" modifier on list elements
             //
             if ($va_hier = $this->_getElementHierarchy($t_instance, $va_path_components)) {
                 return join($vs_hierarchical_delimiter, $va_hier);
             }
             if (isset($pa_options['convertCodesToDisplayText']) && $pa_options['convertCodesToDisplayText'] && $va_path_components['field_name']) {
                 $vs_template = null;
                 if ($va_path_components['subfield_name']) {
                     $va_values = $t_instance->getAttributeDisplayValues($va_path_components['field_name'], $vn_row_id, $pa_options);
                     $va_value_list = array();
                     foreach ($va_values as $vn_id => $va_attr_val_list) {
                         foreach ($va_attr_val_list as $vn_value_id => $va_value_array) {
                             $va_value_list[] = $va_value_array[$va_path_components['subfield_name']];
                         }
                     }
                     return join(" ", $va_value_list);
                 } else {
                     if (isset($pa_options['template'])) {
                         $vs_template = $pa_options['template'];
                     }
                 }
                 unset($pa_options['template']);
                 if (!$vs_template) {
                     $vs_template = "^" . $va_path_components['subfield_name'] ? $va_path_components['subfield_name'] : $va_path_components['field_name'];
                 }
                 return $t_instance->getAttributesForDisplay($va_path_components['field_name'], $vs_template, array_merge(array('row_id' => $vn_row_id), $pa_options));
             }
             if ($t_element && !$va_path_components['subfield_name'] && $t_element->get('datatype') == 0) {
                 return $t_instance->getAttributesForDisplay($va_path_components['field_name'], $vs_template, array_merge($pa_options, array('row_id' => $vn_row_id)));
             } else {
                 if (!$vs_template) {
                     return $t_instance->getRawValue($vn_row_id, $va_path_components['field_name'], $va_path_components['subfield_name'], ',', $pa_options);
                 } else {
                     return caProcessTemplateForIDs($vs_template, $va_path_components['table_name'], array($vn_row_id), array());
                 }
             }
         } else {
             // return array
             //
             // Handle "hierarchy" modifier on list elements
             //
             if ($va_hier = $this->_getElementHierarchy($t_instance, $va_path_components)) {
                 return $va_hier;
             }
             $va_values = $t_instance->getAttributeDisplayValues($va_path_components['field_name'], $vn_row_id, $pa_options);
             if ($vs_template && !$vb_return_all_locales) {
                 $va_values_tmp = array();
                 foreach ($va_values as $vn_i => $va_value_list) {
                     foreach ($va_value_list as $vn_attr_id => $va_attr_data) {
                         $va_values_tmp[] = caProcessTemplateForIDs($vs_template, $va_path_components['table_name'], array($vn_row_id), array_merge($pa_options, array('placeholderPrefix' => $va_path_components['field_name'])));
                     }
                 }
                 $va_values = $va_values_tmp;
             } else {
                 if ($va_path_components['subfield_name']) {
                     if ($vb_return_all_locales) {
                         foreach ($va_values as $vn_row_id => $va_values_by_locale) {
                             foreach ($va_values_by_locale as $vn_locale_id => $va_value_list) {
                                 foreach ($va_value_list as $vn_attr_id => $va_attr_data) {
                                     $va_values[$vn_row_id][$vn_locale_id][$vn_attr_id] = $va_attr_data[$va_path_components['subfield_name']];
                                 }
                             }
                         }
                     } else {
                         $va_processed_value_list = array();
                         foreach ($va_values as $vn_row_id => $va_value_list) {
                             foreach ($va_value_list as $vn_attr_id => $va_attr_data) {
                                 $va_processed_value_list[$vn_attr_id] = $va_attr_data[$va_path_components['subfield_name']];
                             }
                         }
                         $va_values = $va_processed_value_list;
                     }
                 } else {
                     if (!$vb_return_all_locales) {
                         $va_values = array_shift($va_values);
                     }
                 }
             }
             return $va_values;
         }
     } else {
         // Prefetch intrinsic fields in primary and related tables
         if (!isset($this->opa_prefetch_cache[$va_path_components['table_name']][$vn_row_id])) {
             $this->prefetch($va_path_components['table_name'], $this->opo_engine_result->currentRow(), $this->getOption('prefetch'), $pa_options);
             // try to prefetch ahead (usually doesn't hurt and very often helps performance)
         }
     }
     $va_return_values = array();
     if ($va_path_components['table_name'] !== $this->ops_table_name && $va_path_components['field_name'] !== 'relationship_typename' && !$t_instance->hasField($va_path_components['field_name']) && method_exists($t_instance, 'getAttributes')) {
         //
         // Return metadata attributes in a related table
         //
         $vs_pk = $t_instance->primaryKey();
         $vb_is_related = $this->ops_table_name !== $va_path_components['table_name'];
         $va_ids = array();
         $vs_opt_md5 = caMakeCacheKeyFromOptions($pa_options);
         if (!isset($this->opa_rel_prefetch_cache[$va_path_components['table_name']][$vn_row_id][$vs_opt_md5])) {
             $this->prefetchRelated($va_path_components['table_name'], $this->opo_engine_result->currentRow(), $this->getOption('prefetch'), $pa_options);
         }
         if (is_array($this->opa_rel_prefetch_cache[$va_path_components['table_name']][$vn_row_id][$vs_opt_md5])) {
             foreach ($this->opa_rel_prefetch_cache[$va_path_components['table_name']][$vn_row_id][$vs_opt_md5] as $vn_i => $va_values) {
                 //$vn_locale_id => $va_values_by_locale) {
                 $va_ids[] = $va_values[$vs_pk];
                 if (!$vb_return_as_array) {
                     $vs_val = $t_instance->getAttributesForDisplay($va_path_components['field_name'], $vs_template, array_merge(array('row_id' => $va_values[$vs_pk]), $pa_options));
                 } else {
                     $vs_val = $t_instance->getAttributeDisplayValues($va_path_components['field_name'], $va_values[$vs_pk], $pa_options);
                 }
                 if ($vs_val) {
                     if ($vb_return_as_array) {
                         if (!$vb_return_all_locales) {
                             foreach ($vs_val as $vn_i => $va_values_list) {
                                 foreach ($va_values_list as $vn_j => $va_values) {
                                     $va_return_values[] = $va_values;
                                 }
                             }
                         } else {
                             foreach ($vs_val as $vn_i => $va_values_list) {
                                 $va_return_values[] = $va_values_list;
                             }
                         }
                     } else {
                         $va_return_values[] = $vs_val;
                     }
                 }
             }
         }
         if ($vb_return_as_array || $vb_return_all_locales) {
             // return array
             if ($vb_return_as_link && $vb_is_related) {
                 $vs_table_name = $t_instance->tableName();
                 $vs_fld_key = $va_path_components['subfield_name'] ? $va_path_components['subfield_name'] : $va_path_components['field_name'];
                 if (!$vb_return_all_locales) {
                     $va_return_values_tmp = array();
                     foreach ($va_return_values as $vn_i => $va_value) {
                         if ($vs_template) {
                             $vs_value = caProcessTemplateForIDs($vs_template, $va_path_components['table_name'], array($va_ids[$vn_i][$vs_pk]), array('returnAsArray' => false));
                         } else {
                             $vs_value = $va_value[$vs_fld_key];
                         }
                         if ($vb_return_as_link) {
                             $va_return_values_tmp[$vn_i] = array_pop(caCreateLinksFromText(array($vs_value), $va_original_path_components['table_name'], array($va_ids[$vn_i]), $vs_return_as_link_class, $vs_return_as_link_target));
                         } else {
                             $va_return_values_tmp[$vn_i] = $vs_value;
                         }
                     }
                     $va_return_values = $va_return_values_tmp;
                 }
             }
             return $va_return_values;
         } else {
             // return scalar
             if ($vb_return_as_link && $vb_is_related) {
                 $va_return_values = caCreateLinksFromText($va_return_values, $va_original_path_components['table_name'], $va_ids, $vs_return_as_link_class, $vs_return_as_link_target);
             }
             if (isset($pa_options['convertLineBreaks']) && $pa_options['convertLineBreaks']) {
                 return caConvertLineBreaks(join($vs_delimiter, $va_return_values));
             } else {
                 return join($vs_delimiter, $va_return_values);
             }
         }
     } else {
         if ($vs_template) {
             return caProcessTemplateForIDs($vs_template, $this->opo_subject_instance->tableName(), array($vn_row_id), array_merge($pa_options, array('placeholderPrefix' => $va_path_components['field_name'])));
         }
         //
         // Return fields (intrinsics, labels) in primary or related table
         //
         $t_list = $this->opo_datamodel->getInstanceByTableName('ca_lists', true);
         $va_value_list = array($vn_row_id => $this->opa_prefetch_cache[$va_path_components['table_name']][$vn_row_id]);
         // Restrict to relationship types (related)
         if (isset($pa_options['restrict_to_relationship_types']) && $pa_options['restrict_to_relationship_types']) {
             if (!is_array($pa_options['restrict_to_relationship_types'])) {
                 $pa_options['restrict_to_relationship_types'] = array($pa_options['restrict_to_relationship_types']);
             }
             if (sizeof($pa_options['restrict_to_relationship_types'])) {
                 $t_rel_type = $this->opo_datamodel->getInstanceByTableName('ca_relationship_types', true);
                 $va_rel_types = array();
                 $va_rel_path = array_keys($this->opo_datamodel->getPath($this->ops_table_name, $va_path_components['table_name']));
                 foreach ($pa_options['restrict_to_relationship_types'] as $vm_type) {
                     if (!$vm_type) {
                         continue;
                     }
                     if ($vn_type_id = $t_rel_type->getRelationshipTypeID($va_rel_path[1], $vm_type)) {
                         $va_rel_types[] = $vn_type_id;
                         if (is_array($va_children = $t_rel_type->getHierarchyChildren($vn_type_id, array('idsOnly' => true)))) {
                             $va_rel_types = array_merge($va_rel_types, $va_children);
                         }
                     }
                 }
                 if (sizeof($va_rel_types)) {
                     $va_tmp = array();
                     foreach ($va_value_list as $vn_id => $va_by_locale) {
                         foreach ($va_by_locale as $vn_locale_id => $va_values) {
                             foreach ($va_values as $vn_i => $va_value) {
                                 if (!$va_value['rel_type_id'] || in_array($va_value['rel_type_id'], $va_rel_types)) {
                                     $va_tmp[$vn_id][$vn_locale_id][$vn_i] = $va_value;
                                 }
                             }
                         }
                     }
                     $va_value_list = $va_tmp;
                 }
             }
         }
         // Exclude relationship types (related)
         if (isset($pa_options['exclude_relationship_types']) && $pa_options['exclude_relationship_types']) {
             if (!is_array($pa_options['exclude_relationship_types'])) {
                 $pa_options['exclude_relationship_types'] = array($pa_options['exclude_relationship_types']);
             }
             if (sizeof($pa_options['exclude_relationship_types'])) {
                 $t_rel_type = $this->opo_datamodel->getInstanceByTableName('ca_relationship_types', true);
                 $va_rel_types = array();
                 $va_rel_path = array_keys($this->opo_datamodel->getPath($this->ops_table_name, $va_path_components['table_name']));
                 foreach ($pa_options['exclude_relationship_types'] as $vm_type) {
                     if ($vn_type_id = $t_rel_type->getRelationshipTypeID($va_rel_path[1], $vm_type)) {
                         $va_rel_types[] = $vn_type_id;
                         if (is_array($va_children = $t_rel_type->getHierarchyChildren($vn_type_id, array('idsOnly' => true)))) {
                             $va_rel_types = array_merge($va_rel_types, $va_children);
                         }
                     }
                 }
                 if (sizeof($va_rel_types)) {
                     $va_tmp = array();
                     foreach ($va_value_list as $vn_id => $va_by_locale) {
                         foreach ($va_by_locale as $vn_locale_id => $va_values) {
                             foreach ($va_values as $vn_i => $va_value) {
                                 if (!in_array($va_value['rel_type_id'], $va_rel_types)) {
                                     $va_tmp[$vn_id][$vn_locale_id][$vn_i] = $va_value;
                                 }
                             }
                         }
                     }
                     $va_value_list = $va_tmp;
                 }
             }
         }
         // Restrict to types (related)
         $va_type_ids = $vs_type_fld = null;
         if (method_exists($t_instance, "getTypeFieldName")) {
             $va_type_ids = caMergeTypeRestrictionLists($t_instance, $pa_options);
             $vs_type_fld = $t_instance->getTypeFieldName();
         } else {
             if (method_exists($t_instance, "getSubjectTableInstance")) {
                 $t_label_subj_instance = $t_instance->getSubjectTableInstance();
                 if (method_exists($t_label_subj_instance, "getTypeFieldName")) {
                     $va_type_ids = caMergeTypeRestrictionLists($t_label_subj_instance, $pa_options);
                     $vs_type_fld = 'item_type_id';
                 }
             }
         }
         if (is_array($va_type_ids) && sizeof($va_type_ids)) {
             $va_tmp = array();
             foreach ($va_value_list as $vn_id => $va_by_locale) {
                 foreach ($va_by_locale as $vn_locale_id => $va_values) {
                     foreach ($va_values as $vn_i => $va_value) {
                         if (!$va_value[$vs_type_fld ? $vs_type_fld : 'item_type_id'] || in_array($va_value[$vs_type_fld ? $vs_type_fld : 'item_type_id'], $va_type_ids)) {
                             $va_tmp[$vn_id][$vn_locale_id][$vn_i] = $va_value;
                         }
                     }
                 }
             }
             $va_value_list = $va_tmp;
         }
         // Restrict to sources (related)
         $va_source_ids = $vs_source_id_fld = null;
         if (method_exists($t_instance, "getSourceFieldName")) {
             $va_source_ids = caMergeSourceRestrictionLists($t_instance, $pa_options);
             $vs_source_id_fld = $t_instance->getSourceFieldName();
         } else {
             if (method_exists($t_instance, "getSubjectTableInstance")) {
                 $t_label_subj_instance = $t_instance->getSubjectTableInstance();
                 if (method_exists($t_label_subj_instance, "getSourceFieldName")) {
                     $va_source_ids = caMergeSourceRestrictionLists($t_label_subj_instance, $pa_options);
                     $vs_source_id_fld = 'item_source_id';
                 }
             }
         }
         if (is_array($va_source_ids) && sizeof($va_source_ids)) {
             $va_tmp = array();
             foreach ($va_value_list as $vn_id => $va_by_locale) {
                 foreach ($va_by_locale as $vn_locale_id => $va_values) {
                     foreach ($va_values as $vn_i => $va_value) {
                         if (!$va_value[$vs_source_id_fld ? $vs_source_id_fld : 'item_source_id'] || in_array($va_value[$vs_source_id_fld ? $vs_source_id_fld : 'item_source_id'], $va_source_ids)) {
                             $va_tmp[$vn_id][$vn_locale_id][$vn_i] = $va_value;
                         }
                     }
                 }
             }
             $va_value_list = $va_tmp;
         }
         // Exclude types (related)
         if (isset($pa_options['exclude_type']) && $pa_options['exclude_type']) {
             if (!isset($pa_options['exclude_types']) || !is_array($pa_options['exclude_types'])) {
                 $pa_options['exclude_types'] = array();
             }
             $pa_options['exclude_types'][] = $pa_options['exclude_type'];
         }
         if (isset($pa_options['exclude_types']) && is_array($pa_options['exclude_types'])) {
             $va_ids = caMakeTypeIDList($va_path_components['table_name'], $pa_options['exclude_types']);
             if (is_array($va_ids) && sizeof($va_ids) > 0) {
                 $va_tmp = array();
                 foreach ($va_value_list as $vn_id => $va_by_locale) {
                     foreach ($va_by_locale as $vn_locale_id => $va_values) {
                         foreach ($va_values as $vn_i => $va_value) {
                             if (!in_array($va_value[$vs_type_fld ? $vs_type_fld : 'item_type_id'], $va_type_ids)) {
                                 $va_tmp[$vn_id][$vn_locale_id][$vn_i] = $va_value;
                             }
                         }
                     }
                 }
                 $va_value_list = $va_tmp;
             }
         }
         // Handle 'relationship_typename' (related)
         $vb_get_relationship_typename = false;
         if ($va_path_components['field_name'] == 'relationship_typename') {
             $va_path_components['field_name'] = 'rel_type_id';
             $vb_get_relationship_typename = true;
         }
         if ($vb_return_as_array) {
             // return array (intrinsics or labels in primary or related table)
             if ($t_instance->hasField($va_path_components['field_name']) && $va_path_components['table_name'] === $t_instance->tableName()) {
                 // Intrinsic
                 $va_field_info = $t_instance->getFieldInfo($va_path_components['field_name']);
                 $vs_pk = $t_original_instance->primaryKey();
                 // Handle specific intrinsic types
                 switch ($va_field_info['FIELD_TYPE']) {
                     case FT_DATERANGE:
                     case FT_HISTORIC_DATERANGE:
                         foreach ($va_value_list as $vn_id => $va_values_by_locale) {
                             foreach ($va_values_by_locale as $vn_locale_id => $va_values) {
                                 foreach ($va_values as $vn_i => $va_value) {
                                     $va_ids[] = $va_value[$vs_pk];
                                     if (caGetOption('GET_DIRECT_DATE', $pa_options, false) || caGetOption('getDirectDate', $pa_options, false)) {
                                         if (caGetOption('sortable', $pa_options, false)) {
                                             $vs_prop = $va_value[$va_field_info['START']] . '/' . $va_value[$va_field_info['END']];
                                         } else {
                                             $vs_prop = $va_value[$va_field_info['START']];
                                         }
                                     } else {
                                         $this->opo_tep->init();
                                         if ($va_field_info['FIELD_TYPE'] == FT_DATERANGE) {
                                             $this->opo_tep->setUnixTimestamps($va_value[$va_field_info['START']], $va_value[$va_field_info['END']]);
                                         } else {
                                             $this->opo_tep->setHistoricTimestamps($va_value[$va_field_info['START']], $va_value[$va_field_info['END']]);
                                         }
                                         $vs_prop = $this->opo_tep->getText($pa_options);
                                     }
                                     if ($vb_return_all_locales) {
                                         $va_return_values[$vn_row_id][$vn_locale_id][] = $vs_prop;
                                     } else {
                                         $va_return_values[] = $vs_prop;
                                     }
                                 }
                             }
                         }
                         break;
                     case FT_MEDIA:
                         if (!($vs_version = $va_path_components['subfield_name'])) {
                             $vs_version = "largeicon";
                         }
                         foreach ($va_value_list as $vn_id => $va_values_by_locale) {
                             foreach ($va_values_by_locale as $vn_locale_id => $va_values) {
                                 foreach ($va_values as $vn_i => $va_value) {
                                     $va_ids[] = $va_value[$vs_pk];
                                     if (isset($pa_options['unserialize']) && $pa_options['unserialize']) {
                                         $vs_prop = caUnserializeForDatabase($va_value[$va_path_components['field_name']]);
                                         if ($vb_return_all_locales) {
                                             $va_return_values[$vn_row_id][$vn_locale_id][] = $vs_prop;
                                         } else {
                                             $va_return_values[] = $vs_prop;
                                         }
                                     } else {
                                         $o_media_settings = new MediaProcessingSettings($va_path_components['table_name'], $va_path_components['field_name']);
                                         $va_versions = $o_media_settings->getMediaTypeVersions('*');
                                         if (!isset($va_versions[$vs_version])) {
                                             $va_tmp = array_keys($va_versions);
                                             $vs_version = array_shift($va_tmp);
                                         }
                                         // See if an info element was passed, eg. ca_object_representations.media.icon.width should return the width of the media rather than a tag or url to the media
                                         $vs_info_element = $va_path_components['num_components'] == 4 ? $va_path_components['components'][3] : null;
                                         if ($vb_return_all_locales) {
                                             if ($vs_info_element) {
                                                 $va_return_values[$vn_row_id][$vn_locale_id][] = $this->getMediaInfo($va_path_components['table_name'] . '.' . $va_path_components['field_name'], $vs_version, $vs_info_element, $pa_options);
                                             } elseif (isset($pa_options['returnURL']) && $pa_options['returnURL']) {
                                                 $va_return_values[$vn_row_id][$vn_locale_id][] = $this->getMediaUrl($va_path_components['table_name'] . '.' . $va_path_components['field_name'], $vs_version, $pa_options);
                                             } else {
                                                 $va_return_values[$vn_row_id][$vn_locale_id][] = $this->getMediaTag($va_path_components['table_name'] . '.' . $va_path_components['field_name'], $vs_version, $pa_options);
                                             }
                                         } else {
                                             if ($vs_info_element) {
                                                 $va_return_values[] = $this->getMediaInfo($va_path_components['table_name'] . '.' . $va_path_components['field_name'], $vs_version, $vs_info_element, $pa_options);
                                             } elseif (isset($pa_options['returnURL']) && $pa_options['returnURL']) {
                                                 $va_return_values[] = $this->getMediaUrl($va_path_components['table_name'] . '.' . $va_path_components['field_name'], $vs_version, $pa_options);
                                             } else {
                                                 $va_return_values[] = $this->getMediaTag($va_path_components['table_name'] . '.' . $va_path_components['field_name'], $vs_version, $pa_options);
                                             }
                                         }
                                     }
                                 }
                             }
                         }
                         break;
                     default:
                         // is intrinsic field in primary table
                         $vb_supports_preferred = (bool) $t_instance->hasField('is_preferred');
                         foreach ($va_value_list as $vn_id => $va_values_by_locale) {
                             foreach ($va_values_by_locale as $vn_locale_id => $va_values) {
                                 foreach ($va_values as $vn_i => $va_value) {
                                     $va_ids[] = $vn_id = $va_value[$vs_pk];
                                     if ($vb_get_preferred_labels_only && $vb_supports_preferred && !$va_value['is_preferred']) {
                                         continue;
                                     }
                                     if ($vb_get_nonpreferred_labels_only && $vb_supports_preferred && $va_value['is_preferred']) {
                                         continue;
                                     }
                                     $vs_prop = $va_value[$va_path_components['field_name']];
                                     if (isset($pa_options['convertCodesToDisplayText']) && $pa_options['convertCodesToDisplayText'] && ($vs_list_code = $t_instance->getFieldInfo($va_path_components['field_name'], "LIST_CODE"))) {
                                         $vs_prop = $t_list->getItemFromListForDisplayByItemID($vs_list_code, $vs_prop);
                                     } else {
                                         if (isset($pa_options['convertCodesToDisplayText']) && $pa_options['convertCodesToDisplayText'] && ($vs_list_code = $t_instance->getFieldInfo($va_path_components['field_name'], "LIST"))) {
                                             $vs_prop = $t_list->getItemFromListForDisplayByItemValue($vs_list_code, $vs_prop);
                                         } else {
                                             if (isset($pa_options['convertCodesToDisplayText']) && $pa_options['convertCodesToDisplayText'] && $va_path_components['field_name'] === 'locale_id' && (int) $vs_prop > 0) {
                                                 $t_locale = new ca_locales($vs_prop);
                                                 $vs_prop = $t_locale->getName();
                                             } else {
                                                 if (isset($pa_options['convertCodesToDisplayText']) && $pa_options['convertCodesToDisplayText'] && is_array($va_list = $t_instance->getFieldInfo($va_path_components['field_name'], "BOUNDS_CHOICE_LIST"))) {
                                                     foreach ($va_list as $vs_option => $vs_value) {
                                                         if ($vs_value == $vs_prop) {
                                                             $vs_prop = $vs_option;
                                                             break;
                                                         }
                                                     }
                                                 }
                                             }
                                         }
                                     }
                                     if ($vb_return_all_locales) {
                                         $va_return_values[$vn_id][$vn_locale_id][] = $vs_prop;
                                     } else {
                                         $va_return_values[$vn_id][$vn_locale_id] = $vs_prop;
                                     }
                                 }
                             }
                         }
                         if (!$vb_return_all_locales) {
                             $va_return_values = array_values(caExtractValuesByUserLocale($va_return_values));
                         }
                         break;
                 }
             } else {
                 // Attributes
                 $vs_pk = $t_original_instance->primaryKey();
                 $vb_is_related = $this->ops_table_name !== $va_path_components['table_name'];
                 $va_ids = array();
                 $t_instance = $this->opo_datamodel->getInstanceByTableName($va_path_components['table_name'], true);
                 foreach ($va_value_list as $vn_i => $va_values_by_locale) {
                     foreach ($va_values_by_locale as $vn_locale_id => $va_values) {
                         foreach ($va_values as $vn_i => $va_value) {
                             if ($vb_is_related) {
                                 $va_ids[] = $va_value[$vs_pk];
                             }
                             if ($vb_get_preferred_labels_only && !$va_value['is_preferred']) {
                                 continue;
                             }
                             if ($vb_get_nonpreferred_labels_only && $va_value['is_preferred']) {
                                 continue;
                             }
                             // do we need to translate foreign key and choice list codes to display text?
                             $vs_prop = $vb_return_all_label_values && !$vb_return_as_link ? $va_value : $va_value[$va_path_components['field_name']];
                             if ($vb_get_relationship_typename) {
                                 if (!$t_rel_type) {
                                     $t_rel_type = $this->opo_datamodel->getInstanceByTableName('ca_relationship_types', true);
                                 }
                                 if (is_array($va_labels = $t_rel_type->getDisplayLabels(false, array('row_id' => (int) $vs_prop)))) {
                                     $va_label = array_shift($va_labels);
                                     $vs_prop = $va_label[0]['typename'];
                                 } else {
                                     $vs_prop = "?";
                                 }
                             } else {
                                 // Decode list items to text
                                 if (isset($pa_options['convertCodesToDisplayText']) && $pa_options['convertCodesToDisplayText'] && ($vs_list_code = $t_instance->getFieldInfo($va_path_components['field_name'], "LIST_CODE"))) {
                                     $vs_prop = $t_list->getItemFromListForDisplayByItemID($vs_list_code, $vs_prop);
                                 } else {
                                     if (isset($pa_options['convertCodesToDisplayText']) && $pa_options['convertCodesToDisplayText'] && ($vs_list_code = $t_instance->getFieldInfo($va_path_components['field_name'], "LIST"))) {
                                         $vs_prop = $t_list->getItemFromListForDisplayByItemValue($vs_list_code, $vs_prop);
                                     } else {
                                         if (isset($pa_options['convertCodesToDisplayText']) && $pa_options['convertCodesToDisplayText'] && $va_path_components['field_name'] === 'locale_id' && (int) $vs_prop > 0) {
                                             $t_locale = new ca_locales($vs_prop);
                                             $vs_prop = $t_locale->getName();
                                         } else {
                                             if (isset($pa_options['convertCodesToDisplayText']) && $pa_options['convertCodesToDisplayText'] && is_array($va_list = $t_instance->getFieldInfo($va_path_components['field_name'], "BOUNDS_CHOICE_LIST"))) {
                                                 foreach ($va_list as $vs_option => $vs_value) {
                                                     if ($vs_value == $vs_prop) {
                                                         $vs_prop = $vs_option;
                                                         break;
                                                     }
                                                 }
                                             }
                                         }
                                     }
                                 }
                             }
                             if ($vb_return_all_locales) {
                                 $va_return_values[$vn_row_id][$vn_locale_id][] = $vs_prop;
                             } else {
                                 if ($vb_get_nonpreferred_labels_only && is_array($vs_prop)) {
                                     // non-preferred labels are lists of lists because they can repeat
                                     $va_return_values[][] = $vs_prop;
                                 } else {
                                     $va_return_values[] = $vs_prop;
                                 }
                             }
                         }
                     }
                 }
             }
             if ($vb_return_as_link) {
                 if (!$vb_return_all_locales) {
                     $va_return_values = caCreateLinksFromText($va_return_values, $va_original_path_components['table_name'], $va_ids, $vs_return_as_link_class, $vs_return_as_link_target);
                 }
             }
             return $va_return_values;
         } else {
             // Return scalar (intrinsics or labels in primary or related table)
             if ($vb_get_preferred_labels_only || $vb_get_nonpreferred_labels_only) {
                 // We have to distinguish between preferred and non-preferred labels here
                 // so that only appropriate labels are passed for output.
                 $va_filtered_values = array();
                 foreach ($va_value_list as $vn_label_id => $va_labels_by_locale) {
                     foreach ($va_labels_by_locale as $vn_locale_id => $va_labels) {
                         foreach ($va_labels as $vn_i => $va_label) {
                             if ($vb_get_preferred_labels_only && (!isset($va_label['is_preferred']) || $va_label['is_preferred']) || $vb_get_nonpreferred_labels_only && !$va_label['is_preferred']) {
                                 $va_filtered_values[$vn_label_id][$vn_locale_id][] = $va_label;
                             }
                         }
                     }
                 }
                 $va_value_list = $va_filtered_values;
             }
             $va_value_list = caExtractValuesByUserLocale($va_value_list);
             // do we need to translate foreign key and choice list codes to display text?
             $t_instance = $this->opo_datamodel->getInstanceByTableName($va_path_components['table_name'], true);
             $va_field_info = $t_instance->getFieldInfo($va_path_components['field_name']);
             $vs_pk = $t_instance->primaryKey();
             $vb_is_related = $this->ops_table_name !== $va_path_components['table_name'];
             $va_ids = array();
             foreach ($va_value_list as $vn_i => $va_values) {
                 if (!is_array($va_values)) {
                     continue;
                 }
                 // Handle specific intrinsic types
                 $vs_template_value = $vs_template;
                 foreach ($va_values as $vn_j => $va_value) {
                     switch ($va_field_info['FIELD_TYPE']) {
                         case FT_BIT:
                             if ($pa_options['convertCodesToDisplayText']) {
                                 $va_value[$va_path_components['field_name']] = (bool) $vs_prop ? _t('yes') : _t('no');
                             }
                             break;
                         case FT_DATERANGE:
                             if (caGetOption('GET_DIRECT_DATE', $pa_options, false) || caGetOption('getDirectDate', $pa_options, false)) {
                                 if (isset($pa_options['sortable']) && $pa_options['sortable']) {
                                     $va_value[$va_path_components['field_name']] = $va_value[$va_field_info['START']] . '/' . $va_value[$va_field_info['END']];
                                 } else {
                                     $va_value[$va_path_components['field_name']] = $va_value[$va_field_info['START']];
                                 }
                             } else {
                                 $this->opo_tep->init();
                                 $this->opo_tep->setUnixTimestamps($va_value[$va_field_info['START']], $va_value[$va_field_info['END']]);
                                 $va_value[$va_path_components['field_name']] = $this->opo_tep->getText($pa_options);
                             }
                             break;
                         case FT_HISTORIC_DATERANGE:
                             if (caGetOption('GET_DIRECT_DATE', $pa_options, false) || caGetOption('getDirectDate', $pa_options, false)) {
                                 if (caGetOption('sortable', $pa_options, false)) {
                                     $va_value[$va_path_components['field_name']] = $va_value[$va_field_info['START']] . '/' . $va_value[$va_field_info['END']];
                                 } else {
                                     $va_value[$va_path_components['field_name']] = $va_value[$va_field_info['START']];
                                 }
                             } else {
                                 $this->opo_tep->init();
                                 $this->opo_tep->setHistoricTimestamps($va_value[$va_field_info['START']], $va_value[$va_field_info['END']]);
                                 $va_value[$va_path_components['field_name']] = $this->opo_tep->getText($pa_options);
                             }
                             break;
                         case FT_MEDIA:
                             if (!($vs_version = $va_path_components['subfield_name'])) {
                                 $vs_version = "largeicon";
                             }
                             // See if an info element was passed, eg. ca_object_representations.media.icon.width should return the width of the media rather than a tag or url to the media
                             $vs_info_element = $va_path_components['num_components'] == 4 ? $va_path_components['components'][3] : null;
                             if (isset($pa_options['unserialize']) && $pa_options['unserialize']) {
                                 return caUnserializeForDatabase($va_value[$va_path_components['field_name']]);
                             } else {
                                 $o_media_settings = new MediaProcessingSettings($va_path_components['table_name'], $va_path_components['field_name']);
                                 $va_versions = $o_media_settings->getMediaTypeVersions('*');
                                 if (!isset($va_versions[$vs_version])) {
                                     $va_tmp = array_keys($va_versions);
                                     $vs_version = array_shift($va_tmp);
                                 }
                                 if ($vs_info_element) {
                                     // Return media info
                                     $va_value[$va_path_components['field_name']] = $this->getMediaInfo($va_path_components['table_name'] . '.' . $va_path_components['field_name'], $vs_version, $vs_info_element, $pa_options);
                                 } elseif (isset($pa_options['returnURL']) && $pa_options['returnURL']) {
                                     $va_value[$va_path_components['field_name']] = $this->getMediaUrl($va_path_components['table_name'] . '.' . $va_path_components['field_name'], $vs_version, $pa_options);
                                 } else {
                                     $va_value[$va_path_components['field_name']] = $this->getMediaTag($va_path_components['table_name'] . '.' . $va_path_components['field_name'], $vs_version, $pa_options);
                                 }
                             }
                             break;
                         default:
                             // noop
                             break;
                     }
                     $vs_prop = $va_value[$va_path_components['field_name']];
                     // Decode list items to text
                     if (isset($pa_options['convertCodesToDisplayText']) && $pa_options['convertCodesToDisplayText'] && ($vs_list_code = $t_instance->getFieldInfo($va_path_components['field_name'], "LIST_CODE"))) {
                         $va_value[$va_path_components['field_name']] = $t_list->getItemFromListForDisplayByItemID($vs_list_code, $vs_prop);
                     } else {
                         if (isset($pa_options['convertCodesToDisplayText']) && $pa_options['convertCodesToDisplayText'] && ($vs_list_code = $t_instance->getFieldInfo($va_path_components['field_name'], "LIST"))) {
                             $va_value[$va_path_components['field_name']] = $t_list->getItemFromListForDisplayByItemValue($vs_list_code, $vs_prop);
                         } else {
                             if (isset($pa_options['convertCodesToDisplayText']) && $pa_options['convertCodesToDisplayText'] && $va_path_components['field_name'] === 'locale_id' && (int) $vs_prop > 0) {
                                 $t_locale = new ca_locales($vs_prop);
                                 $va_value[$va_path_components['field_name']] = $t_locale->getName();
                             } else {
                                 if (isset($pa_options['convertCodesToDisplayText']) && $pa_options['convertCodesToDisplayText'] && is_array($va_list = $t_instance->getFieldInfo($va_path_components['field_name'], "BOUNDS_CHOICE_LIST"))) {
                                     foreach ($va_list as $vs_option => $vs_value) {
                                         if ($vs_value == $vs_prop) {
                                             $va_value[$va_path_components['field_name']] = $vs_option;
                                             break;
                                         }
                                     }
                                 }
                             }
                         }
                     }
                     $vs_pk = $this->opo_datamodel->getTablePrimaryKeyName($va_original_path_components['table_name']);
                     if ($vs_template) {
                         foreach ($va_value_list as $vn_id => $va_values) {
                             foreach ($va_values as $vn_i => $va_value) {
                                 $vs_prop = caProcessTemplateForIDs($vs_template, $va_original_path_components['table_name'], array($va_value[$vs_pk]), array('returnAsArray' => false));
                                 $va_return_values[] = $vs_prop;
                                 $va_ids[] = $va_value[$vs_pk];
                             }
                         }
                     } else {
                         $vs_prop = $va_value[$va_path_components['field_name']];
                         $va_return_values[] = $vs_prop;
                         if ($vb_is_related) {
                             $va_ids[] = $va_value[$vs_pk];
                         }
                     }
                 }
             }
             if ($vb_return_as_link && $vb_is_related) {
                 $va_return_values = caCreateLinksFromText($va_return_values, $va_original_path_components['table_name'], $va_ids, $vs_return_as_link_class, $vs_return_as_link_target);
             }
             if (isset($pa_options['convertLineBreaks']) && $pa_options['convertLineBreaks']) {
                 return caConvertLineBreaks(join($vs_delimiter, $va_return_values));
             } else {
                 return join($vs_delimiter, $va_return_values);
             }
         }
     }
     return null;
 }