/**
  * The fetch method returns a instance of a Field from tbl_fields. The most common
  * use of this function is to retrieve a Field by ID, but it can be used to retrieve
  * Fields from a Section also. There are several parameters that can be used to fetch
  * fields by their Type, Location, by a Field Constant or with a custom WHERE query.
  *
  * @throws DatabaseException
  * @throws Exception
  * @param integer|array $id
  *  The ID of the field to retrieve. Defaults to null which will return multiple field
  *  objects. Since Symphony 2.3, `$id` will accept an array of Field ID's
  * @param integer $section_id
  *  The ID of the section to look for the fields in. Defaults to null which will allow
  *  all fields in the Symphony installation to be searched on.
  * @param string $order
  *  Available values of ASC (Ascending) or DESC (Descending), which refer to the
  *  sort order for the query. Defaults to ASC (Ascending)
  * @param string $sortfield
  *  The field to sort the query by. Can be any from the tbl_fields schema. Defaults to
  *  'sortorder'
  * @param string $type
  *  Filter fields by their type, ie. input, select. Defaults to null
  * @param string $location
  *  Filter fields by their location in the entry form. There are two possible values,
  *  'main' or 'sidebar'. Defaults to null
  * @param string $where
  *  Allows a custom where query to be included. Must be valid SQL. The tbl_fields alias
  *  is t1
  * @param integer|string $restrict
  *  Only return fields if they match one of the Field Constants. Available values are
  *  `__TOGGLEABLE_ONLY__`, `__UNTOGGLEABLE_ONLY__`, `__FILTERABLE_ONLY__`,
  *  `__UNFILTERABLE_ONLY__` or `__FIELD_ALL__`. Defaults to `__FIELD_ALL__`
  * @return array
  *  An array of Field objects. If no Field are found, null is returned.
  */
 public static function fetch($id = null, $section_id = null, $order = 'ASC', $sortfield = 'sortorder', $type = null, $location = null, $where = null, $restrict = Field::__FIELD_ALL__)
 {
     $fields = array();
     $returnSingle = false;
     $ids = array();
     $field_contexts = array();
     if (!is_null($id)) {
         if (is_numeric($id)) {
             $returnSingle = true;
         }
         if (!is_array($id)) {
             $field_ids = array((int) $id);
         } else {
             $field_ids = $id;
         }
         // Loop over the `$field_ids` and check to see we have
         // instances of the request fields
         foreach ($field_ids as $key => $field_id) {
             if (isset(self::$_initialiased_fields[$field_id]) && self::$_initialiased_fields[$field_id] instanceof Field) {
                 $fields[$field_id] = self::$_initialiased_fields[$field_id];
                 unset($field_ids[$key]);
             }
         }
     }
     // If there is any `$field_ids` left to be resolved lets do that, otherwise
     // if `$id` wasn't provided in the first place, we'll also continue
     if (!empty($field_ids) || is_null($id)) {
         $sql = sprintf("SELECT t1.*\n                FROM tbl_fields AS `t1`\n                WHERE 1\n                %s %s %s %s\n                %s", isset($type) ? " AND t1.`type` = '{$type}' " : null, isset($location) ? " AND t1.`location` = '{$location}' " : null, isset($section_id) ? " AND t1.`parent_section` = '{$section_id}' " : null, $where, isset($field_ids) ? " AND t1.`id` IN(" . implode(',', $field_ids) . ") " : " ORDER BY t1.`{$sortfield}` {$order}");
         if (!($result = Symphony::Database()->fetch($sql))) {
             return $returnSingle ? null : array();
         }
         // Loop over the resultset building an array of type, field_id
         foreach ($result as $f) {
             $ids[$f['type']][] = $f['id'];
         }
         // Loop over the `ids` array, which is grouped by field type
         // and get the field context.
         foreach ($ids as $type => $field_id) {
             $field_contexts[$type] = Symphony::Database()->fetch(sprintf("SELECT * FROM `tbl_fields_%s` WHERE `field_id` IN (%s)", $type, implode(',', $field_id)), 'field_id');
         }
         foreach ($result as $f) {
             // We already have this field in our static store
             if (isset(self::$_initialiased_fields[$f['id']]) && self::$_initialiased_fields[$f['id']] instanceof Field) {
                 $field = self::$_initialiased_fields[$f['id']];
                 // We don't have an instance of this field, so let's set one up
             } else {
                 $field = self::create($f['type']);
                 $field->setArray($f);
                 // If the field has said that's going to have associations, then go find the
                 // association setting value. In future this check will be most robust with
                 // an interface, but for now, this is what we've got. RE: #2082
                 if ($field->canShowAssociationColumn()) {
                     $field->set('show_association', SectionManager::getSectionAssociationSetting($f['id']));
                 }
                 // Get the context for this field from our previous queries.
                 $context = $field_contexts[$f['type']][$f['id']];
                 if (is_array($context) && !empty($context)) {
                     try {
                         unset($context['id']);
                         $field->setArray($context);
                     } catch (Exception $e) {
                         throw new Exception(__('Settings for field %s could not be found in table tbl_fields_%s.', array($f['id'], $f['type'])));
                     }
                 }
                 self::$_initialiased_fields[$f['id']] = $field;
             }
             // Check to see if there was any restricts imposed on the fields
             if ($restrict == Field::__FIELD_ALL__ || $restrict == Field::__TOGGLEABLE_ONLY__ && $field->canToggle() || $restrict == Field::__UNTOGGLEABLE_ONLY__ && !$field->canToggle() || $restrict == Field::__FILTERABLE_ONLY__ && $field->canFilter() || $restrict == Field::__UNFILTERABLE_ONLY__ && !$field->canFilter()) {
                 $fields[$f['id']] = $field;
             }
         }
     }
     return count($fields) <= 1 && $returnSingle ? current($fields) : $fields;
 }