/**
  * Preps a {@link DbCommand} object for querying for elements, based on a given element criteria.
  *
  * @param ElementCriteriaModel &$criteria     The element criteria model
  * @param string               &$contentTable The content table that should be joined in. (This variable will
  *                                            actually get defined by buildElementsQuery(), and is passed by
  *                                            reference so whatever’s calling the method will have access to its
  *                                            value.)
  * @param array                &$fieldColumns Info about the content field columns being selected. (This variable
  *                                            will actually get defined by buildElementsQuery(), and is passed by
  *                                            reference so whatever’s calling the method will have access to its
  *                                            value.)
  *
  * @return DbCommand|false The DbCommand object, or `false` if the method was able to determine ahead of time that
  *                         there’s no chance any elements are going to be found with the given parameters.
  */
 public function buildElementsQuery(&$criteria = null, &$contentTable = null, &$fieldColumns = null)
 {
     if (!$criteria instanceof ElementCriteriaModel) {
         $criteria = $this->getCriteria('Entry', $criteria);
     }
     $elementType = $criteria->getElementType();
     if (!$elementType->isLocalized()) {
         // The criteria *must* be set to the primary locale
         $criteria->locale = craft()->i18n->getPrimarySiteLocaleId();
     } else {
         if (!$criteria->locale) {
             // Default to the current app locale
             $criteria->locale = craft()->language;
         }
     }
     // Set up the query
     // ---------------------------------------------------------------------
     $query = craft()->db->createCommand()->select('elements.id, elements.type, elements.enabled, elements.archived, elements.dateCreated, elements.dateUpdated, elements_i18n.slug, elements_i18n.uri, elements_i18n.enabled AS localeEnabled')->from('elements elements')->join('elements_i18n elements_i18n', 'elements_i18n.elementId = elements.id')->where('elements_i18n.locale = :locale', array(':locale' => $criteria->locale))->group('elements.id');
     if ($elementType->hasContent()) {
         $contentTable = $elementType->getContentTableForElementsQuery($criteria);
         if ($contentTable) {
             $contentCols = 'content.id AS contentId';
             if ($elementType->hasTitles()) {
                 $contentCols .= ', content.title';
             }
             // TODO: Replace this with a call to getFieldsForElementsQuery() in 3.0
             $fieldColumns = $elementType->getContentFieldColumnsForElementsQuery($criteria);
             foreach ($fieldColumns as $column) {
                 $contentCols .= ', content.' . $column['column'];
             }
             $query->addSelect($contentCols);
             $query->join($contentTable . ' content', 'content.elementId = elements.id');
             $query->andWhere('content.locale = :locale');
         }
     }
     // Basic element params
     // ---------------------------------------------------------------------
     // If the 'id' parameter is set to any empty value besides `null`, don't return anything
     if ($criteria->id !== null && empty($criteria->id)) {
         return false;
     }
     if ($criteria->id) {
         $query->andWhere(DbHelper::parseParam('elements.id', $criteria->id, $query->params));
     }
     if ($criteria->archived) {
         $query->andWhere('elements.archived = 1');
     } else {
         $query->andWhere('elements.archived = 0');
         if ($criteria->status) {
             $statusConditions = array();
             $statuses = ArrayHelper::stringToArray($criteria->status);
             foreach ($statuses as $status) {
                 $status = StringHelper::toLowerCase($status);
                 // Is this a supported status?
                 if (in_array($status, array_keys($elementType->getStatuses()))) {
                     if ($status == BaseElementModel::ENABLED) {
                         $statusConditions[] = 'elements.enabled = 1';
                     } else {
                         if ($status == BaseElementModel::DISABLED) {
                             $statusConditions[] = 'elements.enabled = 0';
                         } else {
                             $elementStatusCondition = $elementType->getElementQueryStatusCondition($query, $status);
                             if ($elementStatusCondition) {
                                 $statusConditions[] = $elementStatusCondition;
                             } else {
                                 if ($elementStatusCondition === false) {
                                     return false;
                                 }
                             }
                         }
                     }
                 }
             }
             if ($statusConditions) {
                 if (count($statusConditions) == 1) {
                     $statusCondition = $statusConditions[0];
                 } else {
                     array_unshift($statusConditions, 'or');
                     $statusCondition = $statusConditions;
                 }
                 $query->andWhere($statusCondition);
             }
         }
     }
     if ($criteria->dateCreated) {
         $query->andWhere(DbHelper::parseDateParam('elements.dateCreated', $criteria->dateCreated, $query->params));
     }
     if ($criteria->dateUpdated) {
         $query->andWhere(DbHelper::parseDateParam('elements.dateUpdated', $criteria->dateUpdated, $query->params));
     }
     if ($elementType->hasTitles() && $criteria->title) {
         $query->andWhere(DbHelper::parseParam('content.title', $criteria->title, $query->params));
     }
     // i18n params
     // ---------------------------------------------------------------------
     if ($criteria->slug) {
         $query->andWhere(DbHelper::parseParam('elements_i18n.slug', $criteria->slug, $query->params));
     }
     if ($criteria->uri) {
         $query->andWhere(DbHelper::parseParam('elements_i18n.uri', $criteria->uri, $query->params));
     }
     if ($criteria->localeEnabled) {
         $query->andWhere('elements_i18n.enabled = 1');
     }
     // Relational params
     // ---------------------------------------------------------------------
     // Convert the old childOf and parentOf params to the relatedTo param
     // childOf(element)  => relatedTo({ source: element })
     // parentOf(element) => relatedTo({ target: element })
     if (!$criteria->relatedTo && ($criteria->childOf || $criteria->parentOf)) {
         $relatedTo = array('and');
         if ($criteria->childOf) {
             $relatedTo[] = array('sourceElement' => $criteria->childOf, 'field' => $criteria->childField);
         }
         if ($criteria->parentOf) {
             $relatedTo[] = array('targetElement' => $criteria->parentOf, 'field' => $criteria->parentField);
         }
         $criteria->relatedTo = $relatedTo;
     }
     if ($criteria->relatedTo) {
         $relationParamParser = new ElementRelationParamParser();
         $relConditions = $relationParamParser->parseRelationParam($criteria->relatedTo, $query);
         if ($relConditions === false) {
             return false;
         }
         $query->andWhere($relConditions);
         // If there's only one relation criteria and it's specifically for grabbing target elements, allow the query
         // to order by the relation sort order
         if ($relationParamParser->isRelationFieldQuery()) {
             $query->addSelect('sources1.sortOrder');
         }
     }
     // Give field types a chance to make changes
     // ---------------------------------------------------------------------
     if ($elementType->hasContent() && $contentTable) {
         $contentService = craft()->content;
         $originalFieldColumnPrefix = $contentService->fieldColumnPrefix;
         // TODO: $fields should already be defined by now in Craft 3.0
         $fields = $elementType->getFieldsForElementsQuery($criteria);
         $extraCriteriaAttributes = $criteria->getExtraAttributeNames();
         foreach ($fields as $field) {
             $fieldType = $field->getFieldType();
             if ($fieldType) {
                 // Was this field's parameter set on the criteria model?
                 if (in_array($field->handle, $extraCriteriaAttributes)) {
                     $fieldCriteria = $criteria->{$field->handle};
                 } else {
                     $fieldCriteria = null;
                 }
                 // Set the field's column prefix on ContentService
                 if ($field->columnPrefix) {
                     $contentService->fieldColumnPrefix = $field->columnPrefix;
                 }
                 $fieldTypeResponse = $fieldType->modifyElementsQuery($query, $fieldCriteria);
                 // Set it back
                 $contentService->fieldColumnPrefix = $originalFieldColumnPrefix;
                 // Need to bail early?
                 if ($fieldTypeResponse === false) {
                     return false;
                 }
             }
         }
     }
     // Give the element type a chance to make changes
     // ---------------------------------------------------------------------
     if ($elementType->modifyElementsQuery($query, $criteria) === false) {
         return false;
     }
     // Structure params
     // ---------------------------------------------------------------------
     if ($query->isJoined('structureelements')) {
         $query->addSelect('structureelements.root, structureelements.lft, structureelements.rgt, structureelements.level');
         if ($criteria->ancestorOf) {
             if (!$criteria->ancestorOf instanceof BaseElementModel) {
                 $criteria->ancestorOf = craft()->elements->getElementById($criteria->ancestorOf, $elementType->getClassHandle(), $criteria->locale);
                 if (!$criteria->ancestorOf) {
                     return false;
                 }
             }
             if ($criteria->ancestorOf) {
                 $query->andWhere(array('and', 'structureelements.lft < :ancestorOf_lft', 'structureelements.rgt > :ancestorOf_rgt', 'structureelements.root = :ancestorOf_root'), array(':ancestorOf_lft' => $criteria->ancestorOf->lft, ':ancestorOf_rgt' => $criteria->ancestorOf->rgt, ':ancestorOf_root' => $criteria->ancestorOf->root));
                 if ($criteria->ancestorDist) {
                     $query->andWhere('structureelements.level >= :ancestorOf_level', array(':ancestorOf_level' => $criteria->ancestorOf->level - $criteria->ancestorDist));
                 }
             }
         }
         if ($criteria->descendantOf) {
             if (!$criteria->descendantOf instanceof BaseElementModel) {
                 $criteria->descendantOf = craft()->elements->getElementById($criteria->descendantOf, $elementType->getClassHandle(), $criteria->locale);
                 if (!$criteria->descendantOf) {
                     return false;
                 }
             }
             if ($criteria->descendantOf) {
                 $query->andWhere(array('and', 'structureelements.lft > :descendantOf_lft', 'structureelements.rgt < :descendantOf_rgt', 'structureelements.root = :descendantOf_root'), array(':descendantOf_lft' => $criteria->descendantOf->lft, ':descendantOf_rgt' => $criteria->descendantOf->rgt, ':descendantOf_root' => $criteria->descendantOf->root));
                 if ($criteria->descendantDist) {
                     $query->andWhere('structureelements.level <= :descendantOf_level', array(':descendantOf_level' => $criteria->descendantOf->level + $criteria->descendantDist));
                 }
             }
         }
         if ($criteria->siblingOf) {
             if (!$criteria->siblingOf instanceof BaseElementModel) {
                 $criteria->siblingOf = craft()->elements->getElementById($criteria->siblingOf, $elementType->getClassHandle(), $criteria->locale);
                 if (!$criteria->siblingOf) {
                     return false;
                 }
             }
             if ($criteria->siblingOf) {
                 $query->andWhere(array('and', 'structureelements.level = :siblingOf_level', 'structureelements.root = :siblingOf_root', 'structureelements.elementId != :siblingOf_elementId'), array(':siblingOf_level' => $criteria->siblingOf->level, ':siblingOf_root' => $criteria->siblingOf->root, ':siblingOf_elementId' => $criteria->siblingOf->id));
                 if ($criteria->siblingOf->level != 1) {
                     $parent = $criteria->siblingOf->getParent();
                     if ($parent) {
                         $query->andWhere(array('and', 'structureelements.lft > :siblingOf_lft', 'structureelements.rgt < :siblingOf_rgt'), array(':siblingOf_lft' => $parent->lft, ':siblingOf_rgt' => $parent->rgt));
                     } else {
                         return false;
                     }
                 }
             }
         }
         if ($criteria->prevSiblingOf) {
             if (!$criteria->prevSiblingOf instanceof BaseElementModel) {
                 $criteria->prevSiblingOf = craft()->elements->getElementById($criteria->prevSiblingOf, $elementType->getClassHandle(), $criteria->locale);
                 if (!$criteria->prevSiblingOf) {
                     return false;
                 }
             }
             if ($criteria->prevSiblingOf) {
                 $query->andWhere(array('and', 'structureelements.level = :prevSiblingOf_level', 'structureelements.rgt = :prevSiblingOf_rgt', 'structureelements.root = :prevSiblingOf_root'), array(':prevSiblingOf_level' => $criteria->prevSiblingOf->level, ':prevSiblingOf_rgt' => $criteria->prevSiblingOf->lft - 1, ':prevSiblingOf_root' => $criteria->prevSiblingOf->root));
             }
         }
         if ($criteria->nextSiblingOf) {
             if (!$criteria->nextSiblingOf instanceof BaseElementModel) {
                 $criteria->nextSiblingOf = craft()->elements->getElementById($criteria->nextSiblingOf, $elementType->getClassHandle(), $criteria->locale);
                 if (!$criteria->nextSiblingOf) {
                     return false;
                 }
             }
             if ($criteria->nextSiblingOf) {
                 $query->andWhere(array('and', 'structureelements.level = :nextSiblingOf_level', 'structureelements.lft = :nextSiblingOf_lft', 'structureelements.root = :nextSiblingOf_root'), array(':nextSiblingOf_level' => $criteria->nextSiblingOf->level, ':nextSiblingOf_lft' => $criteria->nextSiblingOf->rgt + 1, ':nextSiblingOf_root' => $criteria->nextSiblingOf->root));
             }
         }
         if ($criteria->positionedBefore) {
             if (!$criteria->positionedBefore instanceof BaseElementModel) {
                 $criteria->positionedBefore = craft()->elements->getElementById($criteria->positionedBefore, $elementType->getClassHandle(), $criteria->locale);
                 if (!$criteria->positionedBefore) {
                     return false;
                 }
             }
             if ($criteria->positionedBefore) {
                 $query->andWhere(array('and', 'structureelements.rgt < :positionedBefore_rgt', 'structureelements.root = :positionedBefore_root'), array(':positionedBefore_rgt' => $criteria->positionedBefore->lft, ':positionedBefore_root' => $criteria->positionedBefore->root));
             }
         }
         if ($criteria->positionedAfter) {
             if (!$criteria->positionedAfter instanceof BaseElementModel) {
                 $criteria->positionedAfter = craft()->elements->getElementById($criteria->positionedAfter, $elementType->getClassHandle(), $criteria->locale);
                 if (!$criteria->positionedAfter) {
                     return false;
                 }
             }
             if ($criteria->positionedAfter) {
                 $query->andWhere(array('and', 'structureelements.lft > :positionedAfter_lft', 'structureelements.root = :positionedAfter_root'), array(':positionedAfter_lft' => $criteria->positionedAfter->rgt, ':positionedAfter_root' => $criteria->positionedAfter->root));
             }
         }
         if ($criteria->level || $criteria->depth) {
             // TODO: 'depth' is deprecated; use 'level' instead.
             $level = $criteria->level ? $criteria->level : $criteria->depth;
             $query->andWhere(DbHelper::parseParam('structureelements.level', $level, $query->params));
         }
     }
     // Search
     // ---------------------------------------------------------------------
     if ($criteria->search) {
         $elementIds = $this->_getElementIdsFromQuery($query);
         $scoredSearchResults = $criteria->order == 'score';
         $filteredElementIds = craft()->search->filterElementIdsByQuery($elementIds, $criteria->search, $scoredSearchResults);
         // No results?
         if (!$filteredElementIds) {
             return array();
         }
         $query->andWhere(array('in', 'elements.id', $filteredElementIds));
         if ($scoredSearchResults) {
             // Order the elements in the exact order that SearchService returned them in
             $query->order(craft()->db->getSchema()->orderByColumnValues('elements.id', $filteredElementIds));
         }
     }
     return $query;
 }
 /**
  * Preps a {@link DbCommand} object for querying for elements, based on a given element criteria.
  *
  * @param Venti_CriteriaModel &$criteria     The events criteria model
  *
  * @return DbCommand|false The DbCommand object, or `false` if the method was able to determine ahead of time that
  *                         there’s no chance any elements are going to be found with the given parameters.
  */
 public function buildEventsQuery(Venti_CriteriaModel $criteria)
 {
     $elementType = $criteria->getElementType();
     if (!$elementType->isLocalized()) {
         // The criteria *must* be set to the primary locale
         $criteria->locale = craft()->i18n->getPrimarySiteLocaleId();
     } else {
         if (!$criteria->locale) {
             // Default to the current app locale
             $criteria->locale = craft()->language;
         }
     }
     $query = craft()->db->createCommand()->select('venti.startDate, venti.endDate, venti.allDay, venti.isrepeat, venti.eid, venti.eventid, venti.repeat, venti.rRule, venti.summary, elements.id, elements.type, elements.enabled, elements.archived, elements.dateCreated, elements.dateUpdated, elements_i18n.slug, elements_i18n.uri, elements_i18n.enabled AS localeEnabled')->from('venti_events venti')->join('elements elements', 'elements.id = venti.eventid')->join('elements_i18n elements_i18n', 'elements_i18n.elementId = venti.eventid')->where('elements_i18n.locale = :locale', array(':locale' => $criteria->locale))->limit($criteria->limit)->offset($criteria->offset)->order($criteria->order);
     if ($elementType->hasContent()) {
         $contentTable = 'content';
         if ($contentTable) {
             $contentCols = 'content.id AS contentId';
             if ($elementType->hasTitles()) {
                 $contentCols .= ', content.title';
             }
             $fieldColumns = $this->getContentFieldColumnsForElementsQuery($criteria);
             foreach ($fieldColumns as $column) {
                 $contentCols .= ', content.' . $column['column'];
             }
             $query->addSelect($contentCols);
             $query->join($contentTable . ' content', 'content.elementId = elements.id');
             $query->andWhere('content.locale = :locale');
         }
     }
     if ($elementType->hasTitles() && $criteria->title) {
         $query->andWhere(DbHelper::parseParam('content.title', $criteria->title, $query->params));
     }
     if ($criteria->id) {
         $query->andWhere(DbHelper::parseParam('venti.eventid', $criteria->id, $query->params));
     }
     if ($criteria->eid) {
         $query->andWhere(DbHelper::parseParam('venti.eid', $criteria->eid, $query->params));
     }
     if ($criteria->isrepeat) {
         $query->andWhere(DbHelper::parseParam('venti.isrepeat', $criteria->isrepeat, $query->params));
     }
     if ($criteria->startDate) {
         $query->andWhere(DbHelper::parseDateParam('venti.startDate', $criteria->startDate, $query->params));
     }
     if ($criteria->endDate) {
         $query->andWhere(DbHelper::parseDateParam('venti.endDate', $criteria->endDate, $query->params));
     }
     if ($criteria->summary) {
         $query->andWhere(DbHelper::parseParam('venti.summary', $criteria->summary, $query->params));
     }
     if ($criteria->slug) {
         $query->andWhere(DbHelper::parseParam('elements_i18n.slug', $criteria->slug, $query->params));
     }
     if ($criteria->uri) {
         $query->andWhere(DbHelper::parseParam('elements_i18n.uri', $criteria->uri, $query->params));
     }
     if ($criteria->localeEnabled) {
         $query->andWhere('elements_i18n.enabled = 1');
     }
     if ($criteria->dateCreated) {
         $query->andWhere(DbHelper::parseDateParam('elements.dateCreated', $criteria->dateCreated, $query->params));
     }
     if ($criteria->dateUpdated) {
         $query->andWhere(DbHelper::parseDateParam('elements.dateUpdated', $criteria->dateUpdated, $query->params));
     }
     if ($criteria->archived) {
         $query->andWhere('elements.archived = 1');
     } else {
         $query->andWhere('elements.archived = 0');
         if ($criteria->status) {
             $statusConditions = array();
             $statuses = ArrayHelper::stringToArray($criteria->status);
             foreach ($statuses as $status) {
                 $status = StringHelper::toLowerCase($status);
                 // Is this a supported status?
                 if (in_array($status, array_keys($this->getStatuses()))) {
                     if ($status == BaseElementModel::ENABLED) {
                         $statusConditions[] = 'elements.enabled = 1';
                     } else {
                         if ($status == BaseElementModel::DISABLED) {
                             $statusConditions[] = 'elements.enabled = 0';
                         } else {
                             $elementStatusCondition = $this->getElementQueryStatusCondition($query, $status);
                             if ($elementStatusCondition) {
                                 $statusConditions[] = $elementStatusCondition;
                             } else {
                                 if ($elementStatusCondition === false) {
                                     return false;
                                 }
                             }
                         }
                     }
                 }
             }
             if ($statusConditions) {
                 if (count($statusConditions) == 1) {
                     $statusCondition = $statusConditions[0];
                 } else {
                     array_unshift($statusConditions, 'or');
                     $statusCondition = $statusConditions;
                 }
                 $query->andWhere($statusCondition);
             }
         }
     }
     // Relational params
     // ---------------------------------------------------------------------
     if ($criteria->relatedTo) {
         $relationParamParser = new ElementRelationParamParser();
         $relConditions = $relationParamParser->parseRelationParam($criteria->relatedTo, $query);
         if ($relConditions === false) {
             return false;
         }
         $query->andWhere($relConditions);
         // If there's only one relation criteria and it's specifically for grabbing target elements, allow the query
         // to order by the relation sort order
         if ($relationParamParser->isRelationFieldQuery()) {
             $query->addSelect('sources1.sortOrder');
         }
     }
     // Search
     // ---------------------------------------------------------------------
     if ($criteria->search) {
         $elementIds = $this->_getElementIdsFromQuery($query);
         $scoredSearchResults = $criteria->order == 'score';
         $filteredElementIds = craft()->search->filterElementIdsByQuery($elementIds, $criteria->search, $scoredSearchResults);
         // No results?
         if (!$filteredElementIds) {
             return array();
         }
         $query->andWhere(array('in', 'venti.eventid', $filteredElementIds));
         if ($scoredSearchResults) {
             // Order the elements in the exact order that SearchService returned them in
             $query->order(craft()->db->getSchema()->orderByColumnValues('venti.eventid', $filteredElementIds));
         }
     }
     return $query;
 }