1. 为什么选择 Magento 2? - 了解其核心优势
  2. 开发环境准备 - 搭建本地开发环境
  3. 核心概念入门 - 理解 Magento 的基本架构
  4. 模块开发 - Magento 开发的基石
  5. 主题开发 - 自定义前端外观
  6. 插件与重写 - 核心功能扩展
  7. 数据库与模型 - 数据操作
  8. 布局与块 - 前端页面结构
  9. 实战演练:创建一个简单的“Hello World”模块
  10. 进阶学习与最佳实践
  11. 推荐资源

为什么选择 Magento 2?

在开始之前,了解 Magento 2 的强大之处至关重要:

magento开发教程
(图片来源网络,侵删)
  • 面向对象与高度模块化:基于 Zend Framework 和 Symfony 组件,代码结构清晰,易于扩展。
  • 性能优化:大量性能提升,如全页面缓存、Varnish 集成、更高效的索引等。
  • 现代前端技术:默认使用 RequireJS 进行 JavaScript 模块化管理,Less/Sass 用于 CSS 预处理。
  • 强大的后台:提供灵活、可配置的后台管理系统,支持多语言、多货币、多站点。
  • REST API:内置强大的 REST API,方便与第三方系统集成或开发移动应用。
  • 依赖注入与服务定位器:遵循现代 PHP 设计模式,使代码更易于测试和维护。

开发环境准备

本地开发环境是 Magento 开发的第一步,强烈推荐使用 Docker,因为它能快速搭建一个与生产环境一致的、隔离的开发环境。

推荐方案:Docker (Magerun2 + Docker)

  1. 安装 Docker:在您的操作系统上安装 Docker Desktop (for Mac/Windows) 或 Docker Engine (for Linux)。
  2. 安装 magento2-docker
    composer global require n98/magerun2
  3. 初始化项目
    # 进入您想放置项目的目录
    cd /path/to/your/projects
    n98-magerun2 docker-compose create project my-magento2-project
  4. 启动环境
    cd my-magento2-project
    docker-compose up -d
  5. 安装 Magento
    docker-compose exec -u www-data fpm bash -c "bin/magento setup:install \
        --db-host=db \
        --db-name=magento2 \
        --db-user=magento2 \
        --db-password=magento2 \
        --base-url=http://localhost:8000 \
        --admin-firstname=admin \
        --admin-lastname=admin \
        --admin-email=admin@admin.com \
        --admin-user=admin \
        --admin-password=admin123 \
        --language=en_US \
        --currency=USD \
        --timezone=America/Chicago \
        --use-rewrites=1"

    注意:端口 8000 可以根据需要修改。

传统方案 (XAMPP/MAMP + 手动安装)

  1. 安装 LAMP/LEMP 环境:安装 Apache/Nginx, PHP (7.4+ 推荐), MySQL。
  2. 配置 PHP:确保 php.ini 中的 memory_limit, max_execution_time, upload_max_filesize 等参数足够大。
  3. 下载 Magento:从 Magento 官网 下载 Magento 2 安装包。
  4. 解压并设置权限:将文件解压到 Web 根目录(如 htdocs/magento2),并设置正确的文件所有者。
  5. 通过浏览器访问:访问 http://localhost/magento2 并按照 Web 安装向导完成安装。

核心概念入门

理解这些概念是 Magento 开发的关键:

  • 模块:Magento 功能的基本单元,一个模块可以包含控制器、模型、布局、块、助手等,所有核心功能(如目录、客户、结账)都是以模块形式存在的。
  • Area(区域):Magento 将应用分为三个主要区域:
    • Frontend:面向用户的商店前端。
    • Adminhtml:管理员后台。
    • Crontab:用于定时任务。
    • 不同区域的配置和布局是独立的。
  • 配置:Magento 的核心是它的配置系统,几乎所有东西(路由、布局、依赖注入等)都在 etc/ 目录下的 XML 文件中声明。
  • 依赖注入:Magento 2 大量使用依赖注入,您无需 new 一个对象,而是在类的构造函数中声明它需要的依赖,Magento 会自动“注入”正确的实例。
  • 插件:这是 Magento 推荐的、最安全的扩展核心类功能的方式,它允许您在方法执行前、后或周围添加逻辑,而无需修改原始代码。
  • 重写:一种更强大的方式,可以完全替换一个类的行为。谨慎使用,因为它会覆盖整个类,可能导致与其他模块的冲突。

模块开发

模块是 Magento 2 的心脏,让我们创建一个简单的模块。

magento开发教程
(图片来源网络,侵删)

模块结构

一个模块的基本结构如下:

app/code/
└── Vendor/
    └── ModuleName/
        ├── etc/               # 模块配置目录
        │   ├── module.xml     # 模块声明文件
        │   ├── di.xml         # 依赖注入配置
        │   ├── routes.xml     # 路由配置
        │   └── ...
        ├── Controller/        # 控制器目录
        │   └── Index/
        │       └── Test.php
        ├── view/              # 视图文件目录
        │   ├── frontend/
        │   │   ├── layout/
        │   │   │   └── test_index_index.xml
        │   │   └── templates/
        │   │       └── test.phtml
        │   └── adminhtml/
        ├── registration.php   # 模块注册文件
        └── ...

步骤:

  1. 创建目录和 registration.php

    // app/code/Vendor/ModuleName/registration.php
    <?php
    \Magento\Framework\Component\ComponentRegistrar::register(
        \Magento\Framework\Component\ComponentRegistrar::MODULE,
        'Vendor_ModuleName',
        __DIR__
    );
  2. 声明模块 (module.xml)

    <!-- app/code/Vendor/ModuleName/etc/module.xml -->
    <?xml version="1.0"?>
    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
        <module name="Vendor_ModuleName" setup_version="1.0.0" />
    </config>
  3. 启用模块: 在 Magento 根目录下执行命令:

    magento开发教程
    (图片来源网络,侵删)
    bin/magento module:enable Vendor_ModuleName
    bin/magento setup:upgrade

主题开发

主题控制着商店的前端外观。

主题结构

app/design/frontend/
└── Vendor/
    └── theme-name/
        ├── composer.json       # 主题元数据
        ├── registration.php    # 主题注册
        ├── etc/
        │   └── view.xml        # 主题配置
        ├── web/                # 静态资源
        │   ├── css/
        │   ├── js/
        │   ├── images/
        │   └── fonts/
        └── templates/          # 覆盖的模板文件
            └── ...

创建主题:

  1. 创建目录结构

  2. 创建 registration.php

    // app/design/frontend/Vendor/theme-name/registration.php
    <?php
    \Magento\Framework\Component\ComponentRegistrar::register(
        \Magento\Framework\Component\ComponentRegistrar::THEME,
        'frontend/Vendor/theme-name',
        __DIR__
    );
  3. 创建 composer.json

    {
        "name": "vendor/theme-name",
        "description": "My custom theme",
        "type": "magento2-theme",
        "version": "1.0.0",
        "license": [
            "OSL-3.0",
            "AFL-3.0"
        ],
        "require": {
            "php": "~7.0.13|~7.1.0|~7.2.0",
            "magento/framework": "*"
        },
        "autoload": {
            "files": [
                "registration.php"
            ]
        }
    }
  4. 应用主题: 在后台 Content -> Design -> Configuration 中为您的网站或商店视图指定此主题。


插件与重写

这是扩展 Magento 功能的两种主要方式。

插件

优点:安全、非侵入式、可以多次作用于同一个方法。 缺点:不能修改方法参数或返回值类型,不能用于 final__constructstatic 方法。

场景:在保存产品前添加日志,在价格计算后添加折扣。

配置 (di.xml)

<!-- app/code/Vendor/ModuleName/etc/di.xml -->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Catalog\Model\Product">
        <plugin name="vendor_module_name_product_plugin"
                type="Vendor\ModuleName\Plugin\Model\ProductPlugin" />
    </type>
</config>

插件类

// app/code/Vendor/ModuleName/Plugin/Model/ProductPlugin.php
<?php
namespace Vendor\ModuleName\Plugin\Model;
class ProductPlugin
{
    public function beforeSave(\Magento\Catalog\Model\Product $subject, $product)
    {
        // 在原始 save 方法执行前运行
        // 可以修改 $product 对象
        return [$product];
    }
    public function afterSave(\Magento\Catalog\Model\Product $subject, $result)
    {
        // 在原始 save 方法执行后运行
        // $result 是原始方法的返回值
        return $result;
    }
}

重写

优点:完全控制类的行为。 缺点:危险,容易与其他模块的重写冲突,升级时可能被覆盖。 使用场景:当插件无法满足需求时(如必须重写 final 方法),或者您想完全替换一个类的逻辑。

步骤

  1. di.xml 中配置

    <!-- app/code/Vendor/ModuleName/etc/di.xml -->
    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
        <preference for="Magento\Catalog\Model\Product" type="Vendor\ModuleName\Model\Catalog\Product" />
    </config>
  2. 创建新类并继承原始类

    // app/code/Vendor/ModuleName/Model/Catalog/Product.php
    <?php
    namespace Vendor\ModuleName\Model\Catalog;
    class Product extends \Magento\Catalog\Model\Product
    {
        // 在这里重写您需要的方法
        public function getName()
        {
            return 'Custom Prefix: ' . parent::getName();
        }
    }

数据库与模型

Magento 2 使用 EAV (Entity-Attribute-Value) 模型来存储大部分数据(如产品、类别、客户),但也有简单的表模型。

模型、资源模型 和 集合

  • 模型:代表一个数据对象(如一个 Product 对象),包含数据和业务逻辑。
  • 资源模型:负责与数据库交互(SELECT, INSERT, UPDATE, DELETE),它不直接使用 SQL,而是使用 Magento 的抽象层。
  • 集合:代表一个模型对象的数组(如一组 Product 对象)。

创建一个简单的自定义表模型

  1. etc/db_schema.xml (推荐方式,声明表结构):

    <schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd">
        <table name="vendor_module_name_example" resource="default" engine="innodb">
            <column xsi:type="int" name="id" padding="10" unsigned="true" nullable="false" identity="true" comment="Example ID"/>
            <column xsi:type="varchar" name="name" length="255" nullable="false" comment="Example Name"/>
            <column xsi:type="timestamp" name="created_at" on_update="false" nullable="false" default="CURRENT_TIMESTAMP" comment="Creation Time"/>
            <constraint xsi:type="primary" referenceId="PRIMARY">
                <column name="id"/>
            </constraint>
        </table>
    </schema>

    执行 bin/magento setup:db-declaration:generate-whitelistbin/magento setup:upgrade 来创建表。

  2. 模型类

    // app/code/Vendor/ModuleName/Model/Example.php
    <?php
    namespace Vendor\ModuleName\Model;
    class Example extends \Magento\Framework\Model\AbstractModel
    {
        protected function _construct()
        {
            $this->_init(\Vendor\ModuleName\Model\ResourceModel\Example::class);
        }
    }
  3. 资源模型类

    // app/code/Vendor/ModuleName/Model/ResourceModel/Example.php
    <?php
    namespace Vendor\ModuleName\Model\ResourceModel;
    class Example extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
    {
        protected function _construct()
        {
            $this->_init('vendor_module_name_example', 'id');
        }
    }

布局与块

布局和块共同构成了 Magento 的前端页面结构。

布局

布局文件是 XML 文件,它们定义了页面的结构,比如包含哪些块,以及它们的顺序和位置。

  • 位置app/design/frontend/Vendor/theme-name/frontend/layout/
  • 命名<router_name>_<controller_name>_<action_name>.xml (catalog_product_view.xml)
  • 核心概念
    • <page>:页面容器。
    • <head>, <body>:页面的 <head><body> 部分。
    • <block>:定义一个块。
      • name:块的唯一名称。
      • class:块的 PHP 类。
      • template:要渲染的 PHTML 文件路径。
      • as:在父块中引用此块的别名。
    • <move>:移动一个块到另一个位置。
    • <referenceBlock>:引用并修改一个已存在的块(如 remove)。

块是 PHP 类,负责准备和传递数据给模板文件。

  • 作用
    1. 加载数据。
    2. 执行业务逻辑。
    3. 将数据分配给模板变量。
  • 类型
    • 纯块:只包含逻辑,不渲染模板。\Magento\Framework\View\Element\Template\Context
    • 模板块:最常见,继承自 \Magento\Framework\View\Element\Template,关联一个 .phtml 文件。

示例

catalog_product_view.xml 中添加一个自定义块:

<!-- app/design/frontend/Vendor/theme-name/frontend/layout/catalog_product_view.xml -->
<page layout="1column" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceContainer name="content">
            <block class="Vendor\ModuleName\Block\Product\View" name="product.custom.info" template="Vendor_ModuleName::product/view/custom-info.phtml" before="product.info.main" />
        </referenceContainer>
    </body>
</page>

对应的块类:

// app/code/Vendor/ModuleName/Block/Product/View.php
<?php
namespace Vendor\ModuleName\Block\Product;
class View extends \Magento\Framework\View\Element\Template
{
    protected $_product;
    public function __construct(
        \Magento\Framework\View\Element\Template\Context $context,
        \Magento\Catalog\Model\Product $product,
        array $data = []
    ) {
        $this->_product = $product;
        parent::__construct($context, $data);
    }
    public function getProduct()
    {
        // 在控制器或布局中设置产品
        if (!$this->_product->getId()) {
            // 可以尝试从当前请求中获取
        }
        return $this->_product;
    }
}

对应的模板文件:

<!-- app/design/frontend/Vendor/theme-name/frontend/templates/product/view/custom-info.phtml -->
<div class="product-custom-info">
    <h2>Custom Information</h2>
    <p>Custom SKU: <?php echo $block->getProduct()->getSku(); ?></p>
</div>

实战演练:创建一个简单的“Hello World”模块

让我们整合以上知识,创建一个模块,在首页显示 "Hello, Magento 2 World!"。

  1. 创建模块结构

    app/code/
    └── HelloWorld/
        └── Helloworld/
            ├── etc/
            │   ├── module.xml
            │   └── frontend/
            │       └── routes.xml
            ├── registration.php
            └── Controller/
                └── Index/
                    └── Index.php
  2. registration.php

    <?php
    \Magento\Framework\Component\ComponentRegistrar::register(
        \Magento\Framework\Component\ComponentRegistrar::MODULE,
        'HelloWorld_Helloworld',
        __DIR__
    );
  3. etc/module.xml

    <?xml version="1.0"?>
    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
        <module name="HelloWorld_Helloworld" setup_version="1.0.0" />
    </config>
  4. 定义路由 (etc/frontend/routes.xml)

    <?xml version="1.0"?>
    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
        <router id="standard">
            <route id="helloworld" frontName="helloworld">
                <module name="HelloWorld_Helloworld"/>
            </route>
        </router>
    </config>
  5. 创建控制器 (Controller/Index/Index.php)

    <?php
    namespace HelloWorld\Helloworld\Controller\Index;
    class Index extends \Magento\Framework\App\Action\Action
    {
        public function execute()
        {
            // 输出 "Hello World!" 到页面
            echo "Hello, Magento 2 World!";
            // 不需要渲染任何布局
            exit;
        }
    }
  6. 启用模块

    bin/magento module:enable HelloWorld_Helloworld
    bin/magento setup:upgrade
  7. 访问: 清除缓存后,访问 http://your-magento-domain.com/helloworld/index/index,您应该能看到 "Hello, Magento 2 World!"。


进阶学习与最佳实践

  • 命令行工具:熟练使用 bin/magento 命令来管理模块、缓存、静态内容等。
  • 调试
    • Xdebug:设置 Xdebug 是调试 PHP 代码的最佳方式。
    • 日志:使用 \Magento\Framework\Logger\Monolog 记录日志。
    • 变位词/翻译:利用 __(...) 函数和翻译文件进行调试。
  • 部署: 每次修改主题文件(CSS, JS, PHTML)后,都需要部署静态内容:
    bin/magento setup:static-content:deploy <locale> # en_US
  • 缓存管理:Magento 2 缓存非常强大,但也需要手动管理,开发时经常需要清空缓存:
    bin/magento cache:clean
    bin/magento cache:flush
  • 代码质量:遵循 Magento 的编码标准,使用工具如 PHP CodeSniffer 和 PHPMD。
  • 依赖管理:使用 Composer 管理第三方库,不要将第三方库直接放入 libvendor 目录,除非是 Magento 官方推荐的。

推荐资源

Magento 2 是一个强大但复杂的平台,学习曲线较陡峭,保持耐心,多动手实践,多阅读官方文档,您会逐渐掌握它的精髓,祝您开发愉快!