PageRenderTime 52ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/app/code/core/Mage/Core/Model/Mysql4/Abstract.php

https://bitbucket.org/claudiu_marginean/magento-hg-mirror
PHP | 761 lines | 370 code | 75 blank | 316 comment | 56 complexity | ca0ec2454c8aa6befcb1dba769778103 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. * Abstract resource model class
  28. *
  29. *
  30. * @category Mage
  31. * @package Mage_Core
  32. * @author Magento Core Team <core@magentocommerce.com>
  33. */
  34. abstract class Mage_Core_Model_Mysql4_Abstract extends Mage_Core_Model_Resource_Abstract
  35. {
  36. const CHECKSUM_KEY_NAME = 'Checksum';
  37. /**
  38. * Cached resources singleton
  39. *
  40. * @var Mage_Core_Model_Resource
  41. */
  42. protected $_resources;
  43. /**
  44. * Prefix for resources that will be used in this resource model
  45. *
  46. * @var string
  47. */
  48. protected $_resourcePrefix;
  49. /**
  50. * Connections cache for this resource model
  51. *
  52. * @var array
  53. */
  54. protected $_connections = array();
  55. /**
  56. * Resource model name that contains entities (names of tables)
  57. *
  58. * @var string
  59. */
  60. protected $_resourceModel;
  61. /**
  62. * Tables used in this resource model
  63. *
  64. * @var array
  65. */
  66. protected $_tables = array();
  67. /**
  68. * Main table name
  69. *
  70. * @var string
  71. */
  72. protected $_mainTable;
  73. /**
  74. * Main table primary key field name
  75. *
  76. * @var string
  77. */
  78. protected $_idFieldName;
  79. /**
  80. * Primery key auto increment flag
  81. *
  82. * @var bool
  83. */
  84. protected $_isPkAutoIncrement = true;
  85. /**
  86. * Use is object new method for save of object
  87. *
  88. * @var boolean
  89. */
  90. protected $_useIsObjectNew = false;
  91. /**
  92. * Fields List for update in forsedSave
  93. *
  94. * @var array
  95. */
  96. protected $_fieldsForUpdate = array();
  97. protected $_mainTableFields;
  98. /**
  99. * Main table unique keys field names
  100. *
  101. * could array(
  102. * array('field' => 'db_field_name1', 'title' => 'Field 1 should be unique')
  103. * array('field' => 'db_field_name2', 'title' => 'Field 2 should be unique')
  104. * array(
  105. * 'field' => array('db_field_name3', 'db_field_name3'),
  106. * 'title' => 'Field 3 and Field 4 combination should be unique'
  107. * )
  108. * )
  109. *
  110. * or string 'my_field_name' - will be autoconverted to
  111. * array( array( 'field' => 'my_field_name', 'title' => 'my_field_name' ) )
  112. *
  113. * @var array
  114. */
  115. protected $_uniqueFields = null;
  116. /**
  117. * Serializable fields declaration
  118. *
  119. * Structure: array(
  120. * <field_name> => array(
  121. * <default_value_for_serialization>,
  122. * <default_for_unserialization>,
  123. * <whether_to_unset_empty_when serializing> // optional parameter
  124. * ),
  125. * )
  126. *
  127. * @var array
  128. */
  129. protected $_serializableFields = array();
  130. /**
  131. * Standard resource model initialization
  132. *
  133. * @param string $mainTable
  134. * @param string $idFieldName
  135. * @return Mage_Core_Model_Mysql4_Abstract
  136. */
  137. protected function _init($mainTable, $idFieldName)
  138. {
  139. $this->_setMainTable($mainTable, $idFieldName);
  140. }
  141. /**
  142. * Initialize connections and tables for this resource model
  143. *
  144. * If one or both arguments are string, will be used as prefix
  145. * If $tables is null and $connections is string, $tables will be the same
  146. *
  147. * @param string|array $connections
  148. * @param string|array|null $tables
  149. * @return Mage_Core_Model_Mysql4_Abstract
  150. */
  151. protected function _setResource($connections, $tables=null)
  152. {
  153. $this->_resources = Mage::getSingleton('core/resource');
  154. if (is_array($connections)) {
  155. foreach ($connections as $k=>$v) {
  156. $this->_connections[$k] = $this->_resources->getConnection($v);
  157. }
  158. }
  159. elseif (is_string($connections)) {
  160. $this->_resourcePrefix = $connections;
  161. }
  162. if (is_null($tables) && is_string($connections)) {
  163. $this->_resourceModel = $this->_resourcePrefix;
  164. }
  165. elseif (is_array($tables)) {
  166. foreach ($tables as $k=>$v) {
  167. $this->_tables[$k] = $this->_resources->getTableName($v);
  168. }
  169. }
  170. elseif (is_string($tables)) {
  171. $this->_resourceModel = $tables;
  172. }
  173. return $this;
  174. }
  175. /**
  176. * Set main entity table name and primary key field name
  177. *
  178. * If field name is ommited {table_name}_id will be used
  179. *
  180. * @param string $mainTable
  181. * @param string|null $idFieldName
  182. * @return Mage_Core_Model_Mysql4_Abstract
  183. */
  184. protected function _setMainTable($mainTable, $idFieldName=null)
  185. {
  186. $mainTableArr = explode('/', $mainTable);
  187. if (!empty($mainTableArr[1])) {
  188. if (empty($this->_resourceModel)) {
  189. $this->_setResource($mainTableArr[0]);
  190. }
  191. $this->_setMainTable($mainTableArr[1], $idFieldName);
  192. } else {
  193. $this->_mainTable = $mainTable;
  194. if (is_null($idFieldName)) {
  195. $idFieldName = $mainTable.'_id';
  196. }
  197. $this->_idFieldName = $idFieldName;
  198. }
  199. return $this;
  200. }
  201. /**
  202. * Get primary key field name
  203. *
  204. * @return string
  205. */
  206. public function getIdFieldName()
  207. {
  208. if (empty($this->_idFieldName)) {
  209. Mage::throwException(Mage::helper('core')->__('Empty identifier field name'));
  210. }
  211. return $this->_idFieldName;
  212. }
  213. /**
  214. * Get main table name
  215. *
  216. * @return string
  217. */
  218. public function getMainTable()
  219. {
  220. if (empty($this->_mainTable)) {
  221. Mage::throwException(Mage::helper('core')->__('Empty main table name'));
  222. }
  223. return $this->getTable($this->_mainTable);
  224. }
  225. /**
  226. * Get table name for the entity
  227. *
  228. * @param string $entityName
  229. * @return string
  230. */
  231. public function getTable($entityName)
  232. {
  233. if (isset($this->_tables[$entityName])) {
  234. return $this->_tables[$entityName];
  235. }
  236. if (strpos($entityName, '/')) {
  237. $this->_tables[$entityName] = $this->_resources->getTableName($entityName);
  238. } elseif (!empty($this->_resourceModel)) {
  239. $this->_tables[$entityName] = $this->_resources->getTableName(
  240. $this->_resourceModel.'/'.$entityName);
  241. } else {
  242. $this->_tables[$entityName] = $entityName;
  243. }
  244. return $this->_tables[$entityName];
  245. }
  246. /**
  247. * Retrieve table name for the entity separated value
  248. *
  249. * @param string $entityName
  250. * @param string $valueType
  251. * @return string
  252. */
  253. public function getValueTable($entityName, $valueType)
  254. {
  255. return $this->getTable($entityName) . '_' . $valueType;
  256. }
  257. /**
  258. * Get connection by name or type
  259. *
  260. * @param string $connectionName
  261. * @return Zend_Db_Adapter_Abstract
  262. */
  263. protected function _getConnection($connectionName)
  264. {
  265. if (isset($this->_connections[$connectionName])) {
  266. return $this->_connections[$connectionName];
  267. }
  268. if (!empty($this->_resourcePrefix)) {
  269. $this->_connections[$connectionName] = $this->_resources->getConnection(
  270. $this->_resourcePrefix.'_'.$connectionName);
  271. } else {
  272. $this->_connections[$connectionName] = $this->_resources->getConnection($connectionName);
  273. }
  274. return $this->_connections[$connectionName];
  275. }
  276. /**
  277. * Retrieve connection for read data
  278. *
  279. * @return Varien_Db_Adapter_Pdo_Mysql
  280. */
  281. protected function _getReadAdapter()
  282. {
  283. return $this->_getConnection('read');
  284. }
  285. /**
  286. * Retrieve connection for write data
  287. *
  288. * @return Varien_Db_Adapter_Pdo_Mysql
  289. */
  290. protected function _getWriteAdapter()
  291. {
  292. return $this->_getConnection('write');
  293. }
  294. /**
  295. * Temporary resolving collection compatibility
  296. *
  297. * @return Varien_Db_Adapter_Pdo_Mysql
  298. */
  299. public function getReadConnection()
  300. {
  301. return $this->_getReadAdapter();
  302. }
  303. /**
  304. * Load an object
  305. *
  306. * @param Mage_Core_Model_Abstract $object
  307. * @param mixed $value
  308. * @param string $field field to load by (defaults to model id)
  309. * @return Mage_Core_Model_Mysql4_Abstract
  310. */
  311. public function load(Mage_Core_Model_Abstract $object, $value, $field=null)
  312. {
  313. if (is_null($field)) {
  314. $field = $this->getIdFieldName();
  315. }
  316. $read = $this->_getReadAdapter();
  317. if ($read && !is_null($value)) {
  318. $select = $this->_getLoadSelect($field, $value, $object);
  319. $data = $read->fetchRow($select);
  320. if ($data) {
  321. $object->setData($data);
  322. }
  323. }
  324. $this->unserializeFields($object);
  325. $this->_afterLoad($object);
  326. return $this;
  327. }
  328. /**
  329. * Retrieve select object for load object data
  330. *
  331. * @param string $field
  332. * @param mixed $value
  333. * @return Zend_Db_Select
  334. */
  335. protected function _getLoadSelect($field, $value, $object)
  336. {
  337. $select = $this->_getReadAdapter()->select()
  338. ->from($this->getMainTable())
  339. ->where($this->getMainTable().'.'.$field.'=?', $value);
  340. return $select;
  341. }
  342. /**
  343. * Save object object data
  344. *
  345. * @param Mage_Core_Model_Abstract $object
  346. * @return Mage_Core_Model_Mysql4_Abstract
  347. */
  348. public function save(Mage_Core_Model_Abstract $object)
  349. {
  350. if ($object->isDeleted()) {
  351. return $this->delete($object);
  352. }
  353. $this->_serializeFields($object);
  354. $this->_beforeSave($object);
  355. $this->_checkUnique($object);
  356. if (!is_null($object->getId()) && (!$this->_useIsObjectNew || !$object->isObjectNew())) {
  357. $condition = $this->_getWriteAdapter()->quoteInto($this->getIdFieldName().'=?', $object->getId());
  358. /**
  359. * Not auto increment primary key support
  360. */
  361. if ($this->_isPkAutoIncrement) {
  362. $this->_getWriteAdapter()->update($this->getMainTable(), $this->_prepareDataForSave($object), $condition);
  363. } else {
  364. $select = $this->_getWriteAdapter()->select()
  365. ->from($this->getMainTable(), array($this->getIdFieldName()))
  366. ->where($condition);
  367. if ($this->_getWriteAdapter()->fetchOne($select) !== false) {
  368. $this->_getWriteAdapter()->update($this->getMainTable(), $this->_prepareDataForSave($object), $condition);
  369. } else {
  370. $this->_getWriteAdapter()->insert($this->getMainTable(), $this->_prepareDataForSave($object));
  371. }
  372. }
  373. } else {
  374. $this->_getWriteAdapter()->insert($this->getMainTable(), $this->_prepareDataForSave($object));
  375. $object->setId($this->_getWriteAdapter()->lastInsertId($this->getMainTable()));
  376. if ($this->_useIsObjectNew) {
  377. $object->isObjectNew(false);
  378. }
  379. }
  380. $this->unserializeFields($object);
  381. $this->_afterSave($object);
  382. return $this;
  383. }
  384. /**
  385. * Forsed save object data
  386. * forsed update If duplicate unique key data
  387. *
  388. * @param Mage_Core_Model_Abstract $object
  389. * @return Mage_Core_Model_Mysql4_Abstract
  390. */
  391. public function forsedSave(Mage_Core_Model_Abstract $object)
  392. {
  393. $this->_beforeSave($object);
  394. // update
  395. if (!is_null($object->getId()) && $this->_isPkAutoIncrement) {
  396. $condition = $this->_getWriteAdapter()->quoteInto($this->getIdFieldName().'=?', $object->getId());
  397. $this->_getWriteAdapter()->update($this->getMainTable(), $this->_prepareDataForSave($object), $condition);
  398. }
  399. else {
  400. $this->_getWriteAdapter()->insertOnDuplicate($this->getMainTable(), $this->_prepareDataForSave($object), $this->_fieldsForUpdate);
  401. $object->setId($this->_getWriteAdapter()->lastInsertId($this->getMainTable()));
  402. }
  403. $this->_afterSave($object);
  404. return $this;
  405. }
  406. /**
  407. * Delete the object
  408. *
  409. * @param Varien_Object $object
  410. * @return Mage_Core_Model_Mysql4_Abstract
  411. */
  412. public function delete(Mage_Core_Model_Abstract $object)
  413. {
  414. $this->_beforeDelete($object);
  415. $this->_getWriteAdapter()->delete(
  416. $this->getMainTable(),
  417. $this->_getWriteAdapter()->quoteInto($this->getIdFieldName().'=?', $object->getId())
  418. );
  419. $this->_afterDelete($object);
  420. return $this;
  421. }
  422. /**
  423. * Add unique field restriction
  424. *
  425. * @param array|string $field
  426. * @return Mage_Core_Model_Mysql4_Abstract
  427. */
  428. public function addUniqueField($field)
  429. {
  430. if (is_null($this->_uniqueFields)) {
  431. $this->_initUniqueFields();
  432. }
  433. if(is_array($this->_uniqueFields) ) {
  434. $this->_uniqueFields[] = $field;
  435. }
  436. return $this;
  437. }
  438. /**
  439. * Reset unique fields restrictions
  440. *
  441. * @return Mage_Core_Model_Mysql4_Abstract
  442. */
  443. public function resetUniqueField()
  444. {
  445. $this->_uniqueFields = array();
  446. return $this;
  447. }
  448. /**
  449. * Unserialize serializeable object fields
  450. *
  451. * @param Mage_Core_Model_Abstract $object
  452. */
  453. public function unserializeFields(Mage_Core_Model_Abstract $object)
  454. {
  455. foreach ($this->_serializableFields as $field => $parameters) {
  456. list($serializeDefault, $unserializeDefault) = $parameters;
  457. $this->_unserializeField($object, $field, $unserializeDefault);
  458. }
  459. }
  460. /**
  461. * Initialize unique fields
  462. *
  463. * @return Mage_Core_Model_Mysql4_Abstract
  464. */
  465. protected function _initUniqueFields()
  466. {
  467. $this->_uniqueFields = array();
  468. return $this;
  469. }
  470. /**
  471. * Get configuration of all unique fields
  472. *
  473. * @return array
  474. */
  475. public function getUniqueFields()
  476. {
  477. if (is_null($this->_uniqueFields)) {
  478. $this->_initUniqueFields();
  479. }
  480. return $this->_uniqueFields;
  481. }
  482. /**
  483. * Prepare data for save
  484. *
  485. * @param Mage_Core_Model_Abstract $object
  486. * @return array
  487. */
  488. protected function _prepareDataForSave(Mage_Core_Model_Abstract $object)
  489. {
  490. return $this->_prepareDataForTable($object, $this->getMainTable());
  491. }
  492. /**
  493. * Prepare data for passed table
  494. *
  495. * @param Varien_Object $object
  496. * @param string $table
  497. * @return array
  498. */
  499. protected function _prepareDataForTable(Varien_Object $object, $table)
  500. {
  501. $data = array();
  502. $fields = $this->_getWriteAdapter()->describeTable($table);
  503. foreach (array_keys($fields) as $field) {
  504. if ($object->hasData($field)) {
  505. $fieldValue = $object->getData($field);
  506. if ($fieldValue instanceof Zend_Db_Expr) {
  507. $data[$field] = $fieldValue;
  508. } else {
  509. if (null !== $fieldValue) {
  510. $data[$field] = $this->_prepareValueForSave($fieldValue, $fields[$field]['DATA_TYPE']);
  511. } elseif (!empty($fields[$field]['NULLABLE'])) {
  512. $data[$field] = null;
  513. }
  514. }
  515. }
  516. }
  517. return $data;
  518. }
  519. /**
  520. * Check that model data fields that can be saved
  521. * has really changed comparing with origData
  522. *
  523. * @param Mage_Core_Model_Abstract $object
  524. * @return boolean
  525. */
  526. public function hasDataChanged($object)
  527. {
  528. if (!$object->getOrigData()) {
  529. return true;
  530. }
  531. $fields = $this->_getWriteAdapter()->describeTable($this->getMainTable());
  532. foreach (array_keys($fields) as $field) {
  533. if ($object->getOrigData($field) != $object->getData($field)) {
  534. return true;
  535. }
  536. }
  537. return false;
  538. }
  539. /**
  540. * Prepare value for save
  541. *
  542. * @param mixed $value
  543. * @param string $type
  544. * @return mixed
  545. */
  546. protected function _prepareValueForSave($value, $type)
  547. {
  548. if ($type == 'decimal') {
  549. $value = Mage::app()->getLocale()->getNumber($value);
  550. }
  551. return $value;
  552. }
  553. /**
  554. * Check for unique values existence
  555. *
  556. * @param Varien_Object $object
  557. * @return Mage_Core_Model_Mysql4_Abstract
  558. * @throws Mage_Core_Exception
  559. */
  560. protected function _checkUnique(Mage_Core_Model_Abstract $object)
  561. {
  562. $existent = array();
  563. $fields = $this->getUniqueFields();
  564. if (!empty($fields)) {
  565. if (!is_array($fields)) {
  566. $this->_uniqueFields = array(
  567. array(
  568. 'field' => $fields,
  569. 'title' => $fields
  570. ));
  571. }
  572. $data = new Varien_Object($this->_prepareDataForSave($object));
  573. $select = $this->_getWriteAdapter()->select()
  574. ->from($this->getMainTable());
  575. foreach ($fields as $unique) {
  576. $select->reset(Zend_Db_Select::WHERE);
  577. if (is_array($unique['field'])) {
  578. foreach ($unique['field'] as $field) {
  579. $select->where($field.'=?', $data->getData($field));
  580. }
  581. }
  582. else {
  583. $select->where( $unique['field'] . ' = ?', $data->getData($unique['field']) );
  584. }
  585. if ($object->getId()) {
  586. $select->where($this->getIdFieldName().' != ?', $object->getId());
  587. }
  588. if ( $test = $this->_getWriteAdapter()->fetchRow($select) ) {
  589. $existent[] = $unique['title'];
  590. }
  591. }
  592. }
  593. if (!empty($existent)) {
  594. if (count($existent) == 1 ) {
  595. $error = Mage::helper('core')->__('%s already exists.', $existent[0]);
  596. }
  597. else {
  598. $error = Mage::helper('core')->__('%s already exist.', implode(', ', $existent));
  599. }
  600. Mage::throwException($error);
  601. }
  602. return $this;
  603. }
  604. public function afterLoad(Mage_Core_Model_Abstract $object)
  605. {
  606. $this->_afterLoad($object);
  607. }
  608. /**
  609. * Perform actions after object load
  610. *
  611. * @param Varien_Object $object
  612. */
  613. protected function _afterLoad(Mage_Core_Model_Abstract $object)
  614. {
  615. return $this;
  616. }
  617. /**
  618. * Perform actions before object save
  619. *
  620. * @param Varien_Object $object
  621. */
  622. protected function _beforeSave(Mage_Core_Model_Abstract $object)
  623. {
  624. return $this;
  625. }
  626. /**
  627. * Perform actions after object save
  628. *
  629. * @param Varien_Object $object
  630. */
  631. protected function _afterSave(Mage_Core_Model_Abstract $object)
  632. {
  633. return $this;
  634. }
  635. /**
  636. * Perform actions before object delete
  637. *
  638. * @param Varien_Object $object
  639. */
  640. protected function _beforeDelete(Mage_Core_Model_Abstract $object)
  641. {
  642. return $this;
  643. }
  644. /**
  645. * Perform actions after object delete
  646. *
  647. * @param Varien_Object $object
  648. */
  649. protected function _afterDelete(Mage_Core_Model_Abstract $object)
  650. {
  651. return $this;
  652. }
  653. /**
  654. * Serialize serializeable fields of the object
  655. *
  656. * @param Mage_Core_Model_Abstract $object
  657. */
  658. protected function _serializeFields(Mage_Core_Model_Abstract $object)
  659. {
  660. foreach ($this->_serializableFields as $field => $parameters) {
  661. list($serializeDefault, $unserializeDefault) = $parameters;
  662. $this->_serializeField($object, $field, $serializeDefault, isset($parameters[2]));
  663. }
  664. }
  665. /**
  666. * Retrieve table checksum
  667. *
  668. * @param string $table
  669. * @return int
  670. */
  671. public function getChecksum($table)
  672. {
  673. if (!$this->_getConnection('read')) {
  674. return false;
  675. }
  676. if (is_array($table)) {
  677. $table = implode(',', $table);
  678. }
  679. $data = $this->_getConnection('read')->fetchAll('checksum table '.$table);
  680. $checksum = 0;
  681. foreach ($data as $row) {
  682. $checksum+= $row[self::CHECKSUM_KEY_NAME];
  683. }
  684. return $checksum;
  685. }
  686. }