/** * @return \SQLQuery */ public function getQuery() { if (!$this->queryCache) { if ($this->dataClass) { $dataQuery = new \DataQuery($this->dataClass); if ($this->stage) { $dataQuery->setQueryParam('Versioned.mode', 'stage'); $dataQuery->setQueryParam('Versioned.stage', $this->stage); } $this->queryCache = $dataQuery->getFinalisedQuery(); } else { $this->queryCache = new \SQLQuery(); } if (is_array($this->queryModifiers)) { foreach ($this->queryModifiers as $queryModifier) { if ($queryModifier instanceof QueryModifierInterface) { $queryModifier->modify($this->queryCache, $this->data, $this); } elseif (is_callable($queryModifier)) { $queryModifier($this->queryCache, $this->data, $this); } } } } return $this->queryCache; }
/** * Amend freshly created DataQuery objects with the "should filter admin?" option, current locale and frontend status * * @param SQLQuery * @param DataQuery */ public function augmentDataQueryCreation(SQLQuery $query, DataQuery $dataQuery) { $dataQuery->setQueryParam('Fluent.FilterAdmin', Fluent::config()->filter_admin); $dataQuery->setQueryParam('Fluent.Locale', Fluent::current_locale()); $dataQuery->setQueryParam('Fluent.IsFrontend', Fluent::is_frontend()); }
/** * Loads all the stub fields that an initial lazy load didn't load fully. * * @param tableClass Base table to load the values from. Others are joined as required. * Not specifying a tableClass will load all lazy fields from all tables. */ protected function loadLazyFields($tableClass = null) { if (!$tableClass) { $loaded = array(); foreach ($this->record as $key => $value) { if (strlen($key) > 5 && substr($key, -5) == '_Lazy' && !array_key_exists($value, $loaded)) { $this->loadLazyFields($value); $loaded[$value] = $value; } } return; } $dataQuery = new DataQuery($tableClass); // Reset query parameter context to that of this DataObject if ($params = $this->getSourceQueryParams()) { foreach ($params as $key => $value) { $dataQuery->setQueryParam($key, $value); } } // TableField sets the record ID to "new" on new row data, so don't try doing anything in that case if (!is_numeric($this->record['ID'])) { return false; } // Limit query to the current record, unless it has the Versioned extension, // in which case it requires special handling through augmentLoadLazyFields() if (!$this->hasExtension('Versioned')) { $dataQuery->where("\"{$tableClass}\".\"ID\" = {$this->record['ID']}")->limit(1); } $columns = array(); // Add SQL for fields, both simple & multi-value // TODO: This is copy & pasted from buildSQL(), it could be moved into a method $databaseFields = self::database_fields($tableClass); if ($databaseFields) { foreach ($databaseFields as $k => $v) { if (!isset($this->record[$k]) || $this->record[$k] === null) { $columns[] = $k; } } } if ($columns) { $query = $dataQuery->query(); $this->extend('augmentLoadLazyFields', $query, $dataQuery, $this); $this->extend('augmentSQL', $query, $dataQuery); $dataQuery->setQueriedColumns($columns); $newData = $dataQuery->execute()->record(); // Load the data into record if ($newData) { foreach ($newData as $k => $v) { if (in_array($k, $columns)) { $this->record[$k] = $v; $this->original[$k] = $v; unset($this->record[$k . '_Lazy']); } } // No data means that the query returned nothing; assign 'null' to all the requested fields } else { foreach ($columns as $k) { $this->record[$k] = null; $this->original[$k] = null; unset($this->record[$k . '_Lazy']); } } } }
/** * Amend freshly created DataQuery objects with the current locale and frontend status * * @param SQLQuery * @param DataQuery */ public function augmentDataQueryCreation(SQLQuery $query, DataQuery $dataQuery) { $dataQuery->setQueryParam('Fluent.Locale', Fluent::current_locale()); $dataQuery->setQueryParam('Fluent.IsFrontend', Fluent::is_frontend()); }
/** * Amend freshly created DataQuery objects with versioned-specific information */ function augmentDataQueryCreation(SQLQuery &$query, DataQuery &$dataQuery) { $parts = explode('.', Versioned::get_reading_mode()); if ($parts[0] == 'Archive') { $dataQuery->setQueryParam('Versioned.mode', 'archive'); $dataQuery->setQueryParam('Versioned.date', $parts[1]); } else { if ($parts[0] == 'Stage' && $parts[1] != $this->defaultStage && array_search($parts[1], $this->stages) !== false) { $dataQuery->setQueryParam('Versioned.mode', 'stage'); $dataQuery->setQueryParam('Versioned.stage', $parts[1]); } } }
/** * For lazy loaded fields requiring extra sql manipulation, ie versioning. * * @param SQLSelect $query * @param DataQuery $dataQuery * @param DataObject $dataObject */ public function augmentLoadLazyFields(SQLSelect &$query, DataQuery &$dataQuery = null, $dataObject) { // The VersionedMode local variable ensures that this decorator only applies to // queries that have originated from the Versioned object, and have the Versioned // metadata set on the query object. This prevents regular queries from // accidentally querying the *_versions tables. $versionedMode = $dataObject->getSourceQueryParam('Versioned.mode'); $dataClass = $dataQuery->dataClass(); $modesToAllowVersioning = array('all_versions', 'latest_versions', 'archive'); if (!empty($dataObject->Version) && (!empty($versionedMode) && in_array($versionedMode, $modesToAllowVersioning))) { $dataQuery->where("\"{$dataClass}\".\"RecordID\" = " . $dataObject->ID); $dataQuery->where("\"{$dataClass}\".\"Version\" = " . $dataObject->Version); $dataQuery->setQueryParam('Versioned.mode', 'all_versions'); } else { // Same behaviour as in DataObject->loadLazyFields $dataQuery->where("\"{$dataClass}\".\"ID\" = {$dataObject->ID}")->limit(1); } }
public function augmentDataQueryCreation(SQLSelect $sqlQuery, DataQuery $dataQuery) { $enabled = self::locale_filter_enabled(); $dataQuery->setQueryParam(self::QUERY_LOCALE_FILTER_ENABLED, $enabled); }
/** * Augment the the SQLQuery that is created by the DataQuery * @todo Should this all go into VersionedDataQuery? */ public function augmentSQL(SQLQuery &$query, DataQuery &$dataQuery = null) { $baseTable = ClassInfo::baseDataClass($dataQuery->dataClass()); switch ($dataQuery->getQueryParam('Versioned.mode')) { // Noop case '': break; // Reading a specific data from the archive // Reading a specific data from the archive case 'archive': $date = $dataQuery->getQueryParam('Versioned.date'); foreach ($query->getFrom() as $table => $dummy) { $query->renameTable($table, $table . '_versions'); $query->replaceText("\"{$table}_versions\".\"ID\"", "\"{$table}_versions\".\"RecordID\""); $query->replaceText("`{$table}_versions`.`ID`", "`{$table}_versions`.`RecordID`"); // Add all <basetable>_versions columns foreach (self::$db_for_versions_table as $name => $type) { $query->selectField(sprintf('"%s_versions"."%s"', $baseTable, $name), $name); } $query->selectField(sprintf('"%s_versions"."%s"', $baseTable, 'RecordID'), "ID"); if ($table != $baseTable) { $query->addWhere("\"{$table}_versions\".\"Version\" = \"{$baseTable}_versions\".\"Version\""); } } // Link to the version archived on that date $safeDate = Convert::raw2sql($date); $query->addWhere("\"{$baseTable}_versions\".\"Version\" IN \n\t\t\t\t\t(SELECT LatestVersion FROM \n\t\t\t\t\t\t(SELECT \n\t\t\t\t\t\t\t\"{$baseTable}_versions\".\"RecordID\", \n\t\t\t\t\t\t\tMAX(\"{$baseTable}_versions\".\"Version\") AS LatestVersion\n\t\t\t\t\t\t\tFROM \"{$baseTable}_versions\"\n\t\t\t\t\t\t\tWHERE \"{$baseTable}_versions\".\"LastEdited\" <= '{$safeDate}'\n\t\t\t\t\t\t\tGROUP BY \"{$baseTable}_versions\".\"RecordID\"\n\t\t\t\t\t\t) AS \"{$baseTable}_versions_latest\"\n\t\t\t\t\t\tWHERE \"{$baseTable}_versions_latest\".\"RecordID\" = \"{$baseTable}_versions\".\"RecordID\"\n\t\t\t\t\t)"); break; // Reading a specific stage (Stage or Live) // Reading a specific stage (Stage or Live) case 'stage': $stage = $dataQuery->getQueryParam('Versioned.stage'); if ($stage && $stage != $this->defaultStage) { foreach ($query->getFrom() as $table => $dummy) { // Only rewrite table names that are actually part of the subclass tree // This helps prevent rewriting of other tables that get joined in, in // particular, many_many tables if (class_exists($table) && ($table == $this->owner->class || is_subclass_of($table, $this->owner->class) || is_subclass_of($this->owner->class, $table))) { $query->renameTable($table, $table . '_' . $stage); } } } break; // Reading a specific stage, but only return items that aren't in any other stage // Reading a specific stage, but only return items that aren't in any other stage case 'stage_unique': $stage = $dataQuery->getQueryParam('Versioned.stage'); // Recurse to do the default stage behavior (must be first, we rely on stage renaming happening before // below) $dataQuery->setQueryParam('Versioned.mode', 'stage'); $this->augmentSQL($query, $dataQuery); // Now exclude any ID from any other stage. Note that we double rename to avoid the regular stage rename // renaming all subquery references to be Versioned.stage foreach ($this->stages as $excluding) { if ($excluding == $stage) { continue; } $tempName = 'ExclusionarySource_' . $excluding; $excludingTable = $baseTable . ($excluding && $excluding != $this->defaultStage ? "_{$excluding}" : ''); $query->addWhere('"' . $baseTable . '"."ID" NOT IN (SELECT "ID" FROM "' . $tempName . '")'); $query->renameTable($tempName, $excludingTable); } break; // Return all version instances // Return all version instances case 'all_versions': case 'latest_versions': foreach ($query->getFrom() as $alias => $join) { if ($alias != $baseTable) { $query->setJoinFilter($alias, "\"{$alias}\".\"RecordID\" = \"{$baseTable}_versions\".\"RecordID\"" . " AND \"{$alias}\".\"Version\" = \"{$baseTable}_versions\".\"Version\""); } $query->renameTable($alias, $alias . '_versions'); } // Add all <basetable>_versions columns foreach (self::$db_for_versions_table as $name => $type) { $query->selectField(sprintf('"%s_versions"."%s"', $baseTable, $name), $name); } $query->selectField(sprintf('"%s_versions"."%s"', $baseTable, 'RecordID'), "ID"); $query->addOrderBy(sprintf('"%s_versions"."%s"', $baseTable, 'Version')); // latest_version has one more step // Return latest version instances, regardless of whether they are on a particular stage // This provides "show all, including deleted" functonality if ($dataQuery->getQueryParam('Versioned.mode') == 'latest_versions') { $query->addWhere("\"{$alias}_versions\".\"Version\" IN \n\t\t\t\t\t(SELECT LatestVersion FROM \n\t\t\t\t\t\t(SELECT \n\t\t\t\t\t\t\t\"{$alias}_versions\".\"RecordID\", \n\t\t\t\t\t\t\tMAX(\"{$alias}_versions\".\"Version\") AS LatestVersion\n\t\t\t\t\t\t\tFROM \"{$alias}_versions\"\n\t\t\t\t\t\t\tGROUP BY \"{$alias}_versions\".\"RecordID\"\n\t\t\t\t\t\t) AS \"{$alias}_versions_latest\"\n\t\t\t\t\t\tWHERE \"{$alias}_versions_latest\".\"RecordID\" = \"{$alias}_versions\".\"RecordID\"\n\t\t\t\t\t)"); } break; default: throw new InvalidArgumentException("Bad value for query parameter Versioned.mode: " . $dataQuery->getQueryParam('Versioned.mode')); } }