/** * Build a query based on the given options * * @param array $arrOptions The options array * * @return string The query string */ public static function find($arrOptions) { $objBase = new \DcaExtractor($arrOptions['table']); if (!$objBase->hasRelations()) { $strQuery = "SELECT * FROM " . $arrOptions['table']; } else { $arrJoins = array(); $arrFields = array($arrOptions['table'] . ".*"); $intCount = 0; foreach ($objBase->getRelations() as $strKey => $arrConfig) { // Automatically join the single-relation records if ($arrConfig['load'] == 'eager' || $arrOptions['eager']) { if ($arrConfig['type'] == 'hasOne' || $arrConfig['type'] == 'belongsTo') { ++$intCount; $objRelated = new \DcaExtractor($arrConfig['table']); foreach (array_keys($objRelated->getFields()) as $strField) { $arrFields[] = 'j' . $intCount . '.' . $strField . ' AS ' . $strKey . '__' . $strField; } $arrJoins[] = " LEFT JOIN " . $arrConfig['table'] . " j{$intCount} ON " . $arrOptions['table'] . "." . $strKey . "=j{$intCount}.id"; } } } // Generate the query $strQuery = "SELECT " . implode(', ', $arrFields) . " FROM " . $arrOptions['table'] . implode("", $arrJoins); } // Where condition if ($arrOptions['column'] !== null) { $strQuery .= " WHERE " . (is_array($arrOptions['column']) ? implode(" AND ", $arrOptions['column']) : $arrOptions['table'] . '.' . $arrOptions['column'] . "=?"); } // Order by if ($arrOptions['order'] !== null) { $strQuery .= " ORDER BY " . $arrOptions['order']; } return $strQuery; }
/** * Auto-format model data based on DCA config * @param \Model * @param callable * @return \ArrayObject */ public static function generate(\Model $objModel = null, $varCallable = null) { if (null === $objModel) { return new \ArrayObject(array(), \ArrayObject::ARRAY_AS_PROPS); } $strTable = $objModel->getTable(); $objDca = new \DcaExtractor($strTable); $arrRelations = $objDca->getRelations(); $arrData = array(); \System::loadLanguageFile($strTable); \Controller::loadDataContainer($strTable); $arrFields =& $GLOBALS['TL_DCA'][$strTable]['fields']; foreach ($objModel->row() as $strField => $varValue) { $arrAdditional = array(); $strLabel = Format::dcaLabel($strTable, $strField); if (isset($arrRelations[$strField])) { $objRelated = $objModel->getRelated($strField); if ($objRelated == null) { $arrData[$strField] = new Plain('', $strLabel, $arrAdditional); } elseif ($objRelated instanceof \Model\Collection) { $arrCollection = array(); foreach ($objRelated as $objRelatedModel) { $arrCollection[] = new Relation($objRelatedModel, '', array(), $varCallable); } $arrData[$strField] = new Collection($arrCollection, $strLabel); } else { $arrData[$strField] = new Relation($objRelated, $strLabel, array(), $varCallable); } continue; } $arrAdditional['formatted'] = Format::dcaValue($strTable, $strField, $varValue); if (in_array($arrFields[$strField]['eval']['rgxp'], array('date', 'datim', 'time'))) { $arrData[$strField] = new Timestamp($varValue, $strLabel, $arrAdditional); } else { $arrData[$strField] = new Plain($varValue, $strLabel, $arrAdditional); } } if (null !== $varCallable) { call_user_func_array($varCallable, array($objModel, &$arrData)); } return new \ArrayObject($arrData, \ArrayObject::ARRAY_AS_PROPS); }
/** * Load the relations and optionally process a result set * * @param \Database_Result $objResult An optional database result */ public function __construct(\Database_Result $objResult = null) { parent::__construct(); $objRelations = new \DcaExtractor(static::$strTable); $this->arrRelations = $objRelations->getRelations(); if ($objResult !== null) { $this->arrData = $objResult->row(); // Look for joined fields foreach ($this->arrData as $k => $v) { if (strpos($k, '__') !== false) { list($key, $field) = explode('__', $k, 2); // Create the related model if (!isset($this->arrRelated[$key])) { $table = $this->arrRelations[$key]['table']; $strClass = $this->getModelClassFromTable($table); $this->arrRelated[$key] = new $strClass(); } $this->arrRelated[$key]->{$field} = $v; unset($this->arrData[$k]); } } } }
/** * Build model based on database result * * @param \Database\Result $objResult * * @return \Model */ public static function createModelFromDbResult(\Database\Result $objResult) { $strClass = ''; if (is_numeric($objResult->type)) { $objRelations = new \DcaExtractor(static::$strTable); $arrRelations = $objRelations->getRelations(); if (isset($arrRelations['type'])) { $strTypeClass = static::getClassFromTable($arrRelations['type']['table']); $objType = $strTypeClass::findOneBy($arrRelations['type']['field'], $objResult->type); if (null !== $objType) { $strClass = static::getClassForModelType($objType->class); } } } else { $strClass = static::getClassForModelType($objResult->type); } // Try to use the current class as fallback if ($strClass == '') { $strClass = get_called_class(); $objReflection = new \ReflectionClass($strClass); if ($objReflection->isAbstract()) { return null; } } $objModel = new $strClass($objResult); if (null !== static::$strInterface && !is_a($objModel, static::$strInterface)) { throw new \RuntimeException(get_class($objModel) . ' must implement interface ' . static::$strInterface); } return $objModel; }
/** * Return select statement to load product data including multilingual fields * @param array an array of columns * @return string */ protected static function buildFindQuery(array $arrOptions) { $objBase = new \DcaExtractor($arrOptions['table']); $arrJoins = array(); $arrFields = array($arrOptions['table'] . ".*", "IF(" . $arrOptions['table'] . ".pid>0, parent.type, " . $arrOptions['table'] . ".type) AS type", "'" . str_replace('-', '_', $GLOBALS['TL_LANGUAGE']) . "' AS language"); foreach (Attribute::getMultilingualFields() as $attribute) { $arrFields[] = "IFNULL(translation.{$attribute}, " . $arrOptions['table'] . ".{$attribute}) AS {$attribute}"; } foreach (Attribute::getFetchFallbackFields() as $attribute) { $arrFields[] = "{$arrOptions['table']}.{$attribute} AS {$attribute}_fallback"; } $arrFields[] = "c.sorting"; $arrJoins[] = " LEFT OUTER JOIN " . \Isotope\Model\ProductCategory::getTable() . " c ON {$arrOptions['table']}.id=c.pid"; $arrJoins[] = " LEFT OUTER JOIN " . $arrOptions['table'] . " translation ON " . $arrOptions['table'] . ".id=translation.pid AND translation.language='" . str_replace('-', '_', $GLOBALS['TL_LANGUAGE']) . "'"; $arrJoins[] = " LEFT OUTER JOIN " . $arrOptions['table'] . " parent ON " . $arrOptions['table'] . ".pid=parent.id"; if ($objBase->hasRelations()) { $intCount = 0; foreach ($objBase->getRelations() as $strKey => $arrConfig) { // Automatically join the single-relation records if ($arrConfig['load'] == 'eager' || $arrOptions['eager']) { if ($arrConfig['type'] == 'hasOne' || $arrConfig['type'] == 'belongsTo') { if (is_array($arrOptions['joinAliases']) && ($key = array_search($arrConfig['table'], $arrOptions['joinAliases'])) !== false) { $strJoinAlias = $key; unset($arrOptions['joinAliases'][$key]); } else { ++$intCount; $strJoinAlias = 'j' . $intCount; } $objRelated = new \DcaExtractor($arrConfig['table']); foreach (array_keys($objRelated->getFields()) as $strField) { $arrFields[] = $strJoinAlias . '.' . $strField . ' AS ' . $strKey . '__' . $strField; } $arrJoins[] = " LEFT JOIN " . $arrConfig['table'] . " {$strJoinAlias} ON " . $arrOptions['table'] . "." . $strKey . "={$strJoinAlias}.id"; } } } } // Generate the query $strQuery = "SELECT " . implode(', ', $arrFields) . " FROM " . $arrOptions['table'] . implode("", $arrJoins); // Where condition if (!is_array($arrOptions['column'])) { $arrOptions['column'] = array($arrOptions['table'] . '.' . $arrOptions['column'] . '=?'); } // The model must never find a language record $strQuery .= " WHERE {$arrOptions['table']}.language='' AND " . implode(" AND ", $arrOptions['column']); // Group by if ($arrOptions['group'] !== null) { $strQuery .= " GROUP BY " . $arrOptions['group']; } // Order by if ($arrOptions['order'] !== null) { $strQuery .= " ORDER BY " . $arrOptions['order']; } return $strQuery; }
/** * Load the relations and optionally process a result set * * @param \Database\Result $objResult An optional database result */ public function __construct(\Database\Result $objResult = null) { $this->arrModified = array(); $objDca = new \DcaExtractor(static::$strTable); $this->arrRelations = $objDca->getRelations(); if ($objResult !== null) { $arrRelated = array(); $arrData = $objResult->row(); // Look for joined fields foreach ($arrData as $k => $v) { if (strpos($k, '__') !== false) { list($key, $field) = explode('__', $k, 2); if (!isset($arrRelated[$key])) { $arrRelated[$key] = array(); } $arrRelated[$key][$field] = $v; unset($arrData[$k]); } } // Create the related models foreach ($arrRelated as $key => $row) { $table = $this->arrRelations[$key]['table']; $strClass = static::getClassFromTable($table); $intPk = $strClass::getPk(); // If the primary key is empty, set null (see #5356) if (!isset($row[$intPk])) { $this->arrRelated[$key] = null; } else { $objRelated = \Model\Registry::getInstance()->fetch($table, $row[$intPk]); if ($objRelated !== null) { $objRelated->mergeRow($row); } else { $objRelated = new $strClass(); $objRelated->setRow($row); } $this->arrRelated[$key] = $objRelated; } } $this->setRow($arrData); // see #5439 \Model\Registry::getInstance()->register($this); } }
/** * Create the DCA extract cache files */ public function generateDcaExtracts() { $included = array(); $arrExtracts = array(); // Only check the active modules (see #4541) foreach (\ModuleLoader::getActive() as $strModule) { $strDir = 'system/modules/' . $strModule . '/dca'; if (!is_dir(TL_ROOT . '/' . $strDir)) { continue; } foreach (scan(TL_ROOT . '/' . $strDir) as $strFile) { // Ignore non PHP files and files which have been included before if (strncmp($strFile, '.', 1) === 0 || substr($strFile, -4) != '.php' || in_array($strFile, $included)) { continue; } $strTable = substr($strFile, 0, -4); $objExtract = new \DcaExtractor($strTable); if ($objExtract->isDbTable()) { $arrExtracts[$strTable] = $objExtract; } $included[] = $strFile; } } // Create one file per table foreach ($arrExtracts as $strTable => $objExtract) { // Create the file $objFile = new \File('system/cache/sql/' . $strTable . '.php', true); $objFile->write("<?php\n\n"); // Meta $arrMeta = $objExtract->getMeta(); $objFile->append("\$this->arrMeta = array\n("); $objFile->append("\t'engine' => '{$arrMeta['engine']}',"); $objFile->append("\t'charset' => '{$arrMeta['charset']}',"); $objFile->append(');', "\n\n"); // Fields $arrFields = $objExtract->getFields(); $objFile->append("\$this->arrFields = array\n("); foreach ($arrFields as $field => $sql) { $sql = str_replace('"', '\\"', $sql); $objFile->append("\t'{$field}' => \"{$sql}\","); } $objFile->append(');', "\n\n"); // Order fields $arrFields = $objExtract->getOrderFields(); $objFile->append("\$this->arrOrderFields = array\n("); foreach ($arrFields as $field) { $objFile->append("\t'{$field}',"); } $objFile->append(');', "\n\n"); // Keys $arrKeys = $objExtract->getKeys(); $objFile->append("\$this->arrKeys = array\n("); foreach ($arrKeys as $field => $type) { $objFile->append("\t'{$field}' => '{$type}',"); } $objFile->append(');', "\n\n"); // Relations $arrRelations = $objExtract->getRelations(); $objFile->append("\$this->arrRelations = array\n("); foreach ($arrRelations as $field => $config) { $objFile->append("\t'{$field}' => array\n\t("); foreach ($config as $k => $v) { $objFile->append("\t\t'{$k}' => '{$v}',"); } $objFile->append("\t),"); } $objFile->append(');', "\n\n"); // Set the database table flag $objFile->append("\$this->blnIsDbTable = true;", "\n"); // Close the file (moves it to its final destination) $objFile->close(); } // Add a log entry $this->log('Generated the DCA extracts', __METHOD__, TL_CRON); }