在Magento安装脚本中不使用SQL实现ALTER TABLE

55

Jonathan Day表示:

“更新不应以SQL命令的形式提供”。我还没有遇到任何无法通过Magento的配置结构执行的DDL或DML语句。

(在问题如何将配置更改从开发环境迁移到生产环境?中)

我想知道如何最好地添加/修改/删除表格中的列或索引,但不依赖于SQL?这是否可能?

此外,还有哪些操作只能使用SQL完成?


你不能不使用SQL修改SQL数据库,但是可以进行二进制差异和合并。Magento在更新脚本中处理更新的方式非常好。配置更改可以在Magento中作为设置/升级脚本处理。另一个选项是在生产环境中应用配置更改,并将数据库复制到开发环境中。 - Anton S
@Anton S - 我正是在询问那些更新脚本。 - clockworkgeek
请尽可能使用Magento提供的方法(或Zend抽象)进行编程,因为这些方法会计算表前缀和其他配置选项,这些选项可能依赖于其他设置,而这些设置在每个安装中并不总是相同的。 - Anton S
3个回答

131

您可以在设置脚本中使用以下方法:

  • 使用Varien_Db_Ddl_Table类创建新表,您可以通过与$this->getConnection()->createTable($tableObject)结合配置所有字段、键和关系。

    例如:

/* @var $this Mage_Core_Model_Resource_Setup */
$table = new Varien_Db_Ddl_Table();
$table->setName($this->getTable('module/table'));
$table->addColumn('id', Varien_Db_Ddl_Table::TYPE_INT, 10, 
                  array('unsigned' => true, 'primary' => true));

$table->addColumn('name', Varien_Db_Ddl_Table::TYPE_VARCHAR, 255);
$table->addIndex('name', 'name');
$table->setOption('type', 'InnoDB');
$table->setOption('charset', 'utf8');

$this->getConnection()->createTable($table);
请使用连接设置方法($this->getConnection()):
  • addColumn() 方法用于向现有表中添加新列。它具有以下参数:
    • $tableName - 应修改的表名
    • $columnName - 要添加的列的名称
    • $definition - 列的定义 (INT(10), DECIMAL(12,4),等等)
  • addConstraint() 方法创建一个新的外键约束。它具有以下参数:
    • $fkName - 外键名称,在数据库中应该是唯一的,如果不指定FK_前缀,将自动添加
    • $tableName - 要添加外键的表名
    • $columnName - 应引用另一张表的列名。如果您有复杂的外键,请使用逗号指定多个列
    • $refTableName - 外部表名
    • $refColumnName - 外部表中的列名
    • $onDelete - 在外部表中移除行的操作。可以为空字符串(不执行任何操作),cascadeset null。此字段是可选的,如果未指定,则将使用cascade值。
    • $onUpdate - 在外部表中更新密钥的操作。可以为空字符串(不执行任何操作),cascadeset null。此字段是可选的,如果未指定,则将使用cascade值。
    • $purge - 启用添加外键后的行清理标志(例如,删除未引用的记录)
  • addKey() 方法用于向表中添加索引。它具有以下参数:
    • $tableName - 应添加索引的表名
    • $indexName - 索引名称
    • $fields - 索引中使用的列名
    • $indexType - 索引类型。可能的值为:indexuniqueprimaryfulltext。此参数是可选的,因此默认值为index
  • dropColumn() 方法用于从现有表中删除列。它具有以下参数:
    • $tableName - 应修改的表名
    • $columnName - 要删除的列的名称
  • dropForeignKey() 方法用于删除外键。它具有以下参数:
    • $tableName - 要删除外键的表名
    • $fkName - 外键名称
  • dropKey() 方法用于删除表索引。它具有以下参数:
    • $tableName - 应删除索引的表名
    • $keyName - 索引名称
  • modifyColumn 方法用于修改表中现有列。它具有以下参数:
    • $tableName - 应修改的表名
    • $columnName - 应重命名的列的名称
    • $definition - 列的新定义 (INT(10), DECIMAL(12,4),等等)
  • changeColumn 方法用于

2
很高兴知道它们在那里! - Alana Storm
2
谢谢Ivan。这是一个非常棒的方法参考!我已经查看了Mage_*类,但没有看到类似这样的内容,所以开始感到绝望。希望我的更新脚本将来会更加整洁。 - clockworkgeek
2
@clockworkgeek 欢迎。顺便说一下,Mage仅包含Magento的MVC实现,用于处理文件系统、数据库、套接字、不同的文件格式的API位于lib/Varien下,并且许多这些类都是从Zend Framework中扩展而来的。 - Ivan Chepurnyi
2
@Jonathan 希望 Magento 2.0 更加友好于开发人员,尤其是在数据库升级方面。但当然它只会扩展 Zend_Db。使用 Doctrine 2.0 ORM 可以解决这个问题,但需要重新编写 Magento :) - Ivan Chepurnyi
2
@demonkoryu 我在1.3中没有看到它,但在1.4中看到了。 - clockworkgeek
显示剩余8条评论

18
任何Magento的更新中不应包含SQL语句的想法基于以下观点:
  1. Magento对象提供了对数据库/数据存储层的抽象。
  2. 您应该使用这些抽象来更新Magento,这可以确保如果Magento团队更改了对象与数据存储层的交互方式,您的更新仍将起作用。(假设核心团队维护了对象方法所暗示的原始“合同”)
因此,问题在于“ALTER TABLE”语句直接更改数据存储层。 如果您完全遵循以上两个想法,就不应更改数据存储层。(在添加列或索引的情况下,这意味着仅使用EAV模型,使用设置资源来管理更改并接受Magento的索引)。
一个很好的经验法则是,如果您正在更改或添加一些核心Magento功能(产品、评论等),请避免直接更改数据库结构,除非您愿意在升级期间仔细管理它。
如果您正在构建新对象和功能,请使用任何SQL语句通过设置资源创建和更改表格。如果您查看安装程序/升级文件,可以看到Magento核心团队自己也这样做。

4
我很喜欢你在知识库中的文章,但并非所有Magento(关键和最常用)对象都可以使用EAV扩展,因为它们是平面表结构(例如订单和报价),即使您通过提供的方法进行操作,向这些对象添加变量也需要修改表。回滚此类更改需要额外的数据完整性和管理步骤。 - Anton S
3
顺便说一下,订单和报价在安装脚本中具有伪EAV API。例如,对于订单实体调用addAttribute方法将导致在order_flat表中创建一个新列。 - Ivan Chepurnyi
2
@Anton 模块设置类应该从 Mage_Sales_Model_Mysql4_Setup 继承以支持这些功能。 - Ivan Chepurnyi
1
@Alan,我在答案中提供的方法不是在最初的Magento版本中出现的,因此在“核心”中有很多旧代码,似乎在不久的将来没有人会重构它。 - Ivan Chepurnyi
1
@Ivan:同意,尽管我坚持我的大点。做某事的“正确”方式是1.它是否有效;2.你是否愿意支持它。 - Alana Storm
显示剩余6条评论

12

我已成功使用Magento CE v1.6.1.0更改表并添加外键列:

// Alter table to add column
$installer->getConnection()

        ->addColumn(
            $installer->getTable('modulekey/model'), 
            'column_name',  
            array(
                'type'      => Varien_Db_Ddl_Table::TYPE_INTEGER,
                'length'    => null,
                'unsigned'  => true,
                'nullable'  => true,
                'comment'   => 'Foreign key'
            )
        );

// Add foreign key constraint
$installer->getConnection()

        ->addForeignKey(
            $installer->getFkName( 
                'modulekey/model',  'column_name',
                'modulekey/foreign_model',  'foreign_column_name'
            ),
            $installer->getTable('modulekey/model'), 
            'column_name',
            $installer->getTable('modulekey/foreign_model'),
            'foreign_column_name',
            Varien_Db_Ddl_Table::ACTION_SET_NULL, 
            Varien_Db_Ddl_Table::ACTION_SET_NULL
        );

这些是来自 Varien_Db_Adapter_Pdo_Mysql 的方法。


除了Ivan的答案之外,知道如何做这个也很重要。 - Tyler V.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接