PageRenderTime 43ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/app/code/core/Mage/Core/Model/Resource/Setup.php

https://bitbucket.org/claudiu_marginean/magento-hg-mirror
PHP | 679 lines | 414 code | 63 blank | 202 comment | 56 complexity | 1e5d2846a8ff5b46d27942812e3621e3 MD5 | raw file
Possible License(s): CC-BY-SA-3.0, LGPL-2.1, GPL-2.0, WTFPL
  1. <?php
  2. /**
  3. * Magento
  4. *
  5. * NOTICE OF LICENSE
  6. *
  7. * This source file is subject to the Open Software License (OSL 3.0)
  8. * that is bundled with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://opensource.org/licenses/osl-3.0.php
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@magentocommerce.com so we can send you a copy immediately.
  14. *
  15. * DISCLAIMER
  16. *
  17. * Do not edit or add to this file if you wish to upgrade Magento to newer
  18. * versions in the future. If you wish to customize Magento for your
  19. * needs please refer to http://www.magentocommerce.com for more information.
  20. *
  21. * @category Mage
  22. * @package Mage_Core
  23. * @copyright Copyright (c) 2010 Magento Inc. (http://www.magentocommerce.com)
  24. * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
  25. */
  26. /**
  27. * Resource setup model
  28. *
  29. * @author Magento Core Team <core@magentocommerce.com>
  30. */
  31. class Mage_Core_Model_Resource_Setup
  32. {
  33. const DEFAULT_SETUP_CONNECTION = 'core_setup';
  34. const VERSION_COMPARE_EQUAL = 0;
  35. const VERSION_COMPARE_LOWER = -1;
  36. const VERSION_COMPARE_GREATER = 1;
  37. protected $_resourceName;
  38. protected $_resourceConfig;
  39. protected $_connectionConfig;
  40. protected $_moduleConfig;
  41. /**
  42. * Call afterApplyAllUpdates method flag
  43. *
  44. * @var boolean
  45. */
  46. protected $_callAfterApplyAllUpdates = false;
  47. /**
  48. * Setup Connection
  49. *
  50. * @var Varien_Db_Adapter_Pdo_Mysql
  51. */
  52. protected $_conn;
  53. protected $_tables = array();
  54. protected $_setupCache = array();
  55. /**
  56. * Flag wich allow to detect that some schema update was applied dueting request
  57. *
  58. * @var bool
  59. */
  60. protected static $_hadUpdates;
  61. protected static $_schemaUpdatesChecked;
  62. public function __construct($resourceName)
  63. {
  64. $config = Mage::getConfig();
  65. $this->_resourceName = $resourceName;
  66. $this->_resourceConfig = $config->getResourceConfig($resourceName);
  67. $connection = $config->getResourceConnectionConfig($resourceName);
  68. if ($connection) {
  69. $this->_connectionConfig = $connection;
  70. } else {
  71. $this->_connectionConfig = $config->getResourceConnectionConfig(self::DEFAULT_SETUP_CONNECTION);
  72. }
  73. $modName = (string)$this->_resourceConfig->setup->module;
  74. $this->_moduleConfig = $config->getModuleConfig($modName);
  75. $connection = Mage::getSingleton('core/resource')->getConnection($this->_resourceName);
  76. /**
  77. * If module setup configuration wasn't loaded
  78. */
  79. if (!$connection) {
  80. $connection = Mage::getSingleton('core/resource')->getConnection('core_setup');
  81. }
  82. $this->_conn = $connection;
  83. }
  84. /**
  85. * Get connection object
  86. *
  87. * @return Varien_Db_Adapter_Pdo_Mysql
  88. */
  89. public function getConnection()
  90. {
  91. return $this->_conn;
  92. }
  93. /**
  94. * Add table placeholder/table name relation
  95. *
  96. * @param string $tableName
  97. * @param string $realTableName
  98. * @return Mage_Core_Model_Resource_Setup
  99. */
  100. public function setTable($tableName, $realTableName)
  101. {
  102. $this->_tables[$tableName] = $realTableName;
  103. return $this;
  104. }
  105. /**
  106. * Get table name by table placeholder
  107. *
  108. * @param string $tableName
  109. * @return string
  110. */
  111. public function getTable($tableName)
  112. {
  113. if (!isset($this->_tables[$tableName])) {
  114. $this->_tables[$tableName] = Mage::getSingleton('core/resource')->getTableName($tableName);
  115. }
  116. return $this->_tables[$tableName];
  117. }
  118. /**
  119. * Get core resource resource model
  120. *
  121. * @return Mage_Core_Model_Mysql4_Resource
  122. */
  123. protected function _getResource()
  124. {
  125. return Mage::getResourceSingleton('core/resource');
  126. }
  127. /**
  128. * Apply database updates whenever needed
  129. *
  130. * @return boolean
  131. */
  132. static public function applyAllUpdates()
  133. {
  134. Mage::app()->setUpdateMode(true);
  135. self::$_hadUpdates = false;
  136. $resources = Mage::getConfig()->getNode('global/resources')->children();
  137. $afterApplyUpdates = array();
  138. foreach ($resources as $resName=>$resource) {
  139. if (!$resource->setup) {
  140. continue;
  141. }
  142. $className = __CLASS__;
  143. if (isset($resource->setup->class)) {
  144. $className = $resource->setup->getClassName();
  145. }
  146. $setupClass = new $className($resName);
  147. $setupClass->applyUpdates();
  148. if ($setupClass->getCallAfterApplyAllUpdates()) {
  149. $afterApplyUpdates[] = $setupClass;
  150. }
  151. }
  152. foreach ($afterApplyUpdates as $setupClass) {
  153. $setupClass->afterApplyAllUpdates();
  154. }
  155. Mage::app()->setUpdateMode(false);
  156. self::$_schemaUpdatesChecked = true;
  157. return true;
  158. }
  159. /**
  160. * Apply database data updates whenever needed
  161. */
  162. static public function applyAllDataUpdates()
  163. {
  164. if (!self::$_schemaUpdatesChecked) {
  165. return;
  166. }
  167. $resources = Mage::getConfig()->getNode('global/resources')->children();
  168. foreach ($resources as $resName=>$resource) {
  169. if (!$resource->setup) {
  170. continue;
  171. }
  172. $className = __CLASS__;
  173. if (isset($resource->setup->class)) {
  174. $className = $resource->setup->getClassName();
  175. }
  176. $setupClass = new $className($resName);
  177. $setupClass->applyDataUpdates();
  178. }
  179. }
  180. /**
  181. * Apply data updates to the system after upgrading.
  182. *
  183. * @param string $fromVersion
  184. * @return Mage_Core_Model_Resource_Setup
  185. */
  186. public function applyDataUpdates()
  187. {
  188. $dataVer= $this->_getResource()->getDataVersion($this->_resourceName);
  189. $configVer = (string)$this->_moduleConfig->version;
  190. if ($dataVer !== false) {
  191. $status = version_compare($configVer, $dataVer);
  192. if ($status == self::VERSION_COMPARE_GREATER) {
  193. $this->_upgradeData($dataVer, $configVer);
  194. }
  195. } elseif ($configVer) {
  196. $this->_installData($configVer);
  197. }
  198. }
  199. /**
  200. * Apply module resource install, upgrade and data scripts
  201. */
  202. public function applyUpdates()
  203. {
  204. $dbVer = $this->_getResource()->getDbVersion($this->_resourceName);
  205. $configVer = (string)$this->_moduleConfig->version;
  206. // Module is installed
  207. if ($dbVer!==false) {
  208. $status = version_compare($configVer, $dbVer);
  209. switch ($status) {
  210. case self::VERSION_COMPARE_LOWER:
  211. $this->_rollbackResourceDb($configVer, $dbVer);
  212. break;
  213. case self::VERSION_COMPARE_GREATER:
  214. $this->_upgradeResourceDb($dbVer, $configVer);
  215. break;
  216. default:
  217. return true;
  218. break;
  219. }
  220. } elseif ($configVer) {
  221. $this->_installResourceDb($configVer);
  222. }
  223. }
  224. /**
  225. * Run data install scripts
  226. *
  227. * @param string $newVersion
  228. */
  229. protected function _installData($newVersion)
  230. {
  231. $oldVersion = $this->_modifyResourceDb('data-install', '', $newVersion);
  232. $this->_modifyResourceDb('data-upgrade', $oldVersion, $newVersion);
  233. $this->_getResource()->setDataVersion($this->_resourceName, $newVersion);
  234. }
  235. /**
  236. * Run data upgrade scripts
  237. *
  238. * @param string $oldVersion
  239. * @param string $newVersion
  240. */
  241. protected function _upgradeData($oldVersion, $newVersion)
  242. {
  243. $appliedVersion = $this->_modifyResourceDb('data-upgrade', $oldVersion, $newVersion);
  244. $this->_getResource()->setDataVersion($this->_resourceName, $newVersion);
  245. }
  246. /**
  247. * Run resource installation file
  248. *
  249. * @param string $version
  250. * @return boolean
  251. */
  252. protected function _installResourceDb($newVersion)
  253. {
  254. $oldVersion = $this->_modifyResourceDb('install', '', $newVersion);
  255. $this->_modifyResourceDb('upgrade', $oldVersion, $newVersion);
  256. $this->_getResource()->setDbVersion($this->_resourceName, $newVersion);
  257. }
  258. /**
  259. * Run resource upgrade files from $oldVersion to $newVersion
  260. *
  261. * @param string $oldVersion
  262. * @param string $newVersion
  263. */
  264. protected function _upgradeResourceDb($oldVersion, $newVersion)
  265. {
  266. $this->_modifyResourceDb('upgrade', $oldVersion, $newVersion);
  267. $this->_getResource()->setDbVersion($this->_resourceName, $newVersion);
  268. }
  269. /**
  270. * Roll back resource
  271. *
  272. * @param string $newVersion
  273. * @return bool
  274. */
  275. protected function _rollbackResourceDb($newVersion, $oldVersion)
  276. {
  277. $this->_modifyResourceDb('rollback', $newVersion, $oldVersion);
  278. }
  279. /**
  280. * Uninstall resource
  281. *
  282. * @param $version existing resource version
  283. * @return bool
  284. */
  285. protected function _uninstallResourceDb($version)
  286. {
  287. $this->_modifyResourceDb('uninstall', $version, '');
  288. }
  289. /**
  290. * Run module modification files. Return version of last applied upgrade (false if no upgrades applied)
  291. *
  292. * @param string $actionType install|upgrade|uninstall
  293. * @param string $fromVersion
  294. * @param string $toVersion
  295. * @return string | false
  296. */
  297. protected function _modifyResourceDb($actionType, $fromVersion, $toVersion)
  298. {
  299. $resModel = (string)$this->_connectionConfig->model;
  300. $modName = (string)$this->_moduleConfig[0]->getName();
  301. $sqlFilesDir = Mage::getModuleDir('sql', $modName).DS.$this->_resourceName;
  302. if (!is_dir($sqlFilesDir) || !is_readable($sqlFilesDir)) {
  303. return false;
  304. }
  305. // Read resource files
  306. $arrAvailableFiles = array();
  307. $sqlDir = dir($sqlFilesDir);
  308. while (false !== ($sqlFile = $sqlDir->read())) {
  309. $matches = array();
  310. if (preg_match('#^'.$resModel.'-'.$actionType.'-(.*)\.(sql|php)$#i', $sqlFile, $matches)) {
  311. $arrAvailableFiles[$matches[1]] = $sqlFile;
  312. }
  313. }
  314. $sqlDir->close();
  315. if (empty($arrAvailableFiles)) {
  316. return false;
  317. }
  318. // Get SQL files name
  319. $arrModifyFiles = $this->_getModifySqlFiles($actionType, $fromVersion, $toVersion, $arrAvailableFiles);
  320. if (empty($arrModifyFiles)) {
  321. return false;
  322. }
  323. $modifyVersion = false;
  324. foreach ($arrModifyFiles as $resourceFile) {
  325. $sqlFile = $sqlFilesDir.DS.$resourceFile['fileName'];
  326. $fileType = pathinfo($resourceFile['fileName'], PATHINFO_EXTENSION);
  327. // Execute SQL
  328. if ($this->_conn) {
  329. if (method_exists($this->_conn, 'disallowDdlCache')) {
  330. $this->_conn->disallowDdlCache();
  331. }
  332. try {
  333. switch ($fileType) {
  334. case 'sql':
  335. $sql = file_get_contents($sqlFile);
  336. if ($sql!='') {
  337. $result = $this->run($sql);
  338. } else {
  339. $result = true;
  340. }
  341. break;
  342. case 'php':
  343. $conn = $this->_conn;
  344. $result = include($sqlFile);
  345. break;
  346. default:
  347. $result = false;
  348. }
  349. if ($result) {
  350. if (strpos($actionType, 'data-') !== false) {
  351. $this->_getResource()->setDataVersion($this->_resourceName, $resourceFile['toVersion']);
  352. } else {
  353. $this->_getResource()->setDbVersion($this->_resourceName, $resourceFile['toVersion']);
  354. }
  355. }
  356. } catch (Exception $e){
  357. echo "<pre>".print_r($e,1)."</pre>";
  358. throw Mage::exception('Mage_Core', Mage::helper('core')->__('Error in file: "%s" - %s', $sqlFile, $e->getMessage()));
  359. }
  360. if (method_exists($this->_conn, 'allowDdlCache')) {
  361. $this->_conn->allowDdlCache();
  362. }
  363. }
  364. $modifyVersion = $resourceFile['toVersion'];
  365. }
  366. self::$_hadUpdates = true;
  367. return $modifyVersion;
  368. }
  369. /**
  370. * Get sql files for modifications
  371. *
  372. * @param $actionType
  373. * @return array
  374. */
  375. protected function _getModifySqlFiles($actionType, $fromVersion, $toVersion, $arrFiles)
  376. {
  377. $arrRes = array();
  378. switch ($actionType) {
  379. case 'install':
  380. case 'data-install':
  381. uksort($arrFiles, 'version_compare');
  382. foreach ($arrFiles as $version => $file) {
  383. if (version_compare($version, $toVersion)!==self::VERSION_COMPARE_GREATER) {
  384. $arrRes[0] = array('toVersion'=>$version, 'fileName'=>$file);
  385. }
  386. }
  387. break;
  388. case 'upgrade':
  389. case 'data-upgrade':
  390. uksort($arrFiles, 'version_compare');
  391. foreach ($arrFiles as $version => $file) {
  392. $version_info = explode('-', $version);
  393. // In array must be 2 elements: 0 => version from, 1 => version to
  394. if (count($version_info)!=2) {
  395. break;
  396. }
  397. $infoFrom = $version_info[0];
  398. $infoTo = $version_info[1];
  399. if (version_compare($infoFrom, $fromVersion)!==self::VERSION_COMPARE_LOWER
  400. && version_compare($infoTo, $toVersion)!==self::VERSION_COMPARE_GREATER) {
  401. $arrRes[] = array('toVersion'=>$infoTo, 'fileName'=>$file);
  402. }
  403. }
  404. break;
  405. case 'rollback':
  406. break;
  407. case 'uninstall':
  408. break;
  409. }
  410. return $arrRes;
  411. }
  412. /******************* UTILITY METHODS *****************/
  413. /**
  414. * Retrieve row or field from table by id or string and parent id
  415. *
  416. * @param string $table
  417. * @param string $idField
  418. * @param string|integer $id
  419. * @param string $field
  420. * @param string $parentField
  421. * @param string|integer $parentId
  422. * @return mixed|boolean
  423. */
  424. public function getTableRow($table, $idField, $id, $field=null, $parentField=null, $parentId=0)
  425. {
  426. if (strpos($table, '/')!==false) {
  427. $table = $this->getTable($table);
  428. }
  429. if (empty($this->_setupCache[$table][$parentId][$id])) {
  430. $sql = "select * from $table where $idField=?";
  431. if (!is_null($parentField)) {
  432. $sql .= $this->_conn->quoteInto(" and $parentField=?", $parentId);
  433. }
  434. $this->_setupCache[$table][$parentId][$id] = $this->_conn->fetchRow($sql, $id);
  435. }
  436. if (is_null($field)) {
  437. return $this->_setupCache[$table][$parentId][$id];
  438. }
  439. return isset($this->_setupCache[$table][$parentId][$id][$field]) ? $this->_setupCache[$table][$parentId][$id][$field] : false;
  440. }
  441. /**
  442. * Delete table row
  443. *
  444. * @param string $table
  445. * @param string $idField
  446. * @param string|int $id
  447. * @param null|string $parentField
  448. * @param int|string $parentId
  449. * @return Mage_Core_Model_Resource_Setup
  450. */
  451. public function deleteTableRow($table, $idField, $id, $parentField=null, $parentId=0)
  452. {
  453. if (strpos($table, '/')!==false) {
  454. $table = $this->getTable($table);
  455. }
  456. $condition = $this->_conn->quoteInto("$idField=?", $id);
  457. if ($parentField !== null) {
  458. $condition.= $this->_conn->quoteInto(" AND $parentField=?", $parentId);
  459. }
  460. $this->_conn->delete($table, $condition);
  461. if (isset($this->_setupCache[$table][$parentId][$id])) {
  462. unset($this->_setupCache[$table][$parentId][$id]);
  463. }
  464. return $this;
  465. }
  466. /**
  467. * Update one or more fields of table row
  468. *
  469. * @param string $table
  470. * @param string $idField
  471. * @param string|integer $id
  472. * @param string|array $field
  473. * @param mixed|null $value
  474. * @param string $parentField
  475. * @param string|integer $parentId
  476. * @return Mage_Eav_Model_Entity_Setup
  477. */
  478. public function updateTableRow($table, $idField, $id, $field, $value=null, $parentField=null, $parentId=0)
  479. {
  480. if (is_array($field)) {
  481. $updateArr = array();
  482. foreach ($field as $f=>$v) {
  483. $updateArr[] = $this->_conn->quoteInto("$f=?", $v);
  484. }
  485. $updateStr = join(', ', $updateArr);
  486. } else {
  487. $updateStr = $this->_conn->quoteInto("$field=?", $value);
  488. }
  489. if (strpos($table, '/')!==false) {
  490. $table = $this->getTable($table);
  491. }
  492. $sql = "update $table set $updateStr where ".$this->_conn->quoteInto("$idField=?", $id);
  493. if (!is_null($parentField)) {
  494. $sql .= $this->_conn->quoteInto(" and $parentField=?", $parentId);
  495. }
  496. $this->_conn->query($sql);
  497. if (isset($this->_setupCache[$table][$parentId][$id])) {
  498. if (is_array($field)) {
  499. $this->_setupCache[$table][$parentId][$id] = array_merge($this->_setupCache[$table][$parentId][$id], $field);
  500. } else {
  501. $this->_setupCache[$table][$parentId][$id][$field] = $value;
  502. }
  503. }
  504. return $this;
  505. }
  506. public function updateTable($table, $conditionExpr, $valueExpr)
  507. {
  508. if (strpos($table, '/')!==false) {
  509. $table = $this->getTable($table);
  510. }
  511. $sql = 'update ' . $table . ' set ' . $valueExpr . ' where ' . $conditionExpr;
  512. $this->_conn->query($sql);
  513. return $this;
  514. }
  515. public function tableExists($table)
  516. {
  517. $select = $this->getConnection()->quoteInto('SHOW TABLES LIKE ?', $table);
  518. $result = $this->getConnection()->fetchOne($select);
  519. return !empty($result);
  520. }
  521. /******************* CONFIG *****************/
  522. public function addConfigField($path, $label, array $data=array(), $default=null)
  523. {
  524. $data['level'] = sizeof(explode('/', $path));
  525. $data['path'] = $path;
  526. $data['frontend_label'] = $label;
  527. if ($id = $this->getTableRow('core/config_field', 'path', $path, 'field_id')) {
  528. $this->updateTableRow('core/config_field', 'field_id', $id, $data);
  529. } else {
  530. if (empty($data['sort_order'])) {
  531. $sql = "select max(sort_order) cnt from ".$this->getTable('core/config_field')." where level=".($data['level']+1);
  532. if ($data['level']>1) {
  533. $sql.= $this->_conn->quoteInto(" and path like ?", dirname($path).'/%');
  534. }
  535. $result = $this->_conn->raw_fetchRow($sql);
  536. $this->_conn->fetchAll($sql);
  537. #print_r($result); die;
  538. $data['sort_order'] = $result['cnt']+1;
  539. /*
  540. // Triggers "Command out of sync" mysql error for next statement!?!?
  541. $data['sort_order'] = $this->_conn->fetchOne("select max(sort_order)
  542. from ".$this->getTable('core/config_field')."
  543. where level=?".$parentWhere, $data['level'])+1;
  544. */
  545. }
  546. #$this->_conn->raw_query("insert into ".$this->getTable('core/config_field')." (".join(',', array_keys($data)).") values ('".join("','", array_values($data))."')");
  547. $this->_conn->insert($this->getTable('core/config_field'), $data);
  548. }
  549. if (!is_null($default)) {
  550. $this->setConfigData($path, $default);
  551. }
  552. return $this;
  553. }
  554. public function setConfigData($path, $value, $scope='default', $scopeId=0, $inherit=0)
  555. {
  556. $this->_conn->showTableStatus($this->getTable('core/config_data')); // this is a fix for mysql 4.1
  557. $this->_conn->raw_query("replace into ".$this->getTable('core/config_data')." (scope, scope_id, path, value) values ('$scope', $scopeId, '$path', '$value')");
  558. return $this;
  559. }
  560. /**
  561. * Delete config field values
  562. *
  563. * @param string $path
  564. * @param string $scope (default|stores|websites|config)
  565. * @return Mage_Core_Model_Resource_Setup
  566. */
  567. public function deleteConfigData($path, $scope=null)
  568. {
  569. $sql = "delete from ".$this->getTable('core/config_data')." where path='".$path."'";
  570. if ($scope) {
  571. $sql.= " and scope='".$scope."'";
  572. }
  573. $this->_conn->raw_query($sql);
  574. return $this;
  575. }
  576. public function run($sql)
  577. {
  578. $this->_conn->multi_query($sql);
  579. return $this;
  580. }
  581. public function startSetup()
  582. {
  583. $this->_conn->multi_query("SET SQL_MODE='';
  584. SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
  585. SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO';
  586. ");
  587. return $this;
  588. }
  589. public function endSetup()
  590. {
  591. $this->_conn->multi_query("
  592. SET SQL_MODE=IFNULL(@OLD_SQL_MODE,'');
  593. SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS=0, 0, 1);
  594. ");
  595. return $this;
  596. }
  597. /**
  598. * Check call afterApplyAllUpdates method for setup class
  599. *
  600. * @return boolean
  601. */
  602. public function getCallAfterApplyAllUpdates()
  603. {
  604. return $this->_callAfterApplyAllUpdates;
  605. }
  606. /**
  607. * Run each time after applying of all updates,
  608. * if setup model setted $_callAfterApplyAllUpdates flag to true
  609. *
  610. * @return Mage_Core_Model_Resource_Setup
  611. */
  612. public function afterApplyAllUpdates()
  613. {
  614. return $this;
  615. }
  616. }