diff --git a/.gitignore b/.gitignore index 489ded2..dd778a1 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,5 @@ Thumbs.db .settings/ .build/ .idea/ -nbproject/ \ No newline at end of file +nbproject/ +/vendor diff --git a/assets/.htaccess b/assets/.htaccess deleted file mode 100644 index ba5cc4f..0000000 --- a/assets/.htaccess +++ /dev/null @@ -1,7 +0,0 @@ - - Order allow,deny - Allow from all - - - Require all granted - diff --git a/classes/DC_Multilingual_Query.php b/classes/DC_Multilingual_Query.php deleted file mode 100644 index c81b23d..0000000 --- a/classes/DC_Multilingual_Query.php +++ /dev/null @@ -1,157 +0,0 @@ - - * @license http://opensource.org/licenses/lgpl-3.0.html LGPL - * @link http://github.com/terminal42/contao-dc_multilingual - */ - - -/** - * Create a Query for a DC_Multilingual table - */ -class DC_Multilingual_Query -{ - - protected $strTable = ''; - protected $arrFields = array(); - protected $arrWhere = array(); - protected $arrOrder = array(); - protected $arrJoin = array(); - - /** - * @var string Language to fetch - */ - public $language = ''; - - - /** - * Construct the QueryBuilder. - * - * If you need to reference the table you have - * * t1 for the base-fields - * * t2 for the language-fields - * - * @param string $strTable - * - * @return DC_Multilingual_Query current instance - */ - public function __construct($strTable) - { - $this->strTable = $strTable; - - // Load DataContainer if its not already done - if (!is_array($GLOBALS['TL_DCA'][$this->strTable])) { - $loader = new \DcaLoader($strTable); - $loader->load(); - } - - // add multilingual fields - foreach ($GLOBALS['TL_DCA'][$this->strTable]['fields'] as $field => $arrData) { - if ($arrData['eval']['translatableFor'] == '') continue; - $this->arrFields[] = "IFNULL(t2.$field, t1.$field) AS $field"; - } - - // set default language - $this->language = str_replace('-', '_', $GLOBALS['TL_LANGUAGE']); - - return $this; - } - - - /** - * Add a field - * - * @param string $strField fielname - * - * @return DC_Multilingual_Query current instance - */ - public function addField($strField) - { - $this->arrFields[] = $strField; - - return $this; - } - - - /** - * Add a WHERE-constraint - * all WHERE-pieces glued with AND - * - * @param string $strWhere - * - * @return DC_Multilingual_Query current instance - */ - public function addWhere($strWhere) - { - $this->arrWhere[] = $strWhere; - - return $this; - } - - - /** - * Add a JOIN-statement - * - * @param string $strJoin - * - * @return DC_Multilingual_Query current instance - */ - public function addJoin($strJoin) - { - $this->arrJoin[] = $strJoin; - - return $this; - } - - - /** - * Add a ORDER-constraint - * - * @param string $strOrderfield - * - * @return DC_Multilingual_Query current instance - */ - public function addOrder($strOrderfield) - { - $this->arrOrder[] = $strOrderfield; - - return $this; - } - - - /** - * Returns the query - * - * @return string - */ - public function getQuery() - { - $strPid = \DC_Multilingual::getPidColumnForTable($this->strTable); - $strLang = \DC_Multilingual::getLanguageColumnForTable($this->strTable); - - return " -SELECT t1.*, - " . implode(', ', $this->arrFields) . " -FROM {$this->strTable} AS t1 -LEFT OUTER JOIN {$this->strTable} AS t2 ON (t1.id=t2.$strPid AND t2.$strLang='{$this->language}') " . - implode(' ', $this->arrJoin) . " -WHERE t1.$strPid=0" . - (count($this->arrWhere) ? ' AND (' . implode(' AND ', $this->arrWhere) . ')' : '') . - (count($this->arrOrder) ? ' ORDER BY ' . implode(',', $this->arrOrder) : ''); - } - - - /** - * Returns the Database_Statement for the query - * - * @return \Database\Statement - */ - public function getStatement() - { - return \Database::getInstance()->prepare($this->getQuery()); - } -} diff --git a/classes/MultilingualQueryBuilder.php b/classes/MultilingualQueryBuilder.php deleted file mode 100644 index fbf4472..0000000 --- a/classes/MultilingualQueryBuilder.php +++ /dev/null @@ -1,146 +0,0 @@ - - * @license http://opensource.org/licenses/lgpl-3.0.html LGPL - * @link http://github.com/terminal42/contao-dc_multilingual - */ - - -/** - * Class MultilingualQueryBuilder - * - * The class reads the relation meta data from the DCA and creates the necessary - * JOIN queries to retrieve an object from the database. - */ -class MultilingualQueryBuilder -{ - - /** - * Build a query based on the given options - * - * @param array $arrOptions The options array - * - * @return string The query string - * - * @throws \BadMethodCallException - */ - public static function find($arrOptions) - { - $arrLanguageFields = static::getMultilingualFields($arrOptions['table']); - $strPid = \DC_Multilingual::getPidColumnForTable($arrOptions['table']); - $strLang = \DC_Multilingual::getLanguageColumnForTable($arrOptions['table']); - - // Use the current language if none provided - if (!isset($arrOptions['language'])) { - $arrOptions['language'] = str_replace('-', '_', $GLOBALS['TL_LANGUAGE']); - } - - // Consider the fallback language - $fallbackLang = \DC_Multilingual::getFallbackLanguageForTable($arrOptions['table']); - if (null !== $fallbackLang - && $fallbackLang === $arrOptions['language'] - ) { - $arrOptions['language'] = ''; - } - - - $strQuery = "SELECT dcm1.*" . (!empty($arrLanguageFields) ? (", " . implode(", ", static::generateFieldsSubquery($arrLanguageFields, 'dcm1', 'dcm2'))) : "") . " FROM " . $arrOptions['table'] . " AS dcm1"; - - // Fetch language fields - if (!empty($arrLanguageFields)) { - $strQuery .= " LEFT OUTER JOIN " . $arrOptions['table'] . " AS dcm2 ON (dcm1.id=dcm2." . $strPid . " AND dcm2.$strLang='" . $arrOptions['language'] . "')"; - } - - $strQuery .= " WHERE dcm1.$strPid=0"; - - // Where condition - if ($arrOptions['column'] !== null) { - $strQuery .= " AND " . str_replace($arrOptions['table'] . ".", "dcm1.", (is_array($arrOptions['column']) ? implode(" AND ", $arrOptions['column']) : $arrOptions['table'] . '.' . $arrOptions['column'] . "=?")); - } - - // Group by - if ($arrOptions['group'] !== null) { - $strQuery .= " GROUP BY " . str_replace($arrOptions['table'] . ".", "dcm1.", $arrOptions['group']); - } - - // Order by - if ($arrOptions['order'] !== null) { - $strQuery .= " ORDER BY " . str_replace($arrOptions['table'] . ".", "dcm1.", $arrOptions['order']); - } - - return $strQuery; - } - - - /** - * Build a query based on the given options to count the number of records - * - * @param array $arrOptions The options array - * - * @return string The query string - */ - public static function count($arrOptions) - { - $strPid = \DC_Multilingual::getPidColumnForTable($arrOptions['table']); - $strQuery = "SELECT COUNT(*) AS count FROM " . $arrOptions['table'] . " WHERE $strPid=0"; - - if ($arrOptions['column'] !== null) { - $strQuery .= " AND " . (is_array($arrOptions['column']) ? implode(" AND ", $arrOptions['column']) : $arrOptions['table'] . '.' . $arrOptions['column'] . "=?"); - } - - return $strQuery; - } - - - /** - * Get the multilingual fields as array - * - * @param string - * - * @return array - */ - public static function getMultilingualFields($strTable) - { - $arrFields = array(); - - \Controller::loadDataContainer($strTable); - - foreach ($GLOBALS['TL_DCA'][$strTable]['fields'] as $k => $v) { - if ($v['eval']['translatableFor']) { - $arrFields[] = $k; - } - } - - return $arrFields; - } - - - /** - * Generate the fields subquery and return it as array - * - * @param mixed - * @param string - * @param string - * @param string - * - * @return mixed - */ - public static function generateFieldsSubquery($varFields, $strFirstTable, $strSecondTable, $strPrefix = '') - { - if (is_array($varFields)) { - $arrReturn = array(); - - foreach ($varFields as $field) { - $arrReturn[] = static::generateFieldsSubquery($field, $strFirstTable, $strSecondTable, $strPrefix); - } - - return $arrReturn; - } - - return "IFNULL(" . $strSecondTable . "." . $varFields . ", " . $strFirstTable . "." . $varFields . ") AS " . (strlen($strPrefix) ? $strPrefix : "") . $varFields; - } -} diff --git a/composer.json b/composer.json index bbba4d0..744db5b 100644 --- a/composer.json +++ b/composer.json @@ -15,18 +15,16 @@ } ], "require":{ - "php":">=5.3.2", - "contao/core-bundle":"^3.3 || ^4.1", - "contao-community-alliance/composer-plugin":"^2.4.0 || 3.*" + "php":">=5.4", + "contao/core-bundle":"^4.1", + "doctrine/orm": "^2.5" }, - "replace": { - "contao-legacy/dc_multilingual": "self.version" - }, - "extra":{ - "contao": { - "sources":{ - "":"system/modules/dc_multilingual" - } + "autoload": { + "classmap": [ + "src/DC_Multilingual.php" + ], + "psr-4": { + "Terminal42\\DcMultilingualBundle\\": "src" } } } diff --git a/config/autoload.ini b/config/autoload.ini deleted file mode 100644 index 780bb3e..0000000 --- a/config/autoload.ini +++ /dev/null @@ -1,12 +0,0 @@ - -;; -; List modules which are required to be loaded beforehand -;; -requires[] = "core" - -;; -; Configure what you want the autoload creator to register -;; -register_namespaces = false -register_classes = false -register_templates = false diff --git a/config/autoload.php b/config/autoload.php deleted file mode 100644 index f090b3d..0000000 --- a/config/autoload.php +++ /dev/null @@ -1,22 +0,0 @@ - - * @license http://opensource.org/licenses/lgpl-3.0.html LGPL - * @link http://github.com/terminal42/contao-dc_multilingual - */ - - -/** - * Register the classes - */ -ClassLoader::addClasses(array -( - 'DC_Multilingual' => 'system/modules/dc_multilingual/drivers/DC_Multilingual.php', - 'MultilingualModel' => 'system/modules/dc_multilingual/models/MultilingualModel.php', - 'MultilingualQueryBuilder' => 'system/modules/dc_multilingual/classes/MultilingualQueryBuilder.php', - 'DC_Multilingual_Query' => 'system/modules/dc_multilingual/classes/DC_Multilingual_Query.php' -)); diff --git a/models/MultilingualModel.php b/models/MultilingualModel.php deleted file mode 100644 index 7868cf5..0000000 --- a/models/MultilingualModel.php +++ /dev/null @@ -1,60 +0,0 @@ - - * @license http://opensource.org/licenses/lgpl-3.0.html LGPL - * @link http://github.com/terminal42/contao-dc_multilingual - */ - - -/** - * Class MultilingualModel - * - * Provide methods to handle multilingual models - */ -abstract class MultilingualModel extends \Model -{ - - /** - * Use the multilingual query - * @param boolean - */ - protected static $blnMultilingual = true; - - - /** - * Build a query based on the given options - * - * @param array $arrOptions The options array - * - * @return string The query string - */ - protected static function buildFindQuery(array $arrOptions) - { - if (static::$blnMultilingual === false) { - return parent::buildFindQuery($arrOptions); - } - - return \MultilingualQueryBuilder::find($arrOptions); - } - - - /** - * Build a query based on the given options to count the number of records - * - * @param array $arrOptions The options array - * - * @return string The query string - */ - protected static function buildCountQuery(array $arrOptions) - { - if (static::$blnMultilingual === false) { - return parent::buildCountQuery($arrOptions); - } - - return \MultilingualQueryBuilder::count($arrOptions); - } -} diff --git a/src/DC_Multilingual.php b/src/DC_Multilingual.php new file mode 100644 index 0000000..245ccdc --- /dev/null +++ b/src/DC_Multilingual.php @@ -0,0 +1,11 @@ + + * @license http://opensource.org/licenses/lgpl-3.0.html LGPL + * @link http://github.com/terminal42/contao-dc_multilingual + */ +class DC_Multilingual extends \Terminal42\DcMultilingualBundle\Driver {} diff --git a/drivers/DC_Multilingual.php b/src/Driver.php similarity index 99% rename from drivers/DC_Multilingual.php rename to src/Driver.php index c859663..fef90b4 100644 --- a/drivers/DC_Multilingual.php +++ b/src/Driver.php @@ -9,21 +9,11 @@ * @link http://github.com/terminal42/contao-dc_multilingual */ +namespace Terminal42\DcMultilingualBundle; -/** - * Class DC_Multilingual - * - * Provide methods to handle multilingual DC_Table entries - * - * @copyright terminal42 gmbh 2011-2012 - * @author Yanick Witschi - * @author Andreas Schempp - * @author Kamil Kuzminski - * @package dc_multilingual - */ -class DC_Multilingual extends \DC_Table -{ +class Driver extends \DC_Table +{ /** * True if we are editing a language that is not the fallback * @@ -97,7 +87,7 @@ public function __construct($strTable) $GLOBALS['TL_DCA'][$this->strTable]['list']['sorting']['filter'][] = array($this->strLangColumn . '=?', ''); - $GLOBALS['TL_CSS'][] = 'system/modules/dc_multilingual/assets/backend.min.css'; + $GLOBALS['TL_CSS'][] = 'bundles/terminal42dcmultilingual/backend.min.css'; } @@ -885,10 +875,11 @@ public function ajaxTreeView($id, $level) * @param boolean * @param boolean * @param boolean + * @param array * * @return string */ - protected function generateTree($table, $id, $arrPrevNext, $blnHasSorting, $intMargin = 0, $arrClipboard = null, $blnCircularReference = false, $protectedPage = false, $blnNoRecursion = false) + protected function generateTree($table, $id, $arrPrevNext, $blnHasSorting, $intMargin = 0, $arrClipboard = null, $blnCircularReference = false, $protectedPage = false, $blnNoRecursion = false, $arrFound=array()) { $session = $this->Session->getData(); $node = ($GLOBALS['TL_DCA'][$this->strTable]['list']['sorting']['mode'] == 6) ? $this->strTable . '_' . $table . '_tree' : $this->strTable . '_tree'; diff --git a/src/Model/Multilingual.php b/src/Model/Multilingual.php new file mode 100644 index 0000000..29d677d --- /dev/null +++ b/src/Model/Multilingual.php @@ -0,0 +1,188 @@ + + * @license http://opensource.org/licenses/lgpl-3.0.html LGPL + * @link http://github.com/terminal42/contao-dc_multilingual + */ + +namespace Terminal42\DcMultilingualBundle\Model; + +use Doctrine\DBAL\Query\QueryBuilder; +use Terminal42\DcMultilingualBundle\QueryBuilder\MultilingualQueryBuilderFactoryInterface; + +class Multilingual extends \Model +{ + /** + * Build a query based on the given options + * + * @param array $options The options array + * + * @return string The query string + */ + protected static function buildFindQuery(array $options) + { + $qb = static::getQueryBuilder(); + + static::applyOptionsToQueryBuilder($qb, $options); + + return $qb->getQueryBuilder()->getSQL(); + + } + + /** + * Build a query based on the given options to count the number of records + * + * @param array $options The options array + * + * @return string The query string + */ + protected static function buildCountQuery(array $options) + { + $qb = static::getQueryBuilder(); + + static::applyOptionsToQueryBuilder($qb, $options); + + return $qb->getQueryBuilder()->getSQL(); + } + + /** + * Apply the model options to the query builder. + * + * @param QueryBuilder $qb + * @param array $options + */ + protected static function applyOptionsToQueryBuilder(QueryBuilder $qb, array $options) + { + // Use the current language if none provided + if (!isset($options['language'])) { + $options['language'] = str_replace('-', '_', $GLOBALS['TL_LANGUAGE']); + } + + // Consider the fallback language + $fallbackLang = static::getFallbackLanguage(); + if (null !== $fallbackLang && $fallbackLang === $options['language']) { + $options['language'] = ''; + } + + // Columns + if (null !== $options['column']) { + if (is_array($options['column'])) { + foreach ($options['column'] as $column) { + $qb->andWhere($column); + } + } else { + $qb->andWhere($options['table'] . '.' . options['column'] . '=?'); + } + } + + // Group by + if (null !== $options['group']) { + $qb->groupBy($options['group']); + } + + // Order by + if (null !== $options['order']) { + $qb->add('orderBy', $options['order']); + } + } + + /** + * Get QueryBuilder. + * + * @return \Terminal42\DcMultilingualBundle\MultilingualQueryBuilderInterface + */ + protected static function getQueryBuilder() + { + /** @var MultilingualQueryBuilderFactoryInterface $factory */ + $factory = \System::getContainer()->get('terminal42.dc_multilingual.querybuilder_factory'); + + return $factory->build( + static::getTable(), + static::getPidColumn(), + static::getLangColumn(), + static::getRegularFields(), + static::getTranslatableFields() + ); + } + + /** + * Get the regular fields + * + * @return array + */ + protected static function getRegularFields() + { + $extractor = \DcaExtractor::getInstance(static::getTable()); + + return array_keys($extractor->getFields()); + } + + /** + * Get the fields that are translatable. + * + * @return array + */ + protected static function getTranslatableFields() + { + $fields = []; + + foreach ($GLOBALS['TL_DCA'][static::getTable()]['fields'] as $field => $data) { + if (!isset($data['eval']['translatableFor'])) { + continue; + } + + $fields[] = $field; + } + + return $fields; + } + + /** + * Get the PID column. + * + * @return string + */ + protected static function getPidColumn() + { + if ($GLOBALS['TL_DCA'][static::getTable()]['config']['langPid']) { + + return $GLOBALS['TL_DCA'][static::getTable()]['config']['langPid']; + } + + return 'langPid'; + } + + /** + * Get the language column. + * + * @return string + */ + public static function getLangColumn() + { + if ($GLOBALS['TL_DCA'][static::getTable()]['config']['langColumn']) { + + return $GLOBALS['TL_DCA'][static::getTable()]['config']['langColumn']; + } + + return 'language'; + } + + /** + * Get the fallback language if available. + * + * @return string|null + */ + public static function getFallbackLanguage() + { + if ($GLOBALS['TL_DCA'][static::getTable()]['config']['fallbackLang']) { + + return $GLOBALS['TL_DCA'][static::getTable()]['config']['fallbackLang']; + } + + return null; + } +} diff --git a/src/QueryBuilder/MultilingualQueryBuilder.php b/src/QueryBuilder/MultilingualQueryBuilder.php new file mode 100644 index 0000000..eb5875c --- /dev/null +++ b/src/QueryBuilder/MultilingualQueryBuilder.php @@ -0,0 +1,122 @@ +qb = $qb; + $this->table = $table; + $this->langColumnName = $langColumnName; + $this->pidColumnName = $pidColumnName; + $this->regularFields = $regularFields; + $this->translatableFields = $translatableFields; + } + + /** + * Build the query for a simple count query. + */ + public function getQueryBuilderForCount() + { + $this->qb->resetQueryParts(); + + $this->qb->addSelect('COUNT(id) AS count') + ->from($this->table) + ->where("{$this->pidColumnName}=0"); + } + + /** + * Build the query builder for a find query. + * + * @param string $language + */ + public function buildQueryBuilderForFind($language) + { + $this->qb->resetQueryParts(); + + // Regular fields + foreach ($this->regularFields as $field) { + $this->qb->addSelect("t1.$field"); + } + + // Translatable fields + foreach ($this->translatableFields as $field) { + $this->qb->addSelect("IFNULL(t2.$field, t1.$field) AS $field"); + } + + $this->qb->from($this->table, 't1'); + $this->qb->add('join', [ + [ + 'joinType' => 'left outer', + 'joinTable' => $this->table, + 'joinAlias' => 't2', + 'condition' => "t1.id=t2.{$this->pidColumnName} AND t2.{$this->langColumnName}='$language')" + ] + ], true); + + $this->qb->where("t1.{$this->pidColumnName}=0"); + } + + /** + * @return QueryBuilder + */ + public function getQueryBuilder() + { + return $this->qb; + } +} diff --git a/src/QueryBuilder/MultilingualQueryBuilderFactory.php b/src/QueryBuilder/MultilingualQueryBuilderFactory.php new file mode 100644 index 0000000..8cab10e --- /dev/null +++ b/src/QueryBuilder/MultilingualQueryBuilderFactory.php @@ -0,0 +1,63 @@ +connection = $connection; + } + + /** + * Builds a MultilingualQueryBuilder. + * + * @param string $table + * @param string $pidColumnName + * @param string $langColumnName + * @param array $regularFields + * @param array $translatableFields + * + * @return MultilingualQueryBuilderInterface + */ + public function build( + $table, + $pidColumnName, + $langColumnName, + array $regularFields, + array $translatableFields + ) { + + return new MultilingualQueryBuilder( + $this->createQueryBuilder(), + $table, + $pidColumnName, + $langColumnName, + $regularFields, + $translatableFields + ); + } + + /** + * @return QueryBuilder + */ + protected function createQueryBuilder() + { + return new QueryBuilder($this->connection); + } +} diff --git a/src/QueryBuilder/MultilingualQueryBuilderFactoryInterface.php b/src/QueryBuilder/MultilingualQueryBuilderFactoryInterface.php new file mode 100644 index 0000000..7f3ab25 --- /dev/null +++ b/src/QueryBuilder/MultilingualQueryBuilderFactoryInterface.php @@ -0,0 +1,27 @@ +