bdc/module-simplenews

BDCrops SimpleNews 模块为 Magento 2 扩展。

安装: 2

依赖: 0

建议者: 0

安全: 0

星标: 3

关注者: 2

分支: 4

开放问题: 0

语言:JavaScript

类型:magento2-module

dev-master 2019-12-23 15:37 UTC

This package is auto-updated.

Last update: 2024-09-29 05:55:23 UTC


README

Magento 2 模块开发Magento 2 SimpleNews 模块从头创建一个完整的模块。您可以只需遵循我的代码来创建此模块。或者您可以直接下载压缩的 tar 文件并安装它并运行它。

先决条件

  • 无先决条件
  • 可能对了解 Magento 2 基础前端、后端及本地/服务器安装的用户更有益。
  • 作为第一步,掌握 Magento 2 开发或模块开发的基本知识。

目标

  • 逐步开发完整的模块。
  • 为 Magento 2 认证Associate/Professional 开发者考试做准备,进行实际操作练习。

Magento 2 SimpleNews 模块逐步指南 (BDCrops)

部分A:新闻模块基础(架构与自定义)

部分B:新闻模块后端

部分 C:前端新闻模块

部分 D:新闻控制台/命令

部分 E:创建/设置/配置自定义计划任务

部分 F:创建 REST WEB API

部分 G:依赖注入配置

部分 H:自定义布局配置和 JavaScript

部分 I:UI 组件库

部分 J:实体-属性-值(EAV)

部分 A:基本新闻模块 返回顶部

解释 Magento 2 基本目录结构?

  • app – 用于附加元素;通常,app 包含以下子目录
    • code – 包含已安装的模块;
    • design – 包含已安装的主题。前端主题位于 frontend 文件夹中;管理面板的主题位于 adminhtml 文件夹中;
    • etc – 包含 Magento 2 配置文件;
    • i18n – 包含已安装的语言包。
  • bin – 包含 Magento 文件,负责在 Magento 2 中执行 CLI 命令。
  • dev – 包含集成和功能测试文件。
  • generated – 用于 Magento 2 中的生成类。
  • lib – 包含 Magento 2 库和非模块化代码。
  • phpserver – 包含 Router.php 文件,实现了内置的 PHP 服务器。
  • pub – 用于静态文件存储
    • errors – 包含负责在浏览器中显示错误(默认情况下禁用)的文件;
    • media – 包含网站的所有媒体文件;
    • static – 包含生成的主题和模块文件。
  • var – 包含临时文件,例如
    • cache – 包含所有缓存的对象,除了页面;
    • composer_home – 安装向导的根目录;
    • log – 存储 Magento 2 日志;
    • page_cache – 包含使用全页缓存缓存的页面;
    • view_preprocessed – 包含最小化的模板和编译的 LESS。
  • 厂商目录 – 包含 Magento 2 的核心文件。此外,此目录还可以包含额外安装的模块。您应通过 Composer 对此目录中的组件执行操作。

什么是模型视图视图模型(MVVM)架构?

  • 模型:保存应用程序的业务逻辑,并依赖于相关的类(ResourceModel)进行数据库访问。模型依靠服务合约将其功能暴露给应用程序的其他层。
  • 视图:用户在屏幕上看到的结构及布局,即实际的 HTML。这是通过模块提供的 PHTML 文件实现的。PHTML 文件与布局 XML 文件中的每个 ViewModel 相关联,在 MVVM 术语中被称为绑定器。布局文件还可能分配用于最终页面的 JavaScript 文件。
  • ViewModel:与模型层交互,仅向由模块的 Block 类处理的视图层暴露必要的信息。请注意,这通常是 MVC 系统中控制器角色的部分。在 MVVM 中,控制器只负责处理用户流程,这意味着它接收请求并告诉系统渲染视图或重定向用户到另一个路由。

Magento 2 架构分为 4 层(PDSP)吗?

  • 持久层:描述资源模型,负责使用 CRUD 请求提取和修改数据库中的数据。这里还实现了额外的业务逻辑功能,例如数据验证和数据库函数实现。

  • 领域层:负责业务逻辑,不包含特定于资源或数据库的信息。领域层还可以包含服务合约。领域层每个数据模型都依赖于资源模型,该模型负责访问数据库。

  • 服务层:表示层和领域层之间的中间层。它实现了使用 PHP 接口定义的服务合约。服务合约允许通过依赖注入文件(di.xml)添加或更改业务逻辑资源模型。服务层还用于授权访问 API(REST/SOAP 或其他模块)。服务接口在模块的 /Api 命名空间中声明。数据(实体)接口在 /Api/Data 中声明。数据实体是传递到服务接口并从服务接口返回的数据结构。

  • 表示层:上层。它包含所有视图元素(包括布局、块、模板、CSS、JS)和控制器。表示层通常使用服务合约调用服务层。但根据实现方式,它可能与业务逻辑重叠。

Magento 有 5 种区域类型?

  • Magento 管理员(adminhtml):此区域的入口点是 index.php 或 pub/index.php。管理员面板区域包含存储管理所需的代码。/app/design/adminhtml 目录包含在管理员面板中工作时会看到的组件的所有代码。
  • 店面(frontend):此区域的入口点是 index.php 或 pub/index.php。店面(或前端)包含定义店面外观的模板和布局文件。
  • 基本(base):用作 adminhtml 和 frontend 区域中缺失文件的回退。
  • Cron(crontab):在 cron.php 中,\Magento\Framework\App\Cron 类始终加载 'crontab' 区域。您还可以使用 SOAP 和 REST API 向 Magento 发送请求。这两个区域
  • Web API REST(webapi_rest):此区域的入口点是 index.php 或 pub/index.php。REST 区域有一个前端控制器,它了解如何执行基于 REST 的 URL 查找。
  • Web API SOAP(webapi_soap):此区域的入口点是 index.php 或 pub/index.php。

模块文件夹包含架构的一部分,如下所示?

  • Api 或 Api/Data:服务合约,定义服务接口和数据接口

  • 适配器:类遵循适配器模式,并围绕第三方库的类进行包装,允许通过将第三方类接口转换为原生代码所期望的接口,在代码中使用第三方库的功能。(module-search/Adapter/)

  • 模块:我们的MVVM架构的ViewModels

  • 收集器:模块-deploy/Collector/Collector.php

  • 命令:目录用于存储负责控制台程序执行的PHP文件。在我们的例子中,Console/Command/ImagesResizeCommand.php处理产品图片缩放的命令。

  • 控制器:负责处理用户与系统交互时的用户流程

  • 配置:模块-deploy/Config/BundleConfig.php

  • 定时任务:我们使用此目录存储在Cron启动时执行的文件。

  • 客户数据:目录包含负责处理各部分信息的PHP文件。Magento 2有一个特殊功能,允许异步处理、更新和传输信息。

  • etc:配置XML文件模块定义自己及其部分(路由、模型、块、观察器和定时任务),也用于非核心模块覆盖核心模块的功能。

  • 异常:(module-sales/Exception/)

  • 文件:示例文件(module-inventory-import-export/Files/)

  • fixtures:示例数据模块(module-sales-sample-data/fixtures/orders.csv)

  • 网关:(module-paypal/Gateway)

  • 助手:包含用于一个以上应用程序层的代码的类。例如,在Cms模块中,助手类负责为浏览器展示准备HTML。

  • i18n:包含国际化CSV文件,用于翻译

  • 索引器:索引处理器(module-inventory-indexer/Indexer)

  • 模型:用于模型和ResourceModels

  • 观察者:包含观察者,或“观察”系统事件的模型。通常,当此类事件触发时,观察者会实例化一个模型来处理此类事件的必要业务逻辑。

  • 包:模块-deploy/Package

  • 定价:最终定价模型(module-msrp-grouped-product/Pricing)

  • 流程:模块-deploy/Process

  • 插件:目录包含插件文件,允许我们在必要时修改配置文件中描述的某些模块的功能:vendor/magento/module-catalog/etc/di.xml

  • 搜索适配器:module-elasticsearch/SearchAdapter

  • ReportXml:vendor/magento/module-analytics/ReportXml

  • 设置:迁移类,负责创建模式和数据

  • 服务:[exam] (module-media-storage/Service/ImageResize.php,module-deploy/或module-catalog-url-rewrite/Service/V1/StoreViewService.php )

  • src:vendor/magento/magento2-functional-testing-framework/src/Magento/

  • 策略:模块-deploy/Strategy

  • 源:模块-deploy/Source

  • 测试:单元测试

  • UI:在管理应用程序中使用的元素,如网格和表单

  • 视图 - 前端和后台应用程序的布局(XML)文件和模板(PHTML)文件包含模板文件、CSS和JS文件、模块媒体文件。这些文件位于根据使用区域的子文件夹中:adminhtml、frontend或base(站点的行政和前端部分的通用文件)。这些子目录包括静态视图文件、设计模板、电子邮件模板和布局文件

    • view/{区域}/email - 包含电子邮件模板。
    • view/{区域}/layout - 包含布局修改的文件。
    • view/{区域}/page_layout - 包含页面布局修改的文件。
    • view/{区域}/templates - 包含模块模板(phtml)的文件。
    • view/{区域}/ui_component - 包含UI模块组件的XML文件。
    • view/{区域}/ui_component/templates
    • view/{区域}/web - 包含CSS、JS、静态和媒体模块文件。
    • view/{区域}/web/js - 包含js
    • view/{区域}/web/template - 包含html
    • view/{区域}/requirejs-config.js
  • ViewModel:(module-sales/ViewModel)

步骤2A1:创建模块目录,格式如上所示

在此模块中,我们将使用BDCrops作为供应商名称,并使用SimpleNews作为模块名称。因此,我们需要创建以下文件夹:app/code/BDC/SimpleNews

步骤2A2:使用配置文件 module.xml 声明模块

Magento 2 在该模块的etc目录中查找每个模块的配置信息。我们需要创建etc文件夹并添加module.xml文件。

  • 创建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="BDC_SimpleNews" setup_version="1.0.0" />
    </config>
    ```
    

在此文件中,我们注册了一个名为BDC_SimpleNews的模块,版本为1.0.0

备注[可以跳过]

  • Magento 2 运行/激活模块需要两个必选文件:etc/module.xml和registration.php。

步骤2A3:通过registration.php注册模块

所有Magento 2模块必须通过magento ComponentRegistrar类在Magento系统中注册。此文件将放置在模块根目录中。在此步骤中,我们需要创建此文件。

  • 创建registration.php并将以下代码插入其中

    源代码
    \Magento\Framework\Component\ComponentRegistrar::register(
        \Magento\Framework\Component\ComponentRegistrar::MODULE,
        'BDC_SimpleNews', __DIR__
    );
    

vendor文件夹中的模块将使用composer进行更新,而app/code中的所有模块将不会通过composer进行更新。因此,当您需要覆盖任何模块时,请将其添加到app/code中。

  • 创建composer.json并将以下代码插入其中

    源代码
    ```
    {
      "name": "bdc/module-simplenews",
      "description": "BDCrops SimpleNews module for Magento 2 extensions.",
      "type": "magento2-module",
      "version": "1.0.3",
      "license": [
          "OSL-3.0",
          "AFL-3.0"
      ],
    "authors": [{
              "name": "Abdul Matin",
              "email": "matinict@gmail.com",
    		      "company": "BDCrops Inc"
          }
      ],
    "homepage": "https://www.bdcrops.com",
      "autoload": {
          "files": [
              "registration.php"
          ],
          "psr-4": {
              "BDC\\SimpleNews\\": ""
          }
      }
    }
    
    ```
    

步骤2A4:配置声明式模式(创建表安装文件)

  • 创建etc/db_schema.xml并将以下代码插入其中

    源代码
    ```
    <?xml version="1.0"?>
    <schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd">
    
    <table name="bdc_simplenews" resource="default" engine="innodb" comment="SimpleNews Table">
      <column xsi:type="smallint" name="id" padding="6" unsigned="false" nullable="false" identity="true" comment="ID"/>
      <column xsi:type="varchar" name="title" nullable="false" length="255" comment="Title"/>
      <column xsi:type="varchar" name="summary" nullable="false" length="255" comment="Summary"/>
      <column xsi:type="varchar" name="description" nullable="false" length="255" comment="Descrition"/>
      <column xsi:type="timestamp" name="created_at" nullable="false" default="CURRENT_TIMESTAMP" on_update="false" comment="Created Datetime"/>
      <column xsi:type="timestamp" name="updated_at" nullable="false" default="CURRENT_TIMESTAMP" on_update="true" comment="Updated Datetime"/>
      <column xsi:type="smallint" name="status"  padding="2" unsigned="false" nullable="false" comment="Status"/>
      <constraint xsi:type="primary" referenceId="PRIMARY">   <column name="id"/> </constraint>
    </table>
    </schema>
    ```
    

注意

模块声明教程

步骤2A5:模式白名单(etc/db_schema_whitelist.json)

没有创建模式白名单,您将无法运行声明模式。注意:为了双重检查的目的,建议为每个版本生成一个新的白名单。在运行升级命令之前,您需要通过运行以下命令将您的模式添加到db_whitelist_schema.json文件中。为此,您需要一个/etc/db_schema_whitelist.json文件,该文件将存储通过声明式模式添加的所有内容。要生成此文件,请运行

db_schema

php bin/magento setup:db-declaration:generate-whitelist [options]
php bin/magento setup:db-declaration:generate-whitelist --module-name=vendor_module
php bin/magento setup:db-declaration:generate-whitelist --module-name=BDC_SimpleNews

现在,将在/vendor/module/etc文件夹中创建db_whitelist_schema.json文件。db_whitelist_schema

步骤2A6:启用模块

完成以上步骤后,您已创建了一个空模块。现在,我们将使其在Magento环境中生效。在启用模块之前,我们必须检查确保Magento已识别我们的模块,可以通过在命令行中输入以下内容来检查

php bin/magento module:status

如果您遵循以上步骤,您将在结果中看到以下内容

List of disabled modules:
BDC_SimpleNews

这意味着模块已被系统识别,但仍然未启用。运行此命令以启用它

php bin/magento module:enable BDC_SimpleNews

如果您看到此结果,则表示模块已成功启用

The following modules has been enabled:
- BDC_SimpleNews

这是您第一次启用此模块,因此Magento需要检查并升级模块数据库。我们需要运行以下命令

php bin/magento setup:upgrade

现在您可以在店铺 -> 配置 -> 高级 -> 高级下检查模块是否存在。

您还可以通过PhpMyAdmin或您喜欢的工具检查数据库表

Table db_schema

步骤2A7:开发数据和模式补丁(插入安装和升级数据)

因为在旧方法中,我们通常在创建表时在Install Schema或Upgrade schema中编写脚本来使用脚本,但现在在新版本中,这将通过补丁系统来完成。数据补丁是一个包含数据修改指令的类。它在//Setup/Patch/Data/.php文件中定义,并实现\ Magento\Setup\Model\Patch\DataPatchInterface。模式补丁包含自定义模式修改指令。这些修改可能很复杂。它在//Setup/Patch/Schema/.php文件中定义,并实现\ Magento\Setup\Model\Patch\SchemaPatchInterface。因此,要将数据添加到bdc_simplenews表,请创建文件夹BDC/SimpleNews/Setup/Patch/Data中的AddData.php文件,并编写以下代码

  • 创建 Setup/Patch/Data/AddData.php

    源代码
    ```
    <?php
    
    namespace BDC\SimpleNews\Setup\Patch\Data;
    
    use Magento\Framework\Setup\Patch\DataPatchInterface;
    use Magento\Framework\Setup\Patch\PatchVersionInterface;
    use Magento\Framework\Module\Setup\Migration;
    use Magento\Framework\Setup\ModuleDataSetupInterface;
    
    class AddData implements DataPatchInterface, PatchVersionInterface {
        private $news;
        public function __construct( \BDC\SimpleNews\Model\News $news ) {
            $this->news = $news;
        }
        public function apply(){
        	$newsData = [];
        	$newsData['title'] = "BDC News Head1";
        	$newsData['summary'] = "BDC News Summary";
        	$newsData['description'] = "BDCrops Inc description evulation of bangladesh";
        	//$newsData['status'] = 1;
    
        	$this->news->addData($newsData);
        	$this->news->getResource()->save($this->news);
    
        }
        public static function getDependencies() {   return []; }
        public static function getVersion() { return '2.0.0'; }
        public function getAliases() {   return []; }
    
    }
    
    ```
    

步骤2A8:为业务逻辑创建 News 模型

我们需要创建这些文件以在数据库中插入、更新、删除和获取数据。

  • 创建模型文件:Model/News.php

    源代码
    ```
    <?php
    // These files to insert, update, delete and get data in the database.
    namespace BDC\SimpleNews\Model;
    use Magento\Framework\Model\AbstractModel;
    
    class News extends AbstractModel{
        /**
         * News constructor.
         * @param \Magento\Framework\Model\Context $context
         * @param \Magento\Framework\Registry $registry
         * @param \Magento\Framework\Model\ResourceModel\AbstractResource|null $resource
         * @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection
         * @param array $data
         */
        public function __construct(
            \Magento\Framework\Model\Context $context,
            \Magento\Framework\Registry $registry,
            \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null,
            \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
            array $data = [] ) {
            parent::__construct($context, $registry, $resource, $resourceCollection, $data);
        }
    
       /**
        * (non-PHPdoc)
        * @see \Magento\Framework\Model\AbstractModel::_construct()
        */
        public function _construct(){
            $this->_init('BDC\SimpleNews\Model\Resource\News');
        }
    
        /**
         * Loading news data
         *
         * @param   mixed $key
         * @param   string $field
         * @return  $this
         */
        public function load($key, $field = null) {
          if ($field === null) {
            $this->_getResource()->load($this, $key, 'id');
            return $this;
          }
          $this->_getResource()->load($this, $key, $field);
          return $this;
        }
    }
    
    ```
    

注意:模型、资源模型和集合的基本概念

Magento 2 中的 CRUD 模型可以轻松管理数据库中的数据,您不需要编写很多代码来创建 CRUD。CRUD 代表创建、读取、更新和删除。由于 Magento ORM 是 Magento 2 服务合同的一部分的存储库实现所使用的,因此这是与 Magento 1 的重要区别,因为模块不应再依赖于其他模块使用特定的 ORM,而是只使用实体存储库。服务合同将在文章的第二部分进行更详细的介绍。Magento ORM 围绕模型、资源模型和资源集合构建。以下是 Magento ORM 元素:

  • 模型表示实体,具有数据和行为。
  • 资源模型是存储结构的映射器。
  • 集合封装了一组模型和相关功能,例如过滤、排序和分页。
  • 资源包括通过适配器提供的数据库连接。

注意:原生 Magento 保存/加载

ORM 允许您在数据库中创建、加载、更新和删除数据。在 Magento 中,集合是一个实现了 PHP5 SPL 接口的 IteratorAggregate 和 Countable 的类。集合在 Magento 中被广泛用于存储特定类型的对象集合。

注意:模型

模型类似于一个黑盒,它为资源模型提供了抽象层。数据的获取、提取和操作通过模型进行。一般来说,我们创建的每个实体(即我们在数据库中创建的每个表)都应该有它自己的模型类。每个模型都扩展了 Magento\Framework\Model\AbstractModel 类,它继承了 \Magento\Framework\DataObject 类,因此,我们可以调用 setData 和 getData 函数来获取或设置模型的数据。类只有一个方法,_construct(),当我们调用 _init() 方法时,并将其作为参数传递给资源模型的名字

步骤2A9:为处理真实数据库事务创建模型's ResourceModel

  • 创建资源模型 Model/Resource/News.php

    源代码
    ```
    <?php
    
    namespace BDC\SimpleNews\Model\Resource;
    
    use Magento\Framework\Model\ResourceModel\Db\AbstractDb;
    
    class News extends AbstractDb {
        /**
         * Define main table
         */
        protected function _construct() { $this->_init('bdc_simplenews', 'id'); }
    }
    
    ```
    

注意:资源模型

所有实际数据库操作都由资源模型执行。每个模型都必须有一个资源模型,因为资源模型的所有方法都期望模型作为其第一个参数。所有资源模型都必须扩展 Magento\Framework\Model\ResourceModel\Db\AbstractDb 类。这里也有一个方法,<__construct>,在其中我们调用 <_initmethod>,并向它传递两个参数。数据库中表的名字,和该表中主键列的名字。在 Magento 2 中,模型类定义了最终用户程序员将用于与模型数据交互的方法。资源模型类包含实际从数据库获取信息的函数。每个 Magento 2 中的 CRUD 模型都有一个相应的资源模型类。

每个CRUD资源模型类都扩展了Magento\Framework\Model\ResourceModel\Db\AbstractDb类。这个基类包含从单个数据库表中获取信息的逻辑。对于像我们这样的基本模型,资源模型必须做的唯一事情是从构造函数中调用_init方法。资源模型的_init方法接受两个参数。第一个是数据库表的名称(bdc_simplenews),第二个是模型的ID列(id)。虽然这超出了本文的范围,但Magento 2的活跃记录实现中没有方法通过主键链接表。如何使用多个数据库表由每个模块开发者自行决定,资源模型通常包含生成从相关表获取信息的SQL方法。

步骤2A10:创建模型's 集合类

  • 创建集合 Model/Resource/News/Collection.php

    源代码
    ```
    <?php
    namespace BDC\SimpleNews\Model\Resource\News;
    
    use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection;
    class Collection extends AbstractCollection {
        /**
         * Define model & resource model
         */
        protected function _construct(){
            $this->_init('BDC\SimpleNews\Model\News', 'BDC\SimpleNews\Model\Resource\News');
        }
    }
    
    ```
    

注意:集合

当我们需要从表中获取多行时,会使用集合。也就是说集合

  • 模型组。当我们需要
  • 从表中获取多行时
  • 与我们的主表连接表
  • 选择特定列
  • 在我们的查询中应用WHERE子句
  • 在我们的查询中使用GROUP BY或ORDER BY

有了模型和资源模型,你就有了一切从数据库中获取和保存单个模型所需的东西。然而,有时你会想要获取特定类型的多个模型。为了解决这个问题,Magento 2中的每个CRUD模型都有一个相应的资源模型集合。集合收集单个模型。由于它构建了从数据库表拉取信息的SQL代码,因此被视为资源模型。Magento 2中的所有集合都扩展了基类\ Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection。就像模型和资源模型一样,集合资源模型必须调用_init方法。集合资源模型的_init方法接受两个参数。第一个是此集合收集的模型。第二个是收集的模型资源模型。我们创建一个新的Magento 2块并注入\ Magento\Catalog\Model\ResourceModel\Product\CollectionFactory类。这是从工厂获取集合所需的。getProductCollection返回一个新的产品集合。此方法执行以下操作

  • 从集合工厂创建新的集合
  • 过滤属性(列)
  • 这次我们想加载所有数据(*),你也可以添加一个以逗号分隔的属性名称列表。只有这些属性会被加载——懒加载优化你的数据库选择语句。
  • 根据属性值过滤。可以按加载的属性值过滤集合。例如,所有价格小于1000美元的产品。我的示例使用setPageSize()只加载指定数量的产品。

步骤2A11:请求流处理-前端路由

在Frontend路由上创建自定义路由吗?

我们将了解如何创建前端路由、管理路由以及如何使用路由重写控制器。

  • routes.xml 要注册前端路由,我们必须创建一个routes.xml文件:文件:app/code/BDC/SampleNews/etc/frontend/routes.xml
<!--Use router 'standard' for frontend route-->
    <router id="standard">
        <!--Define a custom route with id and frontName-->
        <route frontName="samplenews" id="samplenews">
            <!--The module which this route match to-->
            <module name="BDC_SampleNews"/>
        </route>
    </router>

请查看代码,你会看到注册路由非常简单。你必须使用标准路由器的前端。此路由将有一个子路由,定义了模块以及2个属性

id属性是一个唯一的字符串,将标识此路由。你将使用此字符串来声明此模块动作的布局处理程序。frontName属性也是一个唯一的字符串,它将在URL请求中显示。例如,如果你声明一个如下所示的路线:

http://example.com/index.php/samplenews/controller/action 此动作的布局处理程序是:samplenews_controller_action.xml 因此,使用此示例路径,你必须在此文件夹中创建动作类。

{namespace}/{module}/Controller/{Controller}/{Action}.php

http://example.com/<router_name>/<controller_name>/<action_name>

路由器用于将URL分配给相应的控制器和动作。在这个模块中,我们需要为前端区域创建一个路由。因此,我们需要添加此文件

  • 创建 etc/frontend/routes.xml

    源代码
    ~~~
    <?xml version="1.0"?>
    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="../../../../../../lib/internal/Magento/Framework/
    App/etc/routes.xsd">
        <router id="standard">
            <route id="news" frontName="news">
                <module name="BDC_SimpleNews" />
            </route>
        </router>
    </config>
    ~~~
    

定义路由后,我们的模块的URL路径将是: http://example.com/news/

注意:控制器、路由和响应

  • 路由器:为模块定义名称,我们可以在URL中使用该名称来查找模块并执行控制器动作。

  • 控制器:与MVC应用中的典型控制器相比,Magento 2中的控制器有所不同。Magento 2控制器只负责一个特定的URL,并且只包含一个执行方法。该方法负责返回结果对象和偶尔处理输入POST数据。所有控制器都继承自\ Magento\Framework\App\Action\Action类。所需的控制器在基本路由器中搜索,然后在前端控制器中调用。

  • 响应:根据目的和必要的结果,Magento 2中的控制器可以返回几种响应类型。

  • 前端路由:请查看代码,您会发现注册路由非常简单。您必须使用标准路由器进行前端。此路由将有一个子路由,定义了该模块和2个属性

  1. id属性是一个唯一的字符串,用于识别此路由。您将使用此字符串来声明此模块动作的布局处理程序
  2. frontName属性也是一个唯一的字符串,将在URL请求中显示。例如,如果您声明了一个路由如下:此模块的URL应该是: http://example.com/index.php/nwes/controller/action 此动作的布局处理程序是:samplenews_controller_action.xml 因此,使用此示例路径,您必须在以下文件夹中创建动作类:{namespace}/{module}/Controller/{Controller}/{Action}.php

步骤2A12:创建 IndexController

  • 工厂对象:我们已经完成了创建数据库表、CRUD模型、资源模型和集合。那么如何使用它们呢?

在本部分中,我们将讨论用于模型的Factory对象。正如您所知,在OOP中,工厂方法用于实例化对象。在Magento中,Factory对象执行相同的功能。

工厂类的名称是模型类的名称,并附加“Factory”一词。因此,在我们的示例中,我们将有NewsFactory类。您不需要创建此类。Magento将为您创建它。每当Magento的对象管理器遇到以“Factory”结尾的类名时,如果该类不存在,它将自动在var/generation文件夹中生成Factory类。您将看到工厂类

use BDC\SimpleNews\Model\NewsFactory;
var/generation/<vendor_name>/<module_name>/Model/ClassFactory.php
var/generation/BDC/SimpleNew/Model/NewsFactory.php

要实例化模型对象,我们将使用自动构造函数依赖注入来注入工厂对象,然后使用工厂对象来实例化模型对象。

Magento 2注册 & 注册?

Magento 2允许您注册支持静态注册方法的全球变量。与Magento 1一样,以及与Magento 2一样,您被授权注册支持静态注册方法的全球变量。为了实现这一点,您可能以前使用过Mage::register()和Mage::registry()在Magento 1中工作,但在Magento 2平台上,注册的运行方式有所不同。您将需要应用\ Magento\Framework\Registry,它接受设置和恢复数据的注册。然而,首先,您需要了解如何创建或使用自定义注册,并且还将向您展示如何检索全局Magento 2注册对象,例如当前产品、类别、CMS页面、CMS块等。这是幸运的,因为所有这些都会在这里提到。今天的主题将帮助您熟悉Magento 2注册对象。

如何在注册/注册中获取和设置自定义属性

/**
  * @var \Magento\Framework\Registry
  */

 protected $_registry;
 /**
 * ...
 * ...
 * @param \Magento\Framework\Registry $registry,
 */
public function __construct(
    ...,
    ...,
    \Magento\Framework\Registry $registry,
    ...) {
    $this->_registry = $registry;
    ...
    ...
}

 /**
 * Setting custom variable in registry to be used
 *
 */
public function setCustomVariable() {
     $this->registry->register('custom_var', 'Added Value');
}

/**
 * Retrieving custom variable from registry
 * @return string
 */
public function getCustomVariable() {
     return $this->registry->registry('custom_var');
}
  • 创建控制器 Controller/Index/Index.php

    源代码
      <?php
    
      namespace BDC\SimpleNews\Controller\Index;
    
      use Magento\Framework\App\Action\Action;
      use Magento\Framework\App\Action\Context;
      use BDC\SimpleNews\Model\NewsFactory;
    
      class Index extends Action {
          /**
           * @var \BDC\SimpleNews\Model\NewsFactory
           */
          protected $_modelNewsFactory;
          /**
           * @param Context $context
           * @param NewsFactory $modelNewsFactory
           */
          public function __construct(
              Context $context,
              NewsFactory $modelNewsFactory ) {
              parent::__construct($context);
              $this->_modelNewsFactory = $modelNewsFactory;
          }
          public function execute(){
              /**
               * When Magento get your model, it will generate a Factory class
               * for your model at var/generaton folder and we can get your
               * model by this way
               */
              $newsModel = $this->_modelNewsFactory->create();
    
              // Load the item with ID is 1
              $item = $newsModel->load(1);
              var_dump($item->getData());
    
              // Get news collection
              $newsCollection = $newsModel->getCollection();
              // Load all data of collection
              var_dump($newsCollection->getData());
          }
      }
    
      ```
    </details>
    
    

定义控制器后,我们的模块的URL路径将是以下数据:http://example.com/news/

NewsDataFrontend

注释

  • 动作类:路由在匹配请求时返回的动作类的扩展。这些类中的execute()函数包含分发请求的逻辑。每个动作类应实现一个或多个Magento\Framework\App\Action\HttpHTTP MethodActionInterface,以声明它可以处理哪些HTTP请求方法。Magento对所有POST非AJAX请求都实施了表单密钥验证 - 如果您的动作不需要该验证或您想修改它,可以实现CsrfAwareActionInterface。如果需要将请求转发到类中的另一个动作,请使用 _forward()函数。示例
$this->_forward('action', 'controller', 'Other_Module')

响应:创建具有不同响应类型的前端控制器

  • 页面结果(\Magento\Framework\View\Result\Page)是最常见的响应类型。通过返回此对象,控制器开始基于相应的XML布局处理进行标准页面渲染。
public function __construct(
   $pageFactory Magento\Framework\View\Result\PageFactory
) {
   $this->pageResultFactory = $pageFactory
}
public function execute()
{
   return $this->pageResultFactory->create();
}
  • 原始结果(\Magento\Framework\Controller\Result\Raw)在您想向浏览器返回字符串而不使用Magento布局和视图渲染时使用。
public function __construct(
   Magento\Framework\Controller\Result\Raw $rawResultFactory ,
) {
   $this->rawResultFactory = $rawResultFactory;
}
public function execute()
{
   $result = $this->rawResultFactory->create();
   $result->setHeader('Content-Type', 'text/xml');
   $result->setContents('<root><block></block></root>);
   return $result;
}
  • 转发结果(\Magento\Framework\Controller\Result\Forward)允许调用另一个方法/控制器,而无需更改URL或进行重定向。
public function __construct(
   Magento\Framework\Controller\Result\Forward\Factory $resultForwardFactory    
) {
   $this->resultForwardFactory = $resultForwardFactory;
}
public function execute()
{
   $result = $this->resultForwardFactory->create();
   $result->forward('noroute');    
   return $result;
}
  • 重定向结果(\Magento\Framework\Controller\Result\Redirect)用于用户需要被重定向到不同的URL的情况。
public function __construct(
   Magento\Framework\Controller\Result\Redirect\Factory $resultRedirectFactory
) {
   $this->resultRedirectFactory = $resultRedirectFactory;
}
public function execute()
{
   $result = $this->resultRedirectFactory->create();
   $result->setPath('*/*/index');
   return $result;
}

第二部分:后端新闻模块 返回顶部

什么是作用域?

如果您的Magento安装具有网站、商店或视图的层次结构,您可以设置配置设置的上下文,即“作用域”,以应用于安装的特定部分。许多数据库实体的上下文也可以分配特定的作用域,以确定其在商店层次结构中的使用方式。有关更多信息,请参阅:产品作用域和价格作用域。

一些配置设置,如邮政编码,具有[全局]作用域,因为在整个系统中使用的是相同的值。[网站]作用域适用于该级别以下的任何商店,包括所有商店及其视图。任何具有[商店视图]作用域的项目可以为每个商店视图设置不同的值,这通常用于支持多种语言。

除非商店处于单店模式,否则每个配置设置的作用域都会显示在字段标签下方的小文本中。如果您的安装包含多个网站、商店或视图,您应在进行任何更改之前始终选择适用设置的商店视图。

什么是作用域设置?

  • 全局:可在整个Magento安装中使用的系统范围设置和资源。
  • 网站:仅限于当前网站的设置和资源。每个网站都有一个默认商店。
  • 商店:仅限于当前商店的设置和资源。每个商店都有一个默认根类别(主菜单)和默认商店视图。
  • 商店视图:仅限于当前商店视图的设置和资源。

如何通过作用域编写和获取配置值?

Magento 2的一个重要但不太详细的特性是如何通过作用域编写和获取配置值。您会发现大量的代码示例,说明如何在全局范围内这样做。有时您需要针对不同的商店程序化地设置不同的设置。所以,这是如何工作的。

Magento将其所有adminhtml设置保存在您的Magento数据库中的core_config表中。在那里,您可以按路径获取值,这是一个表示路径和变量名的字符串。使用此路径,您可以通过Magento 2核心方法获取或设置值。为此,您需要使用

  • \Magento\Framework\App\Config\Storage\WriteInterface将配置值写入数据库
  • \Magento\Framework\App\Config\ScopeConfigInterface从数据库读取配置值

如何按作用域编写商店配置值?

以下示例代码显示了如何按作用域编写商店配置值

class WriteConfig {
    protected $_logger;
    protected $_storeManager;
    protected $_configWriter;

    public function __construct(
        \Psr\Log\LoggerInterface $logger,
        \Magento\Framework\App\Config\Storage\WriterInterface $configWriter,
        \Magento\Store\Model\StoreManagerInterface $storeManager ){
        $this->_logger = $logger;
        $this->_configWriter = $configWriter;
        $this->_storeManager = $storeManager;
    }

    public function setConfig($value) {
        //for all websites
        $websites = $this->_storeManager->getWebsites();
        $scope = "websites";
        foreach($websites as $website) {
            echo $website->getId().":\n";

            $this->_configWriter->save('my_section/something/configvaluename', $value, $scope, $website->getId());
        }

        return $this;
    }
}

您只需调用带有给定值的setConfig()方法。该方法将此值存储到所有网站的指定路径中。因此,为每个定义的网站生成一个新的设置(核心_config表中的一行)。这是通过在保存方法中使用第三个和第四个参数来完成的。您使用一个唯一的路径、一个值、一个范围以及此范围的ID。如果您不使用范围,您将值写入默认值(商店ID 0)。您可以将值存储在“网站”或“商店”范围中。

如何按范围读取商店配置值?

现在是按商店读取数据的时候了。您可以使用以下示例代码来完成此操作

class ReadConfig
{
    protected $_scopeConfig;

    public function __construct(
        \Magento\Framework\App\Helper\Context $context,
        \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
    ){
        $this->_scopeConfig = $scopeConfig;
        parent::__construct($context);
    }

    public function getConfig() {
        return $this->_scopeConfig->getValue("my_section/something/configvaluename", "websites");
    }
}

这很简单,您只需要使用getValue()方法,并添加一个带有范围的第二个参数(这里我们使用网站范围)。这将返回当前网站的存储值。

如何在Magento 2中使用配置文件?

Magento 2中存储配置值的主要位置有两个:数据库(core_config_data表)和XML文件。存储在数据库中的配置可以通过管理员面板进行更改,而位于XML文件中的数据具有技术性质,只能由开发人员更改。

在Magento 2中使用配置文件很简单。配置文件包括

  • app/etc/config.php — 包含所有模块的声明;
  • app/etc/env.php — 描述包含前端面板名称、数据库连接数据、表前缀、当前商店模式、缓存类型和状态的数组。这些文件在Magento 2设置过程中生成。您可以直接编辑文件来更改它们。然而,运行终端命令bin/magento被认为是最佳实践。

解释如何在Magento 2中利用配置XML和变量范围?

  • etc/config.xml — 包含管理员面板菜单中“存储”>“配置”下的默认选项值。此菜单可以在system.xml中进行配置;
  • di.xml — 包含依赖注入的配置;
  • etc/events.xml — 观察器和事件列表;
  • etc/routes.xml — 路由列表;
  • etc/config.xml — 包含模块设置“存储”>“配置”的默认值;
  • etc/acl.xml — 向资源树添加模块资源,允许您为不同的用户配置访问权限。
  • etc/crontab.xml — 添加和配置cron作业的任务;
  • etc/module.xml — 声明模块的名称、版本以及它对其他模块的依赖关系;
  • etc/widget.xml — 存储小部件设置;
  • etc/indexer.xml — 声明一种新的索引类型。它指定view_id参数,指向etc/mview.xml中描述的视图;etc/mview.xml — 描述etc/indexer.xml中描述的所有索引的表示;
  • etc/webapi.xml — 定义Web API组件,指定要使用的方法以及为特定请求连接的资源;
  • etc/view.xml — 包含产品图片的属性;
  • etc/product_types.xml — 描述商店中的产品类型;
  • etc/product_options.xml — 描述产品可以具有的选项类型以及渲染它们的类;
  • etc/extension_attributes.xml — Magento 2中出现的新功能,可以添加自定义属性。此文件描述了属性、其类型,可以是简单的或复杂的以及表示接口;
  • etc/catalog_attributes.xml — 组合属性;
  • etc/adminhtml/system.xml — 仅适用于管理区域,向“存储”>“配置”添加选项卡,描述表单的章节和字段;
  • etc/adminhtml/menu.xml — 仅适用于管理区域,向管理员面板菜单添加项目。

解释配置加载发生三个阶段?

  • 加载系统级配置。加载运行Magento 2所需的文件,如config.php;
  • 加载全局区域配置。加载位于app/etc/Magento 2目录中的文件,例如di.xml,以及位于etc/module文件夹中的与全局作用域相关的文件。
  • 加载特定区域的配置。加载位于etc/adminhtml或etc/frontend文件夹中的文件。配置文件根据它们的完整XPath连接。在$idAttributes数组中定义特定属性作为标识符。连接2个文件后,它们包含原始文件中的所有节点和值。第二个XML文件将添加或替换第一个XML文件的节点。

Magento/Framework/Config为开发者提供了哪些接口?

Magento/Framework/Config为开发者提供了以下接口:

  • \Magento\Framework\Config\DataInterface — 在作用域内通过键返回值,将配置数据连接到对象;
  • \Magento\Framework\Config\ScopeInterface — 识别当前作用域;
  • \Magento\Framework\Config\FileResolverInterface — 识别必须读取的一组文件;
  • \Magento\Framework\Config\ReaderInterface — 读取配置数据;
  • \Magento\Framework\Config\ConverterInterface — 将DOM对象转换为数组;
  • \Magento\Framework\Config\SchemaLocatorInterface — 确定验证方案的路径;
  • \Magento\Framework\Config\ValidationStateInterface — 确定当前的验证状态。

用于加载XML的类组

  • 配置用于访问配置值;
  • 读取器用于读取文件;
  • SchemaLocator存储验证方案的路径。

XML配置有多少种验证类型?

Magento 2为XML配置文件提供两种类型的验证:合并前验证和合并后验证。可以是相同或不同的方案。要创建自定义配置文件,需要创建以下元素

  • XSD方案
  • 配置PHP文件
  • 配置读取器
  • 方案定位器
  • 转换器

以Magento_Catalog的product_types.xml模块为例进行说明?

Magento_Catalog的product_types.xml模块是一个自定义配置文件的示例。每个模块都可以使用product_types.xml文件添加自己的产品类型,并且这些文件将进行验证和合并。

  • 我们将从创建XSD文件开始。product_types.xsd验证方案在合并前用于Magento_Catalog,product_types_merged.xsd用于合并后的XML文件。
  • 创建PHP配置文件以访问文件中的数据。在我们的示例中是Config.php。为了提供对product_types.xml文件数据的访问,它实现了Magento\Catalog\Model\ProductType\ConfigInterface接口及其所有方法。
  • 在Config.php的构造函数中,我们需要获取读取器类。在我们的例子中,它是Magento\Catalog\Model\ProductType\Config\Reader。这是一个定义了$_idAttributes属性的小类。在$fileName变量的构造函数中,我们定义了XML文件的名字。
  • Magento\Catalog\Model\ProductType\Config\SchemaLocator实现了两个方法:getSchema和getPerFileSchema,并返回合并的XSD和普通XSD文件的路径。在构造函数中,我们在$_schema和$_perFileSchema属性中定义了这些路径。
  • 创建转换器类。在我们的示例中:Magento\Catalog\Model\ProductType\Config\Converter实现了\Magento\Framework\Config\ConverterInterface,并实现了convert方法,该方法将节点DOM树转换为数组。这就是关于Magento 2中的配置XML、变量作用域和配置文件的内容。

第二步B1:设置模块的后端/系统配置

什么是系统配置?

system.xml是一个配置文件,用于在Magento 2系统配置中创建配置字段。如果您模块有一些管理员需要设置的设置,则需要它。您可以通过转到商店 -> 设置 -> 配置来检查其外观。magento 2系统配置页面在逻辑上分为几个部分:选项卡、部分、组、字段。

如何设置默认值?

系统.xml中创建后的每个字段都不会有任何值。当你调用它们时,你会收到‘null’的结果。因此,对于此模块,我们需要为字段设置默认值,并且你可以调用值而无需进入配置、设置值和保存。此默认值将保存在etc文件夹中的config.xml中。让我们为这个简单配置创建它:[etc/config.xml](https://github.com/bdcrops/module-simplenews/blob/HEAD/etc/config.xml)

<default>
    <section>
        <group>
            <field>{value}</field>
        </group>
    </section>
</default>

如何从配置中获取值?

首先,保存值并刷新缓存,然后你可以从数据库中获取保存的值。在system.xml中,我们增加了2个字段:enable和display_text。所以路径应该是:samplenews/general/enable samplenews/general/display_text 简单调用:ex

$this->scopeConfig->getValue('samplenews/general/enable', \Magento\Store\Model\ScopeInterface::SCOPE_STORE);
$this->scopeConfig->getValue('samplenews/general/display_text', \Magento\Store\Model\ScopeInterface::SCOPE_STORE);
  • 创建文件[etc/adminhtml/system.xml](https://github.com/bdcrops/module-simplenews/blob/HEAD/etc/adminhtml/system.xml) 目的:此文件将在“存储 > 设置 > 配置”部分声明你的配置,并将以下代码插入其中

    源代码
    ```
    <?xml version="1.0"?>
    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../Backend/etc/system_file.xsd">
        <system>
            <tab id="bdc" translate="label" sortOrder="1">
                <label>BDC</label>
            </tab>
            <section id="simplenews" translate="label" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1">
                <label>Simple News</label>
                <tab>bdc</tab>
                <resource>BDC_SimpleNews::system_config</resource>
                <group id="general" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1">
                    <label>General Settings</label>
                    <field id="enable_in_frontend" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1">
                        <label>Enable in frontend</label>
                        <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
                    </field>
                    <field id="head_title" translate="label comment" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1">
                        <label>Head title</label>
                        <comment>Fill head title of news list page at here</comment>
                        <validate>required-entry</validate>
                    </field>
                    <field id="lastest_news_block_position" translate="label" type="select" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1">
                        <label>Lastest news block position</label>
                        <source_model>BDC\SimpleNews\Model\System\Config\LastestNews\Position</source_model>
                    </field>
                </group>
            </section>
        </system>
    </config>
    ```
    

注意:[系统配置](https://inviqa.com/blog/how-use-system-configuration-and-helpers-magento-2)

Magento 2中的系统配置值存储在core_config_data数据库表中,与Magento 1完全相同。但xml配置文件有所不同。system.xml是一个用于在Magento 2系统配置中创建配置字段的配置文件。系统配置文件位于etc/adminhtml/system.xml

system.xml是一个用于在Magento 2系统配置中创建配置字段的配置文件。如果你的模块有一些设置,管理员需要设置,那么你需要这个。你可以去“存储 > 设置 > 配置”看看它是什么样子。

注意:[MCAD: 5.4 设置菜单项](https://belvg.com/blog/magento-2-custom-system-configuration.html)

  • 如何向给定的标签添加新的菜单项?
  • 如何向管理员菜单添加新的标签?

步骤2B2:创建自定义源模型

  • 创建文件[Model/System/Config/LastestNews/Position.php](https://github.com/bdcrops/module-simplenews/blob/HEAD/Model/System/Config/LastestNews/Position.php)

    源代码
    ```
    <?php
    namespace BDC\SimpleNews\Model\System\Config\LastestNews;
    use Magento\Framework\Option\ArrayInterface;
    class Position implements ArrayInterface{
        const LEFT      = 1;
        const RIGHT     = 2;
        const DISABLED  = 0;
    
        /**
         * Get positions of lastest news block
         *
         * @return array
         */
        public function toOptionArray(){
            return [
                self::LEFT => __('Left'),
                self::RIGHT => __('Right'),
                self::DISABLED => __('Disabled')
            ];
        }
    }
    
    ```
    

步骤2B3:为配置部分创建ACL角色

  • 创建文件[etc/acl.xml](https://github.com/bdcrops/module-simplenews/blob/HEAD/etc/acl.xml)(目的:此文件将为你的配置部分创建一个角色)并将以下代码插入其中

    源代码
    ```
    <?xml version="1.0"?>
    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Acl/etc/acl.xsd">
        <acl>
            <resources>
                <resource id="Magento_Backend::admin">
                    <resource id="Magento_Backend::stores">
                        <resource id="Magento_Backend::stores_settings">
                            <resource id="Magento_Config::config">
                                <resource id="BDC_SimpleNews::system_config" title="Simple News Section" />
                            </resource>
                        </resource>
                    </resource>
                </resource>
            </resources>
        </acl>
    </config>
    ```
    

注意:5.3 定义/识别ACL的基本术语和元素

  • 你将如何向新实体添加新的ACL资源?
  • 你如何管理现有的ACL层次结构?

步骤2B4:为配置选项设置一些默认值

  • 创建文件[etc/config.xml](https://github.com/bdcrops/module-simplenews/blob/HEAD/etc/config.xml)并将以下代码插入其中

    源代码
    ```
    <?xml version="1.0"?>
    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="../../Core/etc/config.xsd">
        <default>
            <simplenews>
                <general>
                    <enable_in_frontend>1</enable_in_frontend>
                    <head_title>BDC - Simple News</head_title>
                    <lastest_news_position>1</lastest_news_position>
                </general>
            </simplenews>
        </default>
    </config>
    
    ```
    

步骤2B5:创建 Helper Data 类

为什么需要创建助手?

Magento 2中,助手可以在控制器、模型、视图中调用,甚至在其他助手中调用。助手可以被视为全局且始终可用的元素。它们甚至可以被创建为单个对象的实例。此外,一旦你将它们注入到类中,就可以在任何地方调用它们。助手主要创建用于最常见功能的函数。例如,你可以使用助手在Magento应用程序中构建日志。Magento 2助手类包括在整个应用程序中常用的一系列函数和方法。所有已声明为助手的函数都可以在任何地方调用,包括文件、模型、块、控制器类或从另一个助手在Magento 2中调用。

什么是助手?

在早期版本的Magento 2中,有一个助手工厂可用,它使开发人员可以实例化助手方法。此外,你可以使用以下代码使用ObjectManager来实例化助手工厂。

$object_manager = \Magento\Core\Model\ObjectManager::getInstance();
$helper_factory = $object_manager->get('\Magento\Core\Model\Factory\Helper');
$helper = $helper_factory->get('\Magento\Core\Helper\Data');

然而,此代码仍然存在一些问题。幸运的是,一个更好的概念已经被引入,那就是Magento 2中的依赖注入。

使用这个概念,环境将创建并提供一个对象,而不是实例化它。例如,如果一个类被写成以下形式

class Helper{
   public function __contruct(Helper $xyz){
       $this->xyz= $xyz;
   }
}

在Helper类构造函数中,自动创建Helper类的对象并赋值给$xyz。这是依赖注入。

通过这个概念,Magento 2提供了高价值松耦合模块组合的概念。如果你想将其注入到特定的类中,只需将其对象添加到该类的构造函数中。然而,你需要记住,你不能将一个依赖项注入两次。

  • 创建文件: Helper/Data.php 并将以下代码插入到其中

    源代码
    <?php
    
    namespace BDC\SimpleNews\Helper;
    
    use Magento\Framework\App\Helper\AbstractHelper;
    use Magento\Framework\App\Config\ScopeConfigInterface;
    use Magento\Framework\App\Helper\Context;
    use Magento\Store\Model\ScopeInterface;
    
    class Data extends AbstractHelper {
     const XML_PATH_ENABLED      = 'simplenews/general/enable_in_frontend';
     const XML_PATH_HEAD_TITLE   = 'simplenews/general/head_title';
     const XML_PATH_LASTEST_NEWS = 'simplenews/general/lastest_news_block_position';
    
     /**
       * @var \Magento\Framework\App\Config\ScopeConfigInterface
       */
      protected $_scopeConfig;
    
      /**
       * @param Context $context
       * @param ScopeConfigInterface $scopeConfig
       */
      public function __construct(
         Context $context,
         ScopeConfigInterface $scopeConfig ) {
         parent::__construct($context);
         $this->_scopeConfig = $scopeConfig;
      }
    
     /**
       * Check for module is enabled in frontend
       *
       * @return bool
       */
     public function isEnabledInFrontend($store = null){
        return $this->_scopeConfig->getValue(
           self::XML_PATH_ENABLED,
           ScopeInterface::SCOPE_STORE
        );
     }
    
     /**
       * Get head title for news list page
       *
       * @return string
       */
     public function getHeadTitle() {
        return $this->_scopeConfig->getValue(
           self::XML_PATH_HEAD_TITLE,
           ScopeInterface::SCOPE_STORE
        );
     }
    
     /**
       * Get lastest news block position (Left, Right, Disabled)
       *
       * @return int
       */
     public function getLastestNewsBlockPosition() {
        return $this->_scopeConfig->getValue(
           self::XML_PATH_LASTEST_NEWS,
           ScopeInterface::SCOPE_STORE
        );
     }
    }
    
    

步骤2B6:为 Magento 后端创建菜单

解释以下属性:id、标题、模块、父级、动作、资源?如下代码

<add id="BDC_SimpleNews::news" title="Manage News" module="BDC_SimpleNews" sortOrder="10" action="bdc_simplenews/news" resource="BDC_SimpleNews::news" parent="BDC_SimpleNews::samplenews"/>
  • id属性是该笔记的标识符。它是一个唯一的字符串,应遵循以下格式:{Vendor_ModuleName}::{menu_description}。

  • 标题属性是在菜单栏上显示的文本。

  • 模块属性定义了该菜单所属的模块。

  • sortOrder属性定义了菜单的位置。较低的值将显示在菜单顶部。

  • 父级属性是其他菜单节点的ID。它将告诉Magento该菜单是另一个菜单的子菜单。在此示例中,我们有parent="BDC_SampleNews::samplenews",因此我们知道该菜单“管理新闻”是“Hello World”菜单的子菜单,它将显示在Hello World菜单中。

  • 动作属性将定义该菜单链接到的页面的URL。如前所述,URL将遵循以下格式{router_name}{controller_folder}{action_name}。在此示例中,该菜单将链接到SampleNews模块、News控制器和Index动作。

  • 资源属性用于定义管理员用户必须拥有的ACL规则,才能查看和访问此菜单。我们将在其他主题中了解更多关于ACL的详细信息。

  • 创建文件: etc/adminhtml/menu.xml(目的:在此声明你的模块的菜单项)并将以下代码插入其中

    源代码
    ```
    <?xml version="1.0"?>
    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="../../../Backend/etc/menu.xsd">
    <menu>
        <add id="BDC_SimpleNews::main_menu" title="Simple News"
            module="BDC_SimpleNews" sortOrder="20"
            resource="BDC_SimpleNews::simplenews" />
        <add id="BDC_SimpleNews::add_news" title="Add News"
            module="BDC_SimpleNews" sortOrder="1" parent="BDC_SimpleNews::main_menu"
            action="simplenews/news/new" resource="BDC_SimpleNews::manage_news" />
        <add id="BDC_SimpleNews::manage_news" title="Manage News"
            module="BDC_SimpleNews" sortOrder="2" parent="BDC_SimpleNews::main_menu"
            action="simplenews/news/index" resource="BDC_SimpleNews::manage_news" />
        <add id="BDC_SimpleNews::configuration" title="Configurations"
            module="BDC_SimpleNews" sortOrder="3" parent="BDC_SimpleNews::main_menu"
            action="adminhtml/system_config/edit/section/simplenews"
            resource="BDC_SimpleNews::configuration" />
    </menu>
    </config>
    ```
    

MenuLinkAdmin

步骤2B7:请求流处理/创建后端路由文件

如何创建管理路由?

此路由将与前端路由相同,但你必须在adminhtml文件夹中声明它,具有router id为admin。文件:app/code/BDC/SampleNews/etc/adminhtml/routes.xml

管理页面的URL结构与前端页面相同,但必须在route_frontName之前添加admin_area名称以识别这是一个管理员路由。例如,管理CMS页面的URL

http://example.com/index.php/admin/bdc_samplenews/controller/action 管理页面的控制器动作将被添加到Controller/Adminhtml文件夹中。例如,对于上述URL

{namespace}/{module}/Controller/Adminhtml/{Controller}/{Action}.php

<!--Use router 'admin' for admin route -->
    <router id="admin">
        <!--Define a custom route with id and frontName -->
        <route id="bdc_samplenews" frontName="bdc_samplenews">
            <!--The module which this route match to-->
            <module name="BDC_SampleNews"/>
        </route>
    </router>
  • 创建文件 etc/adminhtml/routes.xml(目的:在此声明你的模块的后端路由)并将以下代码插入其中

    源代码
    ```
    <?xml version="1.0"?>
    
    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="../../../../../../lib/internal/Magento/
    Framework/App/etc/routes.xsd">
        <router id="admin">
            <route id="simplenews" frontName="simplenews">
                <module name="BDC_SimpleNews" />
            </route>
        </router>
    </config>
    
    ```
    

注意

  • 管理路由:此路由将与前端路由相同,但你必须在adminhtml文件夹中声明它,具有router id为admin。/etc/adminhtml/routes.xml 管理页面的URL结构与前端页面相同,但必须在route_frontName之前添加admin_area名称以识别这是一个管理员路由。例如,管理CMS页面的URL
http://example.com/index.php/admin/simplenews/controller/action  {namespace}/{module}/Controller/Adminhtml/{Controller}/{Action}.php

步骤2B8:更新 acl.xml 以添加更多角色

如何将我们的模块添加/创建到ACL角色中?

在Admin菜单和系统配置文章中,您看到我们总是在创建时有一个资源属性。现在我们将注册这些资源到系统中,以便Magento可以识别并让我们为它们设置角色。要注册资源,我们使用位于

app/code/{namespace}/{module}/etc/acl.xml

开发者ACL规则?

作为模块开发者,ACL 规则带来了一些有趣的挑战。首先,作为模块开发者,你需要在模块中添加几个地方的 ACL 规则检查。以下是一些示例:

  • 管理应用程序中的每个 URL 端点/控制器都必须实现一个 _isAllowed 方法,以确定用户是否可以访问 URL 端点。

  • 左侧导航中的每个菜单项也都有一个特定的 ACL 规则,用于控制菜单是否为登录用户显示。这通常与 _isAllowed 中的规则相同。

  • 系统 -> 配置中的每个配置字段都有一个特定的 ACL 规则,用于控制菜单是否显示。

尽管是必填字段,但模块开发者应该如何设置和结构化自己的规则并没有硬性规定。此外,模块开发者可能还需要一些特定于其模块的额外规则。本文无法回答这些问题,但我们将向您展示如何检查当前用户是否与特定的 ACL 规则相匹配,如何查找现有规则的 ID 值,以及如何创建自己的 ACL 规则树。

解释 Magento_Backend::admin, Id Title, sortOrder?

  • Magento_Backend::admin:我们的资源将被放置在 Magento_Backend::admin 的子目录下。每个资源都将有一个 Id、标题和 sortOrder 属性

  • Id:属性是该资源的标识符。当你在 Admin 菜单、配置中定义资源并限制模块控制器访问时,可以使用它。这是一个唯一的字符串,应该采用以下格式:Vendor_ModuleName::resource_name。

  • Title:属性是在资源树中显示该资源的标签。

  • sortOrder:属性定义了该资源在树中的位置。

  • 打开此文件 etc/acl.xml 并将其源代码修改如下

    源代码
    ```
    <?xml version="1.0"?>
    
    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/
    Framework/Acl/etc/acl.xsd">
      <acl>
          <resources>
              <resource id="Magento_Backend::admin">
                  <resource id="BDC_SimpleNews::simplenews" title="Simple News" sortOrder="100">
                      <resource id="BDC_SimpleNews::add_news" title="Add News" sortOrder="1" />
                      <resource id="BDC_SimpleNews::manage_news" title="Manage News" sortOrder="2" />
                      <resource id="BDC_SimpleNews::configuration" title="Configurations" sortOrder="3" />
                  </resource>
                  <resource id="Magento_Backend::stores">
                      <resource id="Magento_Backend::stores_settings">
                          <resource id="Magento_Config::config">
                              <resource id="BDC_SimpleNews::system_config" title="Simple News Section" />
                          </resource>
                      </resource>
                  </resource>
              </resource>
          </resources>
      </acl>
    </config>
    
    ```
    

步骤2B9:为网格创建布局

  • 创建文件: view/adminhtml/layout/simplenews_news_index.xml (目的:此文件用于声明网格容器块)并将其以下代码插入其中

    源代码
    <?xml version="1.0"?>
    
    <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../lib/internal/Magento/Framework/View/Layout/etc/page_configuration.xsd">
     <update handle="formkey"/>
     <update handle="simplenews_news_grid_block"/>
     <body>
         <referenceContainer name="content">
             <block class="BDC\SimpleNews\Block\Adminhtml\News"
                 name="bdc_simplenews_news.grid.container" />
         </referenceContainer>
     </body>
    </page>
    
    

步骤2B10:为网格容器创建布局

  • 创建文件:app/code/BDC/SimpleNews/view/adminhtml/layout/simplenews_news_grid_block.xml (目的:此文件用于声明网格块的内容)并将其以下代码插入其中

    源代码
    ```
    <?xml version="1.0"?>
    
    <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../lib/internal/Magento/Framework/View/Layout/etc/page_configuration.xsd">
        <body>
          <referenceBlock name="bdc_simplenews_news.grid.container">
             <block class="Magento\Backend\Block\Widget\Grid" name="bdc_simplenews_news.grid"
                 as="grid">
                 <arguments>
                     <argument name="id" xsi:type="string">newsGrid</argument>
                     <argument name="dataSource" xsi:type="object">BDC\SimpleNews\Model\Resource\News\Collection</argument>
                     <argument name="default_sort" xsi:type="string">id</argument>
                     <argument name="default_dir" xsi:type="string">desc</argument>
                     <argument name="save_parameters_in_session" xsi:type="boolean">true</argument>
                     <argument name="use_ajax" xsi:type="boolean">true</argument>
                     <argument name="grid_url" xsi:type="url" path="*/*/grid">
                         <param name="_current">1</param>
                     </argument>
                 </arguments>
                    <block class="Magento\Backend\Block\Widget\Grid\Massaction"
                        name="bdc_simplenews_news.grid.massaction" as="grid.massaction">
                        <arguments>
                            <argument name="massaction_id_field" xsi:type="string">id</argument>
                            <argument name="form_field_name" xsi:type="string">news</argument>
                            <argument name="options" xsi:type="array">
                                <item name="delete" xsi:type="array">
                                    <item name="label" xsi:type="string" translate="true">Delete</item>
                                    <item name="url" xsi:type="string">*/*/massDelete</item>
                                    <item name="confirm" xsi:type="string" translate="true">Are you sure you want to delete?</item>
                                </item>
                            </argument>
                        </arguments>
                    </block>
                    <block class="Magento\Backend\Block\Widget\Grid\ColumnSet"
                        name="bdc_simplenews_news.grid.columnSet" as="grid.columnSet">
                        <arguments>
                            <argument name="rowUrl" xsi:type="array">
                                <item name="path" xsi:type="string">*/*/edit</item>
                                <item name="extraParamsTemplate" xsi:type="array">
                                    <item name="id" xsi:type="string">getId</item>
                                </item>
                            </argument>
                        </arguments>
                        <block class="Magento\Backend\Block\Widget\Grid\Column" as="id">
                            <arguments>
                                <argument name="header" xsi:type="string" translate="true">ID</argument>
                                <argument name="type" xsi:type="string">number</argument>
                                <argument name="id" xsi:type="string">id</argument>
                                <argument name="index" xsi:type="string">id</argument>
                            </arguments>
                        </block>
                        <block class="Magento\Backend\Block\Widget\Grid\Column" as="title">
                            <arguments>
                                <argument name="header" xsi:type="string" translate="true">Title</argument>
                                <argument name="index" xsi:type="string">title</argument>
                            </arguments>
                        </block>
                        <block class="Magento\Backend\Block\Widget\Grid\Column" as="summary">
                            <arguments>
                                <argument name="header" xsi:type="string" translate="true">Summary</argument>
                                <argument name="index" xsi:type="string">summary</argument>
                            </arguments>
                        </block>
                        <block class="Magento\Backend\Block\Widget\Grid\Column" as="status">
                            <arguments>
                                <argument name="header" xsi:type="string" translate="true">Status</argument>
                                <argument name="index" xsi:type="string">status</argument>
                                <argument name="type" xsi:type="string">options</argument>
                                <argument name="options" xsi:type="options" model="BDC\SimpleNews\Model\System\Config\Status"/>
                            </arguments>
                        </block>
                        <block class="Magento\Backend\Block\Widget\Grid\Column" as="action" acl="BDC_SimpleNews::manage_news">
                            <arguments>
                                <argument name="id" xsi:type="string">action</argument>
                                <argument name="header" xsi:type="string" translate="true">Action</argument>
                                <argument name="type" xsi:type="string">action</argument>
                                <argument name="getter" xsi:type="string">getId</argument>
                                <argument name="filter" xsi:type="boolean">false</argument>
                                <argument name="sortable" xsi:type="boolean">false</argument>
                                <argument name="index" xsi:type="string">stores</argument>
                                <argument name="is_system" xsi:type="boolean">true</argument>
                                <argument name="actions" xsi:type="array">
                                    <item name="view_action" xsi:type="array">
                                        <item name="caption" xsi:type="string" translate="true">Edit</item>
                                        <item name="url" xsi:type="array">
                                            <item name="base" xsi:type="string">*/*/edit</item>
                                        </item>
                                        <item name="field" xsi:type="string">id</item>
                                    </item>
                                </argument>
                                <argument name="header_css_class" xsi:type="string">col-actions</argument>
                                <argument name="column_css_class" xsi:type="string">col-actions</argument>
                            </arguments>
                        </block>
                    </block>
             </block>
          </referenceBlock>
        </body>
    </page>
    
    ```
    

步骤2B11:为异步加载创建布局

  • 创建文件:app/code/BDC/SimpleNews/view/adminhtml/layout/simplenews_news_grid.xml (目的:此文件用于声明在使用 AJAX 重新加载网格时的网格内容)并将其以下代码插入其中

    源代码
    <?xml version="1.0"?>
    <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../lib/internal/Magento/Framework/View/Layout/etc/layout_generic.xsd">
        <update handle="formkey" />
        <update handle="simplenews_news_grid_block" />
        <container name="root">
            <block class="Magento\Backend\Block\Widget\Grid\Container" name="bdc_simplenews_news.grid.container" template="Magento_Backend::widget/grid/container/empty.phtml"/>
        </container>
    </page>
    
    

步骤2B12:创建新闻状态选项文件

  • 创建文件:app/code/BDC/SimpleNews/Model/System/Config/Status.php (目的:此文件用于获取新闻状态选项)并将其以下代码插入其中

    源代码
    ```
    <?php
    
    namespace BDC\SimpleNews\Model\System\Config;
    
    use Magento\Framework\Option\ArrayInterface;
    
    class Status implements ArrayInterface {
        const ENABLED  = 1;
        const DISABLED = 0;
        /**
         * @return array
         */
        public function toOptionArray(){
            $options = [
                self::ENABLED => __('Enabled'),
                self::DISABLED => __('Disabled')
            ];
    
            return $options;
        }
    }
    
    ```
    

步骤2B13:为后端创建新闻块

  • 创建文件:app/code/BDC/SimpleNews/Block/Adminhtml/News.php (目的:这是网格容器的块文件)并将其以下代码插入其中

    源代码
    ```
    <?php
    namespace BDC\SimpleNews\Model\System\Config;
    use Magento\Framework\Option\ArrayInterface;
    class Status implements ArrayInterface {
        const ENABLED  = 1;
        const DISABLED = 0;
        /**
         * @return array
         */
        public function toOptionArray(){
            $options = [
                self::ENABLED => __('Enabled'),
                self::DISABLED => __('Disabled')
            ];
    
            return $options;
        }
    }
    
    ```
    

步骤 2B14:创建状态

  • 创建文件:app/code/BDC/SimpleNews/Model/System/Config/Status.php (目的:检查)并将其以下代码插入其中

    源代码
    ```
    <?php
    
    namespace BDC\SimpleNews\Model\System\Config;
    
    use Magento\Framework\Option\ArrayInterface;
    
    class Status implements ArrayInterface {
        const ENABLED  = 1;
        const DISABLED = 0;
        /**
         * @return array
         */
        public function toOptionArray(){
            $options = [
                self::ENABLED => __('Enabled'),
                self::DISABLED => __('Disabled')
            ];
    
            return $options;
        }
    }
    
    ```
    

步骤 2B15:为子操作类创建后端控制器文件

  • 创建文件:app/code/BDC/SimpleNews/Controller/Adminhtml/News.php (目的:我将此文件用作根控制器,操作类将扩展此控制器)并将其以下代码插入其中

    源代码
    ```
    <?php
    namespace BDC\SimpleNews\Block\Adminhtml;
    use Magento\Backend\Block\Widget\Grid\Container;
    
    class News extends Container{
       /**
         * Constructor
         *
         * @return void
         */
       protected function _construct(){
            $this->_controller = 'adminhtml_news';
            $this->_blockGroup = 'BDC_SimpleNews';
            $this->_headerText = __('Manage News');
            $this->_addButtonLabel = __('Add News');
            parent::_construct();
        }
    }
    
    ```
    

步骤2B16:创建 Backend Action 文件 Index.php

  • 创建文件:app/code/BDC/SimpleNews/Controller/Adminhtml/News/Index.php (目的:这是索引操作)并将其以下代码插入其中

    源代码
    ```
    <?php
    
    namespace BDC\SimpleNews\Controller\Adminhtml\News;
    
    use BDC\SimpleNews\Controller\Adminhtml\News;
    
    class Index extends News{
        /**
         * @return void
         */
       public function execute(){
          if ($this->getRequest()->getQuery('ajax')) {
                $this->_forward('grid');
                return;
            }
            /** @var \Magento\Backend\Model\View\Result\Page $resultPage */
            $resultPage = $this->_resultPageFactory->create();
            $resultPage->setActiveMenu('BDC_SimpleNews::main_menu');
            $resultPage->getConfig()->getTitle()->prepend(__('Simple News'));
    
            return $resultPage;
       }
    }
    
    ```
    

步骤2B17:为异步创建另一个动作

  • 创建文件:app/code/BDC/SimpleNews/Controller/Adminhtml/News/Grid.php (目的:这是网格操作,用于通过 AJAX 加载网格)并将其以下代码插入其中

    源代码
    ```
    <?php
    
    namespace BDC\SimpleNews\Controller\Adminhtml\News;
    
    use BDC\SimpleNews\Controller\Adminhtml\News;
    
    class Grid extends News {
       /**
         * @return void
         */
       public function execute() {
          return $this->_resultPageFactory->create();
       }
    }
    
    ```
    

allNews

步骤2B18:为编辑表单创建布局文件 simplenews_news_edit.xml

  • 创建文件:app/code/BDC/SimpleNews/view/adminhtml/layout/simplenews_news_edit.xml (目的:此文件用于声明用于编辑页面的块)并将其以下代码插入其中

    源代码
    <?xml version="1.0"?>
    <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    layout="admin-2columns-left" xsi:noNamespaceSchemaLocation="../../../../../../../lib/internal/Magento/Framework/View/Layout/etc/page_configuration.xsd">
        <body>
            <referenceContainer name="left">
                <block class="BDC\SimpleNews\Block\Adminhtml\News\Edit\Tabs"  name="bdc_simplenews_news.edit.tabs"/>
            </referenceContainer>
            <referenceContainer name="content">
                <block class="BDC\SimpleNews\Block\Adminhtml\News\Edit"
                    name="bdc_simplenews_news.edit"/>
            </referenceContainer>
        </body>
    </page>
    

步骤2B19:为创建表单创建布局

  • 创建文件:app/code/BDC/SimpleNews/view/adminhtml/layout/simplenews_news_create.xml 并将其以下代码插入其中

    源代码
    ```
    <?xml version="1.0"?>
    
    <layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="../../../../../Magento/Core/etc/layout_single.xsd">
        <update handle="simplenews_news_edit"/>
    </layout>
    
    ```
    

步骤2B20:创建表单容器块

  • 创建文件:app/code/BDC/SimpleNews/Block/Adminhtml/News/Edit.php(目的:这是表单容器的块文件)并将其中的以下代码插入到该文件中

    源代码
    ```
    <?php
    
    namespace BDC\SimpleNews\Block\Adminhtml\News;
    
    use Magento\Backend\Block\Widget\Form\Container;
    use Magento\Backend\Block\Widget\Context;
    use Magento\Framework\Registry;
    
    class Edit extends Container
    {
     /**
       * Core registry
       *
       * @var \Magento\Framework\Registry
       */
      protected $_coreRegistry = null;
    
      /**
       * @param Context $context
       * @param Registry $registry
       * @param array $data
       */
      public function __construct(
          Context $context,
          Registry $registry,
          array $data = []
      ) {
          $this->_coreRegistry = $registry;
          parent::__construct($context, $data);
      }
    
      /**
       * Class constructor
       *
       * @return void
       */
      protected function _construct()
      {
          $this->_objectId = 'id';
          $this->_controller = 'adminhtml_news';
          $this->_blockGroup = 'BDC_SimpleNews';
    
          parent::_construct();
    
          $this->buttonList->update('save', 'label', __('Save'));
          $this->buttonList->add(
              'saveandcontinue',
              [
                  'label' => __('Save and Continue Edit'),
                  'class' => 'save',
                  'data_attribute' => [
                      'mage-init' => [
                          'button' => [
                              'event' => 'saveAndContinueEdit',
                              'target' => '#edit_form'
                          ]
                      ]
                  ]
              ],
              -100
          );
          $this->buttonList->update('delete', 'label', __('Delete'));
      }
    
      /**
       * Retrieve text for header element depending on loaded news
       *
       * @return string
       */
      public function getHeaderText()
      {
          $newsRegistry = $this->_coreRegistry->registry('simplenews_news');
          if ($newsRegistry->getId()) {
              $newsTitle = $this->escapeHtml($newsRegistry->getTitle());
              return __("Edit News '%1'", $newsTitle);
          } else {
              return __('Add News');
          }
      }
    
      /**
       * Prepare layout
       *
       * @return \Magento\Framework\View\Element\AbstractBlock
       */
      protected function _prepareLayout()
      {
          $this->_formScripts[] = "
              function toggleEditor() {
                  if (tinyMCE.getInstanceById('news_content') == null) {
                      tinyMCE.execCommand('mceAddControl', false, 'news_content');
                  } else {
                      tinyMCE.execCommand('mceRemoveControl', false, 'news_content');
                  }
              };
          ";
    
          return parent::_prepareLayout();
      }
    }
    
    ```
    

步骤2B21:创建左侧标签页的块

  • 创建文件:app/code/BDC/SimpleNews/Block/Adminhtml/News/Edit/Tabs.php(目的:此文件将声明编辑页面的左侧列中的选项卡)并将其中的以下代码插入到该文件中

    源代码
    ```
    <?php
    
    namespace BDC\SimpleNews\Block\Adminhtml\News;
    
    use Magento\Backend\Block\Widget\Form\Container;
    use Magento\Backend\Block\Widget\Context;
    use Magento\Framework\Registry;
    
    class Edit extends Container
    {
       /**
         * Core registry
         *
         * @var \Magento\Framework\Registry
         */
        protected $_coreRegistry = null;
    
        /**
         * @param Context $context
         * @param Registry $registry
         * @param array $data
         */
        public function __construct(
            Context $context,
            Registry $registry,
            array $data = []
        ) {
            $this->_coreRegistry = $registry;
            parent::__construct($context, $data);
        }
    
        /**
         * Class constructor
         *
         * @return void
         */
        protected function _construct()
        {
            $this->_objectId = 'id';
            $this->_controller = 'adminhtml_news';
            $this->_blockGroup = 'BDC_SimpleNews';
    
            parent::_construct();
    
            $this->buttonList->update('save', 'label', __('Save'));
            $this->buttonList->add(
                'saveandcontinue',
                [
                    'label' => __('Save and Continue Edit'),
                    'class' => 'save',
                    'data_attribute' => [
                        'mage-init' => [
                            'button' => [
                                'event' => 'saveAndContinueEdit',
                                'target' => '#edit_form'
                            ]
                        ]
                    ]
                ],
                -100
            );
            $this->buttonList->update('delete', 'label', __('Delete'));
        }
    
        /**
         * Retrieve text for header element depending on loaded news
         *
         * @return string
         */
        public function getHeaderText()
        {
            $newsRegistry = $this->_coreRegistry->registry('simplenews_news');
            if ($newsRegistry->getId()) {
                $newsTitle = $this->escapeHtml($newsRegistry->getTitle());
                return __("Edit News '%1'", $newsTitle);
            } else {
                return __('Add News');
            }
        }
    
        /**
         * Prepare layout
         *
         * @return \Magento\Framework\View\Element\AbstractBlock
         */
        protected function _prepareLayout()
        {
            $this->_formScripts[] = "
                function toggleEditor() {
                    if (tinyMCE.getInstanceById('news_content') == null) {
                        tinyMCE.execCommand('mceAddControl', false, 'news_content');
                    } else {
                        tinyMCE.execCommand('mceRemoveControl', false, 'news_content');
                    }
                };
            ";
    
            return parent::_prepareLayout();
        }
    }
    
    ```
    

步骤2B22:创建表单信息的块

  • 创建文件:app/code/BDC/SimpleNews/Block/Adminhtml/News/Edit/Form.php(目的:此文件将声明表单信息)并将其中的以下代码插入到该文件中

    源代码
    ```
    <?php
    
    namespace BDC\SimpleNews\Block\Adminhtml\News;
    
    use Magento\Backend\Block\Widget\Form\Container;
    use Magento\Backend\Block\Widget\Context;
    use Magento\Framework\Registry;
    
    class Edit extends Container
    {
       /**
         * Core registry
         *
         * @var \Magento\Framework\Registry
         */
        protected $_coreRegistry = null;
    
        /**
         * @param Context $context
         * @param Registry $registry
         * @param array $data
         */
        public function __construct(
            Context $context,
            Registry $registry,
            array $data = []
        ) {
            $this->_coreRegistry = $registry;
            parent::__construct($context, $data);
        }
    
        /**
         * Class constructor
         *
         * @return void
         */
        protected function _construct()
        {
            $this->_objectId = 'id';
            $this->_controller = 'adminhtml_news';
            $this->_blockGroup = 'BDC_SimpleNews';
    
            parent::_construct();
    
            $this->buttonList->update('save', 'label', __('Save'));
            $this->buttonList->add(
                'saveandcontinue',
                [
                    'label' => __('Save and Continue Edit'),
                    'class' => 'save',
                    'data_attribute' => [
                        'mage-init' => [
                            'button' => [
                                'event' => 'saveAndContinueEdit',
                                'target' => '#edit_form'
                            ]
                        ]
                    ]
                ],
                -100
            );
            $this->buttonList->update('delete', 'label', __('Delete'));
        }
    
        /**
         * Retrieve text for header element depending on loaded news
         *
         * @return string
         */
        public function getHeaderText()
        {
            $newsRegistry = $this->_coreRegistry->registry('simplenews_news');
            if ($newsRegistry->getId()) {
                $newsTitle = $this->escapeHtml($newsRegistry->getTitle());
                return __("Edit News '%1'", $newsTitle);
            } else {
                return __('Add News');
            }
        }
    
        /**
         * Prepare layout
         *
         * @return \Magento\Framework\View\Element\AbstractBlock
         */
        protected function _prepareLayout()
        {
            $this->_formScripts[] = "
                function toggleEditor() {
                    if (tinyMCE.getInstanceById('news_content') == null) {
                        tinyMCE.execCommand('mceAddControl', false, 'news_content');
                    } else {
                        tinyMCE.execCommand('mceRemoveControl', false, 'news_content');
                    }
                };
            ";
    
            return parent::_prepareLayout();
        }
    }
    
    ```
    

步骤2B23:为编辑表单声明字段创建块

  • 创建文件:app/code/BDC/SimpleNews/Block/Adminhtml/News/Edit/Tab/Info.php(目的:此文件将声明表单中的字段)并将其中的以下代码插入到该文件中

    源代码
    ```
    <?php
    
    namespace BDC\SimpleNews\Block\Adminhtml\News;
    
    use Magento\Backend\Block\Widget\Form\Container;
    use Magento\Backend\Block\Widget\Context;
    use Magento\Framework\Registry;
    
    class Edit extends Container
    {
     /**
       * Core registry
       *
       * @var \Magento\Framework\Registry
       */
      protected $_coreRegistry = null;
    
      /**
       * @param Context $context
       * @param Registry $registry
       * @param array $data
       */
      public function __construct(
          Context $context,
          Registry $registry,
          array $data = []
      ) {
          $this->_coreRegistry = $registry;
          parent::__construct($context, $data);
      }
    
      /**
       * Class constructor
       *
       * @return void
       */
      protected function _construct()
      {
          $this->_objectId = 'id';
          $this->_controller = 'adminhtml_news';
          $this->_blockGroup = 'BDC_SimpleNews';
    
          parent::_construct();
    
          $this->buttonList->update('save', 'label', __('Save'));
          $this->buttonList->add(
              'saveandcontinue',
              [
                  'label' => __('Save and Continue Edit'),
                  'class' => 'save',
                  'data_attribute' => [
                      'mage-init' => [
                          'button' => [
                              'event' => 'saveAndContinueEdit',
                              'target' => '#edit_form'
                          ]
                      ]
                  ]
              ],
              -100
          );
          $this->buttonList->update('delete', 'label', __('Delete'));
      }
    
      /**
       * Retrieve text for header element depending on loaded news
       *
       * @return string
       */
      public function getHeaderText()
      {
          $newsRegistry = $this->_coreRegistry->registry('simplenews_news');
          if ($newsRegistry->getId()) {
              $newsTitle = $this->escapeHtml($newsRegistry->getTitle());
              return __("Edit News '%1'", $newsTitle);
          } else {
              return __('Add News');
          }
      }
    
      /**
       * Prepare layout
       *
       * @return \Magento\Framework\View\Element\AbstractBlock
       */
      protected function _prepareLayout()
      {
          $this->_formScripts[] = "
              function toggleEditor() {
                  if (tinyMCE.getInstanceById('news_content') == null) {
                      tinyMCE.execCommand('mceAddControl', false, 'news_content');
                  } else {
                      tinyMCE.execCommand('mceRemoveControl', false, 'news_content');
                  }
              };
          ";
    
          return parent::_prepareLayout();
      }
    }
    
    ```
    

步骤2B24:创建创建新新闻的控制器动作

  • 创建文件:app/code/BDC/SimpleNews/Controller/Adminhtml/News/NewAction.php(目的:这是新建操作)并将其中的以下代码插入到该文件中

    源代码
    ```
    <?php
    
    namespace BDC\SimpleNews\Controller\Adminhtml\News;
    
    use BDC\SimpleNews\Controller\Adminhtml\News;
    
    class NewAction extends News
    {
       /**
         * Create new news action
         *
         * @return void
         */
       public function execute()
       {
          $this->_forward('edit');
       }
    }
    
    ```
    

步骤2B25:为编辑表单创建编辑动作

  • 创建文件:app/code/BDC/SimpleNews/Controller/Adminhtml/News/Edit.php(目的:这是编辑新闻页面的编辑操作)并将其中的以下代码插入到该文件中

    源代码
      ```
      <?php
    
      namespace BDC\SimpleNews\Controller\Adminhtml\News;
    
      use BDC\SimpleNews\Controller\Adminhtml\News;
    
      class Edit extends News
      {
         /**
           * @return void
           */
         public function execute()
         {
            $newsId = $this->getRequest()->getParam('id');
              /** @var \BDC\SimpleNews\Model\News $model */
              $model = $this->_newsFactory->create();
    
              if ($newsId) {
                  $model->load($newsId);
                  if (!$model->getId()) {
                      $this->messageManager->addError(__('This news no longer exists.'));
                      $this->_redirect('*/*/');
                      return;
                  }
              }
    
              // Restore previously entered form data from session
              $data = $this->_session->getNewsData(true);
              if (!empty($data)) {
                  $model->setData($data);
              }
              $this->_coreRegistry->register('simplenews_news', $model);
    
              /** @var \Magento\Backend\Model\View\Result\Page $resultPage */
              $resultPage = $this->_resultPageFactory->create();
              $resultPage->setActiveMenu('BDC_SimpleNews::main_menu');
              $resultPage->getConfig()->getTitle()->prepend(__('Simple News'));
    
              return $resultPage;
         }
      }
    
      ```
    

步骤2B26:为编辑表单创建保存动作

  • 创建文件:app/code/BDC/SimpleNews/Controller/Adminhtml/News/Save.php(目的:这是保存操作)并将其中的以下代码插入到该文件中

    源代码
    ```
    <?php
    
    namespace BDC\SimpleNews\Controller\Adminhtml\News;
    
    use BDC\SimpleNews\Controller\Adminhtml\News;
    
    class Edit extends News
    {
     /**
       * @return void
       */
     public function execute()
     {
        $newsId = $this->getRequest()->getParam('id');
          /** @var \BDC\SimpleNews\Model\News $model */
          $model = $this->_newsFactory->create();
    
          if ($newsId) {
              $model->load($newsId);
              if (!$model->getId()) {
                  $this->messageManager->addError(__('This news no longer exists.'));
                  $this->_redirect('*/*/');
                  return;
              }
          }
    
          // Restore previously entered form data from session
          $data = $this->_session->getNewsData(true);
          if (!empty($data)) {
              $model->setData($data);
          }
          $this->_coreRegistry->register('simplenews_news', $model);
    
          /** @var \Magento\Backend\Model\View\Result\Page $resultPage */
          $resultPage = $this->_resultPageFactory->create();
          $resultPage->setActiveMenu('BDC_SimpleNews::main_menu');
          $resultPage->getConfig()->getTitle()->prepend(__('Simple News'));
    
          return $resultPage;
     }
    }
    
    ```
    

步骤2B27:为编辑表单创建删除动作

  • 创建文件:app/code/BDC/SimpleNews/Controller/Adminhtml/News/Delete.php(目的:这是删除操作)并将其中的以下代码插入到该文件中

    源代码
    ```
    <?php
    
    namespace BDC\SimpleNews\Controller\Adminhtml\News;
    
    use BDC\SimpleNews\Controller\Adminhtml\News;
    
    class Delete extends News
    {
     /**
      * @return void
      */
     public function execute()
     {
        $newsId = (int) $this->getRequest()->getParam('id');
    
        if ($newsId) {
           /** @var $newsModel \Mageworld\SimpleNews\Model\News */
           $newsModel = $this->_newsFactory->create();
           $newsModel->load($newsId);
    
           // Check this news exists or not
           if (!$newsModel->getId()) {
              $this->messageManager->addError(__('This news no longer exists.'));
           } else {
                 try {
                    // Delete news
                    $newsModel->delete();
                    $this->messageManager->addSuccess(__('The news has been deleted.'));
    
                    // Redirect to grid page
                    $this->_redirect('*/*/');
                    return;
                 } catch (\Exception $e) {
                     $this->messageManager->addError($e->getMessage());
                     $this->_redirect('*/*/edit', ['id' => $newsModel->getId()]);
                 }
              }
        }
     }
    }
    
    ```
    

步骤2B28:为网格列表创建批量删除动作

  • 创建文件:app/code/BDC/SimpleNews/Controller/Adminhtml/News/MassDelete.php(目的:此文件用于在网格中删除多个项目)并将其中的以下代码插入到该文件中

    源代码
    ```
    <?php
    namespace BDC\SimpleNews\Controller\Adminhtml\News;
    use BDC\SimpleNews\Controller\Adminhtml\News;
    
    class MassDelete extends News {
     /**
      * @return void
      */
     public function execute()
     {
        // Get IDs of the selected news
        $newsIds = $this->getRequest()->getParam('news');
    
          foreach ($newsIds as $newsId) {
              try {
                 /** @var $newsModel \Mageworld\SimpleNews\Model\News */
                  $newsModel = $this->_newsFactory->create();
                  $newsModel->load($newsId)->delete();
              } catch (\Exception $e) {
                  $this->messageManager->addError($e->getMessage());
              }
          }
    
          if (count($newsIds)) {
              $this->messageManager->addSuccess(
                  __('A total of %1 record(s) were deleted.', count($newsIds))
              );
          }
    
          $this->_redirect('*/*/index');
     }
    }
    
    ```
    

EditNews

步骤 2B29:后端菜单和网格列表

allNews

addNews

部分C:前端新闻模块 返回顶部

步骤 2C1:为页面处理创建布局文件

  • 创建文件:app/code/BDC/SimpleNews/view/frontend/layout/news_news.xml(我们将在模块中将其用作默认布局文件)并将其中的以下代码插入到该文件中
<?xml version="1.0" encoding="UTF-8"?>

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="3columns"
xsi:noNamespaceSchemaLocation="../../../../../../../lib/internal/Magento/Framework/View/Layout/etc/page_configuration.xsd">
   <head>
      <css src="BDC_SimpleNews::css/style.css" />
   </head>
    <body>
       <referenceContainer name="sidebar.main">
         <block class="BDC\SimpleNews\Block\Lastest\Left" name="lestest.news.left"
             before="-" />
       </referenceContainer>

       <referenceContainer name="sidebar.additional">
         <block class="BDC\SimpleNews\Block\Lastest\Right" name="lestest.news.right"
             before="-" />
       </referenceContainer>
    </body>
</page>

步骤 2C2:通过更新上一个布局创建另一个布局文件

  • 创建文件:app/code/BDC/SimpleNews/view/frontend/layout/news_index_index.xml(此文件将声明在新闻列表页面中使用的块)并将其中的以下代码插入到该文件中
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="3columns" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <update handle="news_news" />
    <body>
        <referenceBlock name="content">
            <block template="BDC_SimpleNews::list.phtml" class="BDC\SimpleNews\Block\NewsList" name="bdc_simplenews_block_news_list"/>
        </referenceBlock>
    </body>
</page>

步骤2C3:创建新闻列表块

  • 创建文件:app/code/BDC/SimpleNews/Block/NewsList.php(此文件将设置新闻数据集合并声明它们的分页)并将其中的以下代码插入到该文件中
<?php

namespace BDC\SimpleNews\Block;

use Magento\Framework\View\Element\Template;
use BDC\SimpleNews\Model\NewsFactory;

class NewsList extends Template
{
   /**
    * @var \BDC\SimpleNews\Model\NewsFactory
    */
   protected $_newsFactory;

   /**
    * @param Template\Context $context
    * @param NewsFactory $newsFactory
    * @param array $data
    */
   public function __construct(
      Template\Context $context,
      NewsFactory $newsFactory,
      array $data = []
   ) {
        $this->_newsFactory = $newsFactory;
        parent::__construct($context, $data);
   }

   /**
     * Set news collection
     */
    protected  function _construct()
    {
        parent::_construct();
        $collection = $this->_newsFactory->create()->getCollection()
            ->setOrder('id', 'DESC');
        $this->setCollection($collection);
    }

   /**
     * @return $this
     */
    protected function _prepareLayout()
    {
        parent::_prepareLayout();
        /** @var \Magento\Theme\Block\Html\Pager */
        $pager = $this->getLayout()->createBlock(
           'Magento\Theme\Block\Html\Pager','simplenews.news.list.pager'
        );
        $pager->setLimit(5)
            ->setShowAmounts(false)
            ->setCollection($this->getCollection());
        $this->setChild('pager', $pager);
        $this->getCollection()->load();

        return $this;
    }

   /**
     * @return string
     */
    public function getPagerHtml()
    {
        return $this->getChildHtml('pager');
    }
}

步骤 2C4:创建前端模板文件 list.phtml

  • 创建文件:app/code/BDC/SimpleNews/view/frontend/templates/list.phtml(此文件将设置新闻数据集合并声明它们的分页)并将其中的以下代码插入到该文件中
<div class="simplenews">
   <?php
      $newsCollection = $block->getCollection();
      if ($newsCollection->getSize() > 0) :
   ?>
      <div class="toolbar top">
         <?php echo $block->getPagerHtml(); ?>
      </div>

      <ul>
         <?php foreach ($newsCollection as $news) : ?>
            <li>
               <div class="simplenews-list">
                  <a class="news-title" href="<?php echo $this->getUrl('news/index/view',
 ['id' => $news->getId()]) ?>"><?php echo $news->getTitle() ?></a>
                  <div class="simplenews-list-content">
                     <?php echo $news->getSummary() ?>
                  </div>
               </div>
            </li>
         <?php endforeach; ?>
      </ul>

      <div style="clear: both"></div>

      <div class="toolbar-bottom">
         <div class="toolbar bottom">
            <?php echo $block->getPagerHtml(); ?>
         </div>
      </div>
   <?php else : ?>
      <p><?php echo __('Have no article!') ?></p>
   <?php endif; ?>
</div>

步骤 2C5:通过扩展 Magento 核心动作类创建抽象类

  • 创建文件:app/code/BDC/SimpleNews/Controller/News.php并将其中的以下代码插入到该文件中
<div class="simplenews">
   <?php
      $newsCollection = $block->getCollection();
      if ($newsCollection->getSize() > 0) :
   ?>
      <div class="toolbar top">
         <?php echo $block->getPagerHtml(); ?>
      </div>

      <ul>
         <?php foreach ($newsCollection as $news) : ?>
            <li>
               <div class="simplenews-list">
                  <a class="news-title" href="<?php echo $this->getUrl('news/index/view',
 ['id' => $news->getId()]) ?>"><?php echo $news->getTitle() ?></a>
                  <div class="simplenews-list-content">
                     <?php echo $news->getSummary() ?>
                  </div>
               </div>
            </li>
         <?php endforeach; ?>
      </ul>

      <div style="clear: both"></div>

      <div class="toolbar-bottom">
         <div class="toolbar bottom">
            <?php echo $block->getPagerHtml(); ?>
         </div>
      </div>
   <?php else : ?>
      <p><?php echo __('Have no article!') ?></p>
   <?php endif; ?>
</div>

步骤 2C6:通过扩展抽象类 'New.php' 更新 Index 控制器

  • 更新/编辑文件:app/code/BDC/SimpleNews/Controller/Index/Index.php并将其中的以下代码插入到该文件中
<?php

namespace BDC\SimpleNews\Controller\Index;

use BDC\SimpleNews\Controller\News;

class Index extends News
{

    public function execute()
    {
        $pageFactory = $this->_pageFactory->create();

        $pageFactory->getConfig()->getTitle()->set($this->_dataHelper->getHeadTitle());

        //Add breadcrumb
        $breadcrumbs = $pageFactory->getLayout()->getBlock('breadcrumbs');
        $breadcrumbs->addCrumb('home', ['label'=>__('Home'), 'title'=>__('Home'), 'link'=>$this->_url->getUrl('')]);
        $breadcrumbs->addCrumb('simplenews', ['label'=>__('Simple News'), 'title'=>__('Simple News')]);

        return $pageFactory;
    }
}

步骤 2C7:为新闻详情页创建布局文件

  • 创建文件:app/code/BDC/SimpleNews/view/frontend/web/css/style.css并将其中的以下代码插入到该文件中
.simplenews > ul {
   list-style: none;
   padding: 0;
}
.simplenews > ul li {
   padding: 10px 5px;
   margin: 0;
   background-color: #fff;
   border-bottom: 1px #c4c1bc solid;
   display: inline-block;
   width: 100%;
}
.simplenews > ul li:last-child {
   border-bottom: none;
}
.simplenews-list {
   float: left;
   position: relative;
   margin-left: 10px;
   width: 100%;
}
.simplenews-list a.news-title {
   font-weight: bold;
}
.simplenews-list a.news-title:hover {
   text-decoration: none;
}
.block-simplenews .block-title {
   margin: 0px 0px 20px;
}
.block-simplenews-heading {
   font-size: 18px;
   font-weight: 300;
}

frontEndNews

步骤 2C8:创建新闻查看动作

  • 创建文件:app/code/BDC/SimpleNews/view/frontend/layout/news_index_view.xml并将其中的以下代码插入到该文件中
<?xml version="1.0" encoding="UTF-8"?>

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="3columns"
xsi:noNamespaceSchemaLocation="../../../../../../../lib/internal/Magento/Framework/View/Layout/etc/page_configuration.xsd">
    <update handle="news_news" />
    <body>
        <referenceContainer name="content">
            <block class="BDC\SimpleNews\Block\View" name="bdc_simplenews_news_view"
                template="BDC_SimpleNews::view.phtml" />
        </referenceContainer>
    </body>
</page>

步骤 2C9:创建查看新闻块

  • 创建文件:app/code/BDC/SimpleNews/Controller/Index/View.php并将其中的以下代码插入到该文件中
<?php

namespace BDC\SimpleNews\Controller\Index;

use BDC\SimpleNews\Controller\News;

class View extends News
{
    public function execute()
    {
	// Get news ID
        $newsId = $this->getRequest()->getParam('id');
	// Get news data
        $news = $this->_newsFactory->create()->load($newsId);
	// Save news data into the registry
        $this->_objectManager->get('Magento\Framework\Registry')
            ->register('newsData', $news);

        $pageFactory = $this->_pageFactory->create();

        // Add title
        $pageFactory->getConfig()->getTitle()->set($news->getTitle());

        // Add breadcrumb
        /** @var \Magento\Theme\Block\Html\Breadcrumbs */
        $breadcrumbs = $pageFactory->getLayout()->getBlock('breadcrumbs');
        $breadcrumbs->addCrumb('home',
            [
                'label' => __('Home'),
                'title' => __('Home'),
                'link' => $this->_url->getUrl('')
            ]
        );
        $breadcrumbs->addCrumb('simplenews',
            [
                'label' => __('Simple News'),
                'title' => __('Simple News'),
                'link' => $this->_url->getUrl('news')
            ]
        );
        $breadcrumbs->addCrumb('news',
            [
                'label' => $news->getTitle(),
                'title' => $news->getTitle()
            ]
        );

        return $pageFactory;
    }
}

步骤 2C10:创建新闻查看模板文件

  • 创建文件:app/code/BDC/SimpleNews/Block/View.php(此文件将获取新闻数据)并将其中的以下代码插入到该文件中
<?php

namespace BDC\SimpleNews\Controller\Index;

use BDC\SimpleNews\Controller\News;

class View extends News
{
    public function execute()
    {
	// Get news ID
        $newsId = $this->getRequest()->getParam('id');
	// Get news data
        $news = $this->_newsFactory->create()->load($newsId);
	// Save news data into the registry
        $this->_objectManager->get('Magento\Framework\Registry')
            ->register('newsData', $news);

        $pageFactory = $this->_pageFactory->create();

        // Add title
        $pageFactory->getConfig()->getTitle()->set($news->getTitle());

        // Add breadcrumb
        /** @var \Magento\Theme\Block\Html\Breadcrumbs */
        $breadcrumbs = $pageFactory->getLayout()->getBlock('breadcrumbs');
        $breadcrumbs->addCrumb('home',
            [
                'label' => __('Home'),
                'title' => __('Home'),
                'link' => $this->_url->getUrl('')
            ]
        );
        $breadcrumbs->addCrumb('simplenews',
            [
                'label' => __('Simple News'),
                'title' => __('Simple News'),
                'link' => $this->_url->getUrl('news')
            ]
        );
        $breadcrumbs->addCrumb('news',
            [
                'label' => $news->getTitle(),
                'title' => $news->getTitle()
            ]
        );

        return $pageFactory;
    }
}

步骤2C11:创建新闻详情前端页面视图文件

  • 创建文件:app/code/BDC/SimpleNews/view/frontend/templates/view.phtml(此文件将设置新闻数据集合并声明它们的分页)并将其中的以下代码插入到该文件中
<?php
   $news = $block->getNewsInformation();
?>
<div class="mw-simplenews">
   <?php echo $news->getDescription() ?>
</div>

FrontendNewsDetails

步骤 2C12:创建最新新闻块

  • 打开文件:app/code/BDC/SimpleNews/view/frontend/layout/news_news.xml(我们将向页面主体添加2个块)并将其中的以下代码插入到该文件中
<?xml version="1.0" encoding="UTF-8"?>

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="3columns"
xsi:noNamespaceSchemaLocation="../../../../../../../lib/internal/Magento/Framework/View/Layout/etc/page_configuration.xsd">
   <head>
      <css src="BDC_SimpleNews::css/style.css" />
   </head>
    <body>
       <referenceContainer name="sidebar.main">
         <block class="BDC\SimpleNews\Block\Lastest\Left" name="lestest.news.left"
             before="-" />
       </referenceContainer>

       <referenceContainer name="sidebar.additional">
         <block class="BDC\SimpleNews\Block\Lastest\Right" name="lestest.news.right"
             before="-" />
       </referenceContainer>
    </body>
</page>

步骤 2C13:创建定位最新新闻的块:左侧或右侧

  • 创建文件:app/code/BDC/SimpleNews/Block/Lastest.php(此文件将获取新闻数据)并将其中的以下代码插入到该文件中
<?php

namespace BDC\SimpleNews\Block;

use Magento\Framework\View\Element\Template;
use BDC\SimpleNews\Helper\Data;
use BDC\SimpleNews\Model\NewsFactory;
use BDC\SimpleNews\Model\System\Config\Status;

class Lastest extends Template
{
   /**
    * @var \BDC\SimpleNews\Helper\Data
    */
   protected $_dataHelper;

   /**
    * @var \BDC\SimpleNews\Model\NewsFactory
    */
   protected $_newsFactory;

   /**
    * @param Template\Context $context
    * @param Data $dataHelper
    * @param NewsFactory $newsFactory
    */
   public function __construct(
      Template\Context $context,
      Data $dataHelper,
      NewsFactory $newsFactory
   ) {
      $this->_dataHelper = $dataHelper;
      $this->_newsFactory = $newsFactory;
      parent::__construct($context);
   }

   /**
    * Get five latest news
    *
    * @return \BDC\SimpleNews\Model\Resource\News\Collection
    */
   public function getLatestNews()
   {
      // Get news collection
      $collection = $this->_newsFactory->create()->getCollection();
      $collection->addFieldToFilter(
         'status',
         ['eq' => Status::ENABLED]
      );
      $collection->getSelect()
         ->order('id DESC')
         ->limit(5);

      return $collection;
   }
}


步骤 2C14:创建最新新闻的模板文件

  • 创建文件:app/code/BDC/SimpleNews/Block/Lastest/Left.php(此文件将检查左侧位置并设置模板文件)并将其中的以下代码插入到该文件中
<?php

namespace BDC\SimpleNews\Block\Lastest;

use BDC\SimpleNews\Block\Lastest;
use BDC\SimpleNews\Model\System\Config\LastestNews\Position;

class Left extends Lastest
{
   public function _construct()
   {
      $position = $this->_dataHelper->getLastestNewsBlockPosition();
      // Check this position is applied or not
      if ($position == Position::LEFT) {
         $this->setTemplate('BDC_SimpleNews::lastest.phtml');
      }
   }
}

  • 创建文件:app/code/BDC/SimpleNews/Block/Lastest/Right.php(此文件将检查右侧位置并设置模板文件)并将其中的以下代码插入到该文件中
<?php

namespace BDC\SimpleNews\Block\Lastest;

use BDC\SimpleNews\Block\Lastest;
use BDC\SimpleNews\Model\System\Config\LastestNews\Position;

class Right extends Lastest
{
   public function _construct()
   {
      $position = $this->_dataHelper->getLastestNewsBlockPosition();
      // Check this position is applied or not
      if ($position == Position::RIGHT) {
         $this->setTemplate('BDC_SimpleNews::lastest.phtml');
      }
   }
}

步骤 2C15:模块的前端视图

  • 创建文件:app/code/BDC/SimpleNews/view/frontend/templates/lastest.phtml(此文件将在页面上显示5条最新新闻)并将其中的以下代码插入到该文件中
<?php
   $latestNews = $block->getLatestNews();
   if ($latestNews->getSize() > 0) :
?>
   <div class="block block-simplenews">
      <div class="block-title">
         <strong class="block-simplenews-heading"><?php echo __('Latest News') ?></strong>
      </div>

      <div class="block-content">
         <?php foreach ($latestNews as $news) : ?>
            <div>
               <span>+ </span>
               <a href="<?php echo $this->getUrl('news/index/view', ['id' => $news->getId()])
?>">
                  <span><?php echo $news->getTitle() ?></span>
               </a>
            </div>
         <?php endforeach; ?>
      </div>
   </div>
<?php endif; ?>

第D部分:新闻控制台命令 返回顶部

命令行界面有哪些特性?

  • 安装Magento(以及创建或更新数据库模式、创建部署配置等相关任务)
  • 清除缓存
  • 管理索引,包括重新索引
  • 创建翻译词典和翻译包
  • 生成插件所需的不存在的类,如工厂和拦截器,为对象管理器生成依赖注入配置
  • 部署静态视图文件
  • 从LESS创建CSS
  • 在我们开始之前,请花几分钟时间了解Magento 2 CLI中的命名。

步骤 2D1:添加新的命令依赖注入

将新命令添加到CLI是基于从XML级别传递参数到类Magento\Framework\Console\CommandList。在这里依赖注入非常有用。让我们

  • 编辑/创建 etc/di.xml

    源代码
    <?xml version="1.0"?>
    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
       <type name="Magento\Framework\Console\CommandList">
           <arguments>
               <argument name="commands" xsi:type="array">
                 <item name="bdc_simplenews_create" xsi:type="object">BDC\SimpleNews\Console\Command\NewsCreate</item>
               </argument>
           </arguments>
       </type>
    </config>
    

步骤 2D2:添加新的命令类

我们将负责执行脚本的对象添加到类Magento\Framework\Console\CommandList中。这个类的构造函数是一个简单的数组,其中类对象以类似上述示例的方式传递。

让我们进行下一步——创建我们新命令的类和负责添加新用户的帮助类

  • 创建 Console/Command/NewsCreate.php

    源代码
    <?php
    namespace BDC\SimpleNews\Console\Command;
    
    use Symfony\Component\Console\Command\Command;
    use Symfony\Component\Console\Input\InputOption;
    use Symfony\Component\Console\Input\InputInterface;
    use Symfony\Component\Console\Output\OutputInterface;
    use BDC\SimpleNews\Helper\News;
    
    class NewsCreate extends Command {
        protected $newsHelper;
    
        public function __construct(News $newsHelper)
        {
            $this->newsHelper = $newsHelper;
            parent::__construct();
        }
    
        protected function configure()
        {
            $this->setName('bdcrops:news:create')
                ->setDescription('Create New News')
                ->setDefinition($this->getOptionsList());
        }
    
        protected function execute(InputInterface $input, OutputInterface $output)
        {
            $output->writeln('<info>Creating new news...</info>');
            $this->newsHelper->setData($input);
            $this->newsHelper->execute();
    
            $output->writeln('');
            $output->writeln('<info>News created with the following data:</info>');
            $output->writeln('<comment>News ID: ' . $this->newsHelper->getNewsId());
            $output->writeln('<comment>Title: ' . $input->getOption(News::KEY_TITLE));
            $output->writeln('<comment>Summary: ' . $input->getOption(News::KEY_SUMMARY));
            $output->writeln('<comment>Description: ' . $input->getOption(News::KEY_DESC));
    
           }
    
        protected function getOptionsList(){
            return [
                new InputOption(News::KEY_TITLE, null, InputOption::VALUE_REQUIRED, '(Required) News Title'),
                new InputOption(News::KEY_SUMMARY, null, InputOption::VALUE_REQUIRED, '(Required) News Summary'),
                new InputOption(News::KEY_DESC, null, InputOption::VALUE_REQUIRED, '(Required) News Description'),
    
                ];
        }
    }
    

第二步D3:帮助类

  • 创建 Helper/News.php

    源代码
    ```
    <?php
    namespace BDC\SimpleNews\Helper;
    
    use \Magento\Framework\App\Helper\Context;
    use \Magento\Store\Model\StoreManagerInterface;
    use \Magento\Framework\App\State;
    use \BDC\SimpleNews\Model\NewsFactory;
    use \Symfony\Component\Console\Input\Input;
    use \Magento\Framework\App\Helper\AbstractHelper;
    
    class News extends AbstractHelper {
        const KEY_TITLE = 'news-title';
        const KEY_SUMMARY = 'news-summary';
        const KEY_DESC = 'news-description';
        protected $storeManager;
        protected $state;
        protected $newsFactory;
        protected $data;
        protected $newsId;
    
        public function __construct(
            Context $context,
            StoreManagerInterface $storeManager,
            State $state,
            NewsFactory $newsFactory ) {
            $this->storeManager = $storeManager;
            $this->state = $state;
            $this->newsFactory = $newsFactory;
    
            parent::__construct($context);
        }
    
        public function setData(Input $input) {
            $this->data = $input;
            return $this;
        }
    
        public function execute() {
            $this->state->setAreaCode('frontend');
            $news = $this->newsFactory->create();
            $news
                ->setTitle($this->data->getOption(self::KEY_TITLE))
                ->setSummary($this->data->getOption(self::KEY_SUMMARY))
                ->setDescription($this->data->getOption(self::KEY_DESC))
    
                ;
            $news->save();
    
            $this->newsId = $news->getId();
    
            // if($this->data->getOption(self::KEY_SENDEMAIL)) {
            //     $news->sendNewAccountEmail();
            // }
        }
    
        public function getNewsId() {
            return (int)$this->newsId;
        }
    }
    ```
    

execute()方法添加新用户。如果此阶段任何数据不正确(即密码太短),脚本将停止,控制台将显示异常。

第二步D4:结果

php bin/magento bdcrops:news:create --news-title="Matin Cli News" --news-summary="summary 1" --news-description="News Description 1"

第E部分:设置/配置自定义Cron作业 返回顶部

第二步E1:创建crontab.xml

在以下文件路径中创建crontab.xml文件,并设置时间计划以运行默认定义的自定义cron代码。

  • 创建 etc/crontab.xml

    源代码
    ```
    <?xml version="1.0"?>
    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Cron:etc/crontab.xsd">
        <group id="bdc_crongroup">
            <job name="bdcAddNews" instance="BDC\SimpleNews\Cron\AddNews" method="execute">
                <!-- <config_path>bdc/general/cron_expression</config_path> -->
                <schedule>* * * * *</schedule>
            </job>
        </group>
    </config>
    
    ```
    

在此处,在定义模块的crontab时,我们还需要定义组名。这里的group_name是cron组的名称。组名不必是唯一的,我们可以一次运行一个组的cron。

这里,

group id:是cron组名称。job name:是此cron作业的唯一ID。instance:是要实例化的类(classpath)。method:是作业实例中要调用的方法。schedule:是cron格式的计划。

* * * * * command to be executed
| | | | |
| | | | +----- Day of week (0 - 7) (Sunday=0 or 7)
| | | +------- Month (1 - 12)
| | +--------- Day of month (1 - 31)
| +----------- Hour (0 - 23)
+------------- Minute (0 - 59)

第二步E.2:定义运行类execute方法的计划

此文件包含自定义cron代码,当cron在Magento 2中运行时将执行。

  • 创建 Cron/AddNews.php

    源代码
    ```
    <?php
    namespace BDC\SimpleNews\Cron;
    
    use BDC\SimpleNews\Model\NewsFactory;
    //use BDC\SimpleNews\Model\Config;
    class AddNews {
        private $newsFactory;
        public function __construct(NewsFactory $newsFactory) {
            $this->newsFactory = $newsFactory;
        }
        public function execute(){
            $this->newsFactory->create()
                ->setTitle('Scheduled News')
                ->setSummary('Scheduled News setSummary ' . date('Ymd'))
                ->setDescription('Scheduled News setDescription ' . date('Ymd'))
                ->save();
        }
    }
    
    ```
    

步骤 2E.3:运行所有计划任务

完成以上步骤后,在您的Magento 2安装的根目录中运行以下SSH命令以运行Magento 2的cron作业

php bin/magento cache:flush
php bin/magento cron:run

要检查cron是否正常工作,请转到db

SELECT * FROM `cron_schedule`
SELECT * FROM `cron_schedule` where `job_code` LIKE "%bdc%"

第二步E.4:创建自定义cron组

通过位于://module-/etc/cron_groups.xml的cron_groups.xml文件,声明一个新的组并指定其配置选项(所有这些都运行在商店的视图范围内)

  • 创建 etc/cron_groups.xml

    源代码
    ```
    <?xml version="1.0"?>
    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:BlogTreat_CustomCron:etc/cron_groups.xsd">
        <group id="bdc_crongroup">
            <schedule_generate_every>1</schedule_generate_every>
            <schedule_ahead_for>4</schedule_ahead_for>
            <schedule_lifetime>2</schedule_lifetime>
            <history_cleanup_every>10</history_cleanup_every>
            <history_success_lifetime>60</history_success_lifetime>
            <history_failure_lifetime>600</history_failure_lifetime>
        </group>
    </config>
    
    ```
    

其中

  • group_name – 自定义组的名称。
  • schedule_generate_every – 计划写入cron_schedule表的时间频率(以分钟为单位)。
  • schedule_ahead_for – 计划写入cron_schedule表的时间(以分钟为单位)。
  • schedule_lifetime – 时间窗口(以分钟为单位),cron作业必须开始或将被认为是遗漏的(“太晚”运行)。
  • history_cleanup_every - 数据库中cron历史记录保留的时间(以分钟为单位)。
  • history_success_lifetime - 成功完成的cron作业记录在数据库中保留的时间(以分钟为单位)。
  • history_failure_lifetime - 失败的cron作业记录在数据库中保留的时间(以分钟为单位)。
  • use_separate_process - 此功能仅适用于Magento 2.1及更高版本。

您可以在管理面板中检查您的新cron组:商店 -> 配置 -> 高级 -> 系统 -> Cron(计划任务)

步骤 2E.5:运行新的计划任务组计划任务

完成上述步骤后,在您的Magento 2安装根目录中运行以下SSH命令以仅运行Magento 2特定的cron作业组。

php bin/magento cron:run --group="bdc_crongroup"

要检查cron是否正常工作,请进入数据库并运行以下查询。

SELECT * FROM `cron_schedule` where `job_code` LIKE "%bdc%"

SELECT * FROM `bdc_simplenews` ORDER BY `id` DESC

步骤2E.6:cron作业常见问题解答

cron作业是什么?在magento 2x中哪里使用?

cron作业是一个强大的功能,用于在特定的时间和日期自动执行特定任务,无需人工操作。cron作业是每天或每周重复执行动作的完美选择。magento 2使用cron作业进行:

  • 目录价格规则
  • 新闻通讯
  • 生成Google sitemaps
  • 重新索引
  • 自动更新货币汇率
  • 所有magento电子邮件(包括订单确认和交易性邮件)
  • 客户警报和通知。
  • 私人销售(仅适用于magento企业版)等。在magento 2中,我们可以轻松创建cron作业,它将列在数据库表中(表名:cron_schedule),以在计划的时间处理我们的任务。

解释<schedule>* * * * * </schedule>

计划是cron运行的时间。在这个例子中,它每分钟运行一次。

* * * * * *
| | | | | |
| | | | | +-- Year              (range: 1900-3000)
| | | | +---- Day of the Week   (range: 1-7, 1 standing for Monday)
| | | +------ Month of the Year (range: 1-12)
| | +-------- Day of the Month  (range: 1-31)
| +---------- Hour              (range: 0-23)
+------------ Minute            (range: 0-59)
            • :它是脚本自动运行的时间(每1分钟)
  • www-data:它是运行此脚本的用户
  • php /home/eden/public_html/magento2/bin/magento cron:run:命令

第F部分:创建REST WEB API 回到顶部

magento 2 API框架允许开发者为与magento 2商店通信创建新的服务。它支持REST和SOAP网络服务,并基于CRUD操作(创建、读取、更新、删除)和搜索模型。

magento 2 REST API是什么?

目前,magento 2使用以下三种认证方法,如magento 2 REST API文档中所述。

  • OAuth 1.0a认证用于第三方应用程序。
  • 令牌用于认证移动应用程序。
  • 管理员和客户使用登录凭据进行认证。

根据magento 2 API文档,这些认证方法只能访问分配给它们的资源。magento 2 API框架首先检查调用是否具有执行请求的适当授权。API框架还支持API响应的字段过滤,以节省蜂窝带宽。开发人员使用magento 2 API执行各种任务。例如,您可以创建购物应用并将其与您的magento 2商店集成。您还可以构建一个Web应用,您的员工可以使用它来帮助客户购物。借助API,您可以集成您的magento 2商店与CRM、ERP或POS系统。

如何使用magento 2 REST API?

在magento 2中使用REST API非常简单。但为了做到这一点,您需要了解调用API的流程。如果您想使用基于令牌的magento 2 REST API,首先您需要从magento 2中进行认证并获取令牌。然后,您必须将其传递到您执行的每个请求的标题中。要开始使用基于令牌认证的REST API的magento 2,您需要创建一个网络服务用户角色并将该角色注册到一个新的magento 2管理员用户。请记住,创建新角色和用户是必要的,因为在Web服务中使用magento所有者用户不是一种好做法。

为什么电子商务选择SOAP?

在电子商务网站方面,API在读取和写入服务器信息方面发挥着至关重要的作用。无论是客户的姓名还是已保存的信用卡详情,向最终用户显示的每一条信息都必须从网络服务器读取或写入。这由REST和SOAP API来处理。REST和SOAP是网络服务的模型,但其中最推荐用于电子商务网站的是SOAP。尽管REST速度快、效率高且简单,但SOAP是标准化的、安全的,非常适合支付。

如何在Magento 2中创建网络服务角色?

要在Magento 2中创建网络服务角色,请按照以下步骤操作:

  • 登录到Magento 2管理面板。
  • 转到系统 >> 用户角色,然后点击添加新角色。
  • 输入角色名称。
  • 在您的密码字段中,输入您的Magento 2管理员的当前密码。
  • 现在,在左侧,点击角色资源。
  • 在资源访问中,仅选择所需的用于您的网络服务的那些。
  • 完成后,点击保存角色。

如何在Magento 2中创建网络服务用户?

现在,通过以下步骤为新创建的角色创建一个新的用户

  • 转到系统 >> 所有用户,然后点击添加新用户。
  • 输入所需信息,包括用户名、名字、姓氏、电子邮件、密码等。
  • 现在,在左侧,点击用户角色并选择新创建的角色。
  • 完成后,点击保存用户。

如何进行Magento 2 REST API认证?

如我之前所述,我将通过令牌认证来认证REST API。这意味着我将在初始连接中传递用户名和密码,并接收令牌。此令牌将被保存在一个变量中,该变量将在后续调用中通过头部传递。

如何使用REST API在Magento 2中获取模块?

您可以使用Magento 2 REST API获取几乎所有内容。有关此主题的REST API列表是很好的指南。为了演示API,我将获取一个Magento 2商店上安装的所有模块。以下是脚本:

源代码
  ```
  <?php
  //API URL for authentication
  $apiURL="http://www.magento.lan/rest/V1/news/admin/token";
  //parameters passing with URL
  $data = array("username" => "apiaccess", "password" => "api@123");
  $data_string = json_encode($data);
  $ch = curl_init($apiURL);
  curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
  curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-Type: application/json","Content-Length: ".strlen($data_string)));
  $token = curl_exec($ch);
  //decoding generated token and saving it in a variable
  $token=  json_decode($token);
  //******************************************//
  //Using above token into header
  $headers = array("Authorization: Bearer ".$token);
  //API URL to get all Magento 2 modules
  $requestUrl='http://www.magento.lan/rest/V1/news';
  $ch = curl_init($requestUrl);
  curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  $result = curl_exec($ch);
  //decoding result
  $result=  json_decode($result);
  //printing result
  print_r($result);

  ```

步骤2F1:Web API路由/配置 – etc/webapi.xml

什么是Web API?

Web API是针对Web服务器或Web浏览器的应用程序编程接口。它是一种网络开发概念,通常限于网络应用程序的客户端(包括使用的任何Web框架),因此通常不包括Web服务器或浏览器实现细节,如SAPIs或APIs,除非它们可以通过远程Web应用程序公开访问。

什么是Magento 2 Web API框架?

支持开发人员使用与Magento系统通信的Web服务。例如,开发人员可以通过Web服务创建客户账户、产品记录。主要特点包括

  • M2同时支持REST & SOAP覆盖。
  • 3种认证类型
    • 第三方应用程序使用OAuth 1.0a进行认证。
    • 移动应用程序使用令牌进行认证。
    • 管理员和客户使用登录凭证进行认证。
  • 所有账户和集成分配资源访问API框架检查任何调用授权以执行请求。
  • 任何Magento或第三方服务都可以通过几行XML配置为Web API。要配置Web API,请在webapi.xml中定义XML元素和属性。
  • 基于CRUD &搜索模型
  • 支持Web API响应的字段过滤以节省移动带宽
  • 集成样式Web API可以通过单个Web API调用同时运行多个服务,从而实现更有效的集成。

我可以用Magento Web API做什么?

API可以用于执行广泛的任务,例如

  • 创建购物应用程序,类似于传统用户在移动设备上下载,同时员工在展厅地板上使用以帮助客户购买。
  • 与CRM/ERP后端系统集成,例如Salesforce/Xero/NetSuite。
  • 与CMS集成
  • 在店面/管理员面板中创建JavaScript小部件,通过AJAX调用访问服务。

我如何开始?

按照常规步骤在Magento管理员上注册网络服务以启用网络服务。

  • 如果使用基于令牌的认证,通过选择系统 > 权限 > 所有用户 > 添加新用户在管理员上创建网络服务用户。(如果基于会话或OAuth认证,则不需要在管理员中创建新用户。)
  • 要创建集成,请点击系统 > 扩展 > 集成 > 添加新集成**。请确保限制集成可以访问的资源。
  • 使用REST或SOAP客户端配置认证。

路由 & 配置?

路由在模块的etc/webapi.xml中定义,尽管定义xml的结构由REST API的需求指导,但SOAP API使用相同的定义。

以下显示获取CMS块的路径配置,如BDC_SimpleNews::etc/webapi.xml中定义的

<routes> <route url="/V1/news" method="GET">
        <service class="BDC\SimpleNews\Api\NewsRepositoryInterface" method="getList"/>
        <resources> <resource ref="anonymous"/> </resources>
    </route>
</routes>

实施新闻webapi.xml

  • 创建 app/code/BDC/SimpleNews/etc/webapi.xml

    源代码
    ```
    <?xml version="1.0"?>
    <routes>
        <route url="/V1/news" method="GET">
            <service class="BDC\SimpleNews\Api\NewsRepositoryInterface" method="getList"/>
            <resources> <resource ref="anonymous"/> </resources>
        </route>
    </routes>
    ```
    

在路由标签中,url属性定义了路由为/V1/cmsBlock/:blockId,其中:blockId部分代表要提供的id参数。method属性定义了路由使用的HTTP动词为‘GET’(其他可用的动词是PUT、POST和DELETE)。

在服务标签中,class属性将服务合约Magento\Cms\Api\BlockRepositoryInterface与路由关联,method属性定义了调用服务合约提供对象的调用方法。

步骤2F2:定义接口 – etc/di.xml

什么是存储库?

存储库为服务请求者提供了对实体或实体列表执行创建、读取、更新和删除(CRUD)操作的能力。存储库是服务合约的一个示例,其实现是域层的一部分。

存储库是服务合约,是接口类,有助于隐藏控制器、模型和帮助器的业务逻辑。定义存储库文件是接口类,模型文件在其中定义在存储库类中声明的声明方法。要创建模块的存储库,首先必须在路径app/code/BDC/SimpleNews/etc/di.xml的di.xml文件中定义它。

实施

<preference type="BDC\SimpleNews\Model\News" for="BDC\SimpleNews\Api\Data\NewsInterface"/>
<preference type="BDC\SimpleNews\Model\NewsRepository" for="BDC\SimpleNews\Api\NewsRepositoryInterface"/>

  • 最终文件如下所示

    源代码
    ```
    <?xml version="1.0"?>
    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
       <type name="Magento\Framework\Console\CommandList">
           <arguments>
               <argument name="commands" xsi:type="array">
                 <item name="bdc_simplenews_create" xsi:type="object">BDC\SimpleNews\Console\Command\NewsCreate</item>
               </argument>
           </arguments>
       </type>
      <preference type="BDC\SimpleNews\Model\News" for="BDC\SimpleNews\Api\Data\NewsInterface"/>
      <preference type="BDC\SimpleNews\Model\NewsRepository" for="BDC\SimpleNews\Api\NewsRepositoryInterface"/>
    
    </config>
    
    ```
    

步骤2F3:声明API接口 – Api/NewsRepositoryInterface.php

什么是存储库接口?

接口定义了特定实体的所有逻辑读取和写入操作。您可以在图中看到一个此类存储库接口的示例。接口通过一个或多个提供每个接口方法数据存储特定实现的类来实现。现在,我们需要创建一个接口和模型,请注意,您还需要注意注释。

什么是magento 2中的存储库接口?

存储库是服务合约,是接口类,有助于隐藏您的业务逻辑从控制器、模型和帮助器。

接口类型和位置有多少?

服务合约必须定义数据接口,以保持数据完整性,以及服务接口,以隐藏服务请求者的业务逻辑。

  • 数据接口:定义返回有关数据实体信息的函数,返回搜索结果,并设置验证规则并返回验证结果。您必须在模块的Api/Data子目录中为服务合约定义数据接口。

  • 服务接口:包括管理、仓库和元数据接口。您必须在模块的Api子目录中定义服务合同的服务接口。

实施

  • 创建 Api/NewsRepositoryInterface.php

    源代码
    <?php
    
    namespace BDC\SimpleNews\Api;
    
    interface NewsRepositoryInterface {
        /**
         * @return \BDC\SimpleNews\Api\Data\NewsInterface[]
         */
        public function getList();
    }
    
    

步骤2F4:数据接口 – Api/Data/NewsInterface.php

PHP接口类是什么?

接口允许无关的类实现同一组方法,无论它们在类继承层次结构中的位置如何。接口使您能够模拟多继承,因为一个类可以实现多个接口,而它只能扩展一个类。

什么是Magento 2中的数据接口?

在模块的Api/Data子目录中定义数据接口。例如,客户模块的数据接口位于/app/code/Magento/Customer/Api/Data子目录中。

现在,我们需要创建一个接口和模型,请注意,您还需要注意注释。

  • 创建 app/code/BDC/SimpleNews/Api/Data/NewsInterface.php 并将以下代码插入其中

    源代码
    ```
    <?php
    
    namespace BDC\SimpleNews\Api\Data;
    
    interface NewsInterface {
      /**
       * @return string
       */
      public function getTitle();
    
      /**
       * @return string|null
       */
      public function getSummary();
    
      /**
       * @return string|null
       */
      public function getDescription();
    }
    
    ```
    

步骤2F5:创建模型 – Model/NewsRepository.php

NewsRepository模型执行什么任务?

通过获取集合意味着在运行命令时显示您的商店中的条目。使用本主题中的代码片段,请求所需的特定数量的新闻。现在让我们开始在Magento 2中调用新闻!

实施

  • 创建 app/code/BDC/SimpleNews/Model/NewsRepository.php 并将以下代码插入其中

    源代码
    ```
    <?php
    namespace BDC\SimpleNews\Model;
    
    use BDC\SimpleNews\Api\NewsRepositoryInterface;
    use BDC\SimpleNews\Model\Resource\News\CollectionFactory;
    
    class NewsRepository implements NewsRepositoryInterface {
      private $collectionFactory;
      public function __construct(CollectionFactory $collectionFactory){
          $this->collectionFactory = $collectionFactory;
      }
      public function getList() {
          return $this->collectionFactory->create()->getItems();
      }
    }
    
    ```
    

步骤2F6:与新的 API 调用进行通信

访问路由

如上所述,配置被REST和SOAP API方便地使用。然而,访问资源的方式差异很大。完整的REST资源URL最容易确定,因为它只需要在前面加上‘http://www.yourdomain.com/rest/’’,所以在上面的例子中,假设所需的新闻有一个实体ID为1,资源URL将是‘http://www.yourdomain.com/rest/V1/news/1’’。

以访客身份测试:要测试REST,您可以去 http://{domain_name}/rest/V1/{method}/{attribute}/{value}。

示例: http://magento2.loc/rest/V1/hello/name/Matin

此示例的响应应如下所示

http://www.magento.lan/rest/V1/news

以下是测试相同API调用但使用SOAP(不实现)的简短代码

<?php
$proxy = new SoapClient('http://www.magento.lan/index.php/soap/default?wsdl&services=/V1/news');
$result = $proxy->bdcSimpleNewsV1();
var_dump($result);

SOAP的响应

object(stdClass)#2 (1) {
  ["result"]=>
  string(10) "..."
}

步骤2F7:添加 ACL Web API

如果我们没有在webapi.xml的资源中设置匿名,我们需要设置现有的Magento资源或创建自己的资源。我们可以通过向etc添加acl.xml来实现这一点。

ACL – etc/acl.xml

<resource id="Magento_Backend::admin">
 <resource id="BDC_SimpleNews::news" title="News API" translate="title" sortOrder="110" />
</resource>

在这种情况下,我们需要将BDC_SimpleNews::news添加到webapi.xml资源中而不是匿名。

G部分:依赖注入配置 回到顶部

什么是依赖注入的Magento 2?

Magento 2依赖注入用于在将工作转换为与Magento 2一起使用时替换Mage类。依赖注入设计模式创建了一个外部环境,您可以将依赖项注入到对象中。多亏了这一点,不再需要手动创建对象。也就是说,当对象A调用对象B时,这意味着B是A的依赖项

什么是依赖反转原则的Magento 2?

如果您正在使用Magento 2依赖注入,您应该看一下依赖反转原则,因为这个原则将限制高级和低级类之间的直接工作。那时,交互将通过低级类的接口作为抽象层来实现。

具体来说,di.xml文件负责将接口依赖项映射到首选实现类。可以说,由于抽象层,使用依赖反转原则,代码的依赖性将显著减少。

  • 对象管理器 - 依赖注入容器对象管理器被称为依赖注入容器,是包含并处理对象之间依赖关系的Magento 2服务类。在类构建过程中,对象管理器会根据di.xml文件中的定义注入适当的依赖。

  • 构造函数签名依赖关系在Magento 2中,类定义使用构造函数签名来获取信息(依赖的类型和数量)。

  • 编译依赖关系所有与Magento 2依赖注入相关的信息都收集在一个类中,并由代码编译器工具保存到文件中。然后,ObjectManager将获取这些信息以在应用程序中生成具体的对象。

解释Magento 2中使用的注入类型?

Magento 2依赖注入包括两种类型:构造函数注入和方法注入。您可以查看以下代码片段以了解它们。

  • 构造函数注入如上例所示,$menuItemFactory和$menu是通过构造函数注入添加到对象类的依赖。此外,请记住,构造函数注入需要声明对象的全部可选和必需依赖。

  • 方法注入关于方法注入,您会在对象的一个方法中明确声明依赖时使用它。例如,在跟踪引用实例时,$command是通过processCommand方法传递到类中的依赖。

  • 对象的组在Magento 2中,对象被分为两组:可注入对象和不可注入(可创建)对象。它们是什么?

  • 可注入对象关于可注入对象,您可以将其称为服务或对象,这些对象在其构造函数中显示依赖,并由对象管理器通过di.xml文件中的配置创建。您可以使用这些可注入对象在构造函数中请求其他可注入服务。

  • 不可注入对象不可注入(可创建)对象与可注入对象相似,因为它们也在其构造函数中公开依赖,但可创建对象允许请求其他可创建对象,如实体、值对象。此外,您不能要求可创建对象保留对可注入对象的引用。

这是关于Magento 2依赖注入设计模式的详细信息。希望您能享受其中的乐趣!

如何在Magento 2中覆盖核心代码?

默认情况下,有三种不同的方法可以覆盖核心功能。

  1. 使用偏好
  2. 使用事件\观察者
  3. 使用插件

定义偏好、参数、虚拟类型、插件、观察者?

  • 偏好:一个类覆盖另一个类,允许您指定Magento的对象管理器选择哪个类/类型。这意味着您可以选择覆盖类中的哪个方法,以及该类扩展的方法。
  • 参数
  • 虚拟类型:在不影响其他类的情况下将不同的依赖注入到现有类中的方法。
  • 插件
  • 观察者

Step2G1:DI偏好、参数&虚拟类型实现

我们在新闻项保存后写入日志。

  • 编辑Helper/News.php看起来像

    源代码
    ```
    <?php
    namespace BDC\SimpleNews\Helper;
    
    use \Magento\Framework\App\Helper\Context;
    use \Magento\Store\Model\StoreManagerInterface;
    use \Magento\Framework\App\State;
    use \BDC\SimpleNews\Model\NewsFactory;
    use \Symfony\Component\Console\Input\Input;
    use \Psr\Log\LoggerInterface;
    
    
    class News extends \Magento\Framework\App\Helper\AbstractHelper {
        const KEY_TITLE = 'news-title';
        const KEY_SUMMARY = 'news-summary';
        const KEY_DESC = 'news-description';
    
        protected $storeManager;
        protected $state;
        protected $newsFactory;
        protected $data;
        protected $newsId;
        protected $logger;
    
    
        public function __construct(
            Context $context,
            StoreManagerInterface $storeManager,
            State $state,
            NewsFactory $newsFactory,
            LoggerInterface $logger ) {
                $this->storeManager = $storeManager;
                $this->state = $state;
                $this->logger = $logger;
    
                $this->newsFactory = $newsFactory;
    
            parent::__construct($context);
        }
    
        public function setData(Input $input){
            $this->data = $input;
            return $this;
        }
    
        public function execute() {
            $this->state->setAreaCode('frontend');
            $news = $this->newsFactory->create();
            $news->setTitle($this->data->getOption(self::KEY_TITLE))
                ->setSummary($this->data->getOption(self::KEY_SUMMARY))
                ->setDescription($this->data->getOption(self::KEY_DESC));
            $news->save();
            $this->logger->debug('DI: '.$news->getTitle());
    
        }
    
        public function getNewsId(){
            return (int)$this->newsId;
        }
    }
    
    ```
    
  • 创建Helper/BdcDebug.php插入

    源代码
    ```
    <?php
    namespace BDC\SimpleNews\Helper;
    
    use Monolog\Logger;
    use Magento\Framework\Logger\Handler\Base;
    
    class BdcDebug extends Base{
        /**
         * @var string
         */
        protected $fileName = '/var/log/bdc_debug.log';
    
        /**
         * @var int
         */
        protected $loggerType = Logger::DEBUG;
    }
    
    ```
    

偏好和插件之间的区别是什么?

虽然它们都用于覆盖核心模块,但使用它们的方式完全不同。

使用偏好时,必须扩展核心类。偏好可以重写函数。当你声明一个偏好时,你的新类被期望是你要覆盖的类的完整实现。

而插件允许你在核心函数执行之前、之后或周围(之前和之后)执行你的函数。它并不像偏好那样真正地重写函数。

由于你的插件类不替换核心类,如果有很多插件钩到目标类,那么Magento 2将根据你的文件di.xml中的sortOrder参数按顺序执行它们。

步骤2G1a:优先级:覆盖所有DEBUG日志

优先级

Magento 2中的类优先级基本上与Magento 1中的重写功能相同。它表明对某个类相对于另一个类的偏好,这允许您指定由Magento的对象管理器选择的类/类型。这意味着您可以选择覆盖类中的哪些方法,以及这个类扩展的方法。

使用类优先级,我将重写

- Model class
- Block Class
- Controller Class

在app/code/BDC/SimpleNews/etc/di.xml中添加以下代码

<preference type="BDC\SimpleNews\Helper\BdcDebug" for="Magento\Framework\Logger\Handler\Debug"/>

步骤2G1b:参数优先级:覆盖特定类Monolog

参数优先级?

参数类型:对象 节点格式:{typeName}

{typeName}

  • 创建typeName类型的实例并将其作为参数传递。您可以将任何类名、接口名或虚拟类型作为typeName。
<type name="Magento\Framework\Logger\Monolog">
    <arguments>
        <argument name="handlers"  xsi:type="array">
            <item name="debug" xsi:type="object">BDC\SimpleNews\Helper\BdcDebug</item>
        </argument>
    </arguments>
</type>

步骤2G1c:虚拟类型:仅覆盖特定类在特定模块中工作

什么是虚拟类型?

在Magento 2中,类可以通过基于构造函数的依赖注入相互依赖。并且除了只允许静态依赖(类A注入类B)外,Magento还提供了一个配置系统,允许一个依赖项被另一个依赖项替换(类B被类C替换)。这些配置之一是虚拟类型。

虚拟类型定义在di.xml文件中,可能位于多个位置 - 例如,您的模块的etc/文件夹。虚拟类型本质上是在其原始类的基础上扩展的新PHP类(但实际上它们不是,它们只是链接),通过在di.xml文件中添加这些内容来覆盖原始类的构造函数参数。

什么是虚拟类型?

在Magento 2中,类可以通过基于构造函数的依赖注入相互依赖。并且除了只允许静态依赖(类A注入类B)外,Magento还提供了一个配置系统,允许一个依赖项被另一个依赖项替换(类B被类C替换)。这些配置之一是虚拟类型。

虚拟类型定义在di.xml文件中,可能位于多个位置 - 例如,您的模块的etc/文件夹。虚拟类型本质上是在其原始类的基础上扩展的新PHP类(但实际上它们不是,它们只是链接),通过在di.xml文件中添加这些内容来覆盖原始类的构造函数参数。

创建一个新的PHP子类或虚拟类型?

一旦您意识到虚拟类型不过是一个新的PHP子对象(就像有一个实际类生成它一样),您会想知道为什么您要通过XML来做这件事。也许直接在您的模块中创建一个新的PHP类并修改那里的内容会更容易?最终结果是一样的:有一个新的类型的新对象。(请注意,这个新类仍需要在其他地方被使用才能变得有用。通常这是通过使用XML Type来修改另一个类的构造函数参数,并将其新的虚拟类注入其中来完成的。)

我个人更喜欢新的PHP类而不是新的虚拟类型。然而,一旦原始类有一个长而复杂的构造函数,新的PHP类就需要在它的构造函数中复制所有父依赖项,并将它们传递给父类 - 而且可能这一切麻烦只是为了替换那些依赖项中的一个。虚拟类型更快:是的,它需要一些XML,但它允许您只突出显示您真正需要替换的那个依赖项。原始构造函数越复杂,使用虚拟类型就越好。(话虽如此,原始构造函数越复杂,就越需要对其进行清理 - 使用SOLID。)

带命名空间的虚拟类型?

现在让我们来谈谈这篇博客的主要内容:虚拟类型与对象管理器动态创建的PHP类相同。并且就像所有的PHP类一样,我们有特定的规则需要遵守,命名空间就是其中之一。那么为什么不使用命名空间呢?

让我们看看一个不带命名空间的示例

<virtualType name="bdcVirtualSomeClass" type="BDC\Example\Some\Class">
</virtualType>

现在让我们看看一个命名空间的版本

<virtualType name="BDC\Example\Some\Class\Virtual" type="BDC\Example\Some\Class">
</virtualType>

对我来说,命名空间的版本看起来要干净得多。记住,定义这个虚拟类型只是故事的一半 - 如果您不打算在其他地方使用它,它同样可以被删除。只有当它被应用到其他地方时,比如使用Type,它才有用。

<type name="Magento\Framework\Some\Existing\Class">
    <arguments>
        <argument name="someDep" xsi:type="object">BDC\Example\Some\Class\Virtual</argument>
    </arguments>
</type>

一旦其他人开始调试Magento\Framework\Some\Existing\Class类,他们可能会遇到someDep参数,而现在,多亏了命名空间,这个虚拟类型的名称可以准确地识别是谁在那里放置了这个依赖项。这就是我们为什么需要命名空间的原因。

包含虚拟的命名空间吗?

然而,如果虚拟类型实际上看起来与PHP类过于相似,这也可能变得令人困惑。我总是倾向于在我的PhpStorm环境中排除生成的/文件夹来浏览我的项目。偶尔,我会遇到一个不存在的类。如果在这个时候Magento没有崩溃,我就假设它是被生成的。一旦类中包含Factory或Proxy这个词,这就证实了我的假设。在虚拟类型的命名空间名称中包含Virtual这个词,不是更有意义吗?

这导致了以下类,这些类表明PHP类实际上是一个虚拟类型

BDC\Example\Some\Class\Virtual
BDC\Example\Virtual\Some\Class
BDC\Example\Some\ClassVirtual
BDC\Example\VirtualType\Some\Class
BDC\Example\Some\Class\VirtualType

显然,还有很多其他的变体。但请确保添加Virtual这个词。

在本模块中我们是这样实现的

<virtualType name="bdcLogger" type="Magento\Framework\Logger\Monolog">
    <arguments>
        <argument name="handlers"  xsi:type="array">
            <item name="debug" xsi:type="object">BDC\SimpleNews\Helper\BdcDebug</item>
        </argument>
    </arguments>
</virtualType>
<type name="BDC\SimpleNews\Helper\News">
     <arguments>  <argument name="logger" xsi:type="object">bdcLogger</argument> </arguments>
 </type>
  • 最后 etc/di.xml看起来像;

    源代码
    ```
    <?xml version="1.0"?>
    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
       <type name="Magento\Framework\Console\CommandList">
           <arguments>
               <argument name="commands" xsi:type="array">
                 <item name="bdc_simplenews_create" xsi:type="object">BDC\SimpleNews\Console\Command\NewsCreate</item>
               </argument>
           </arguments>
       </type>
      <preference type="BDC\SimpleNews\Model\News" for="BDC\SimpleNews\Api\Data\NewsInterface"/>
      <preference type="BDC\SimpleNews\Model\NewsRepository" for="BDC\SimpleNews\Api\NewsRepositoryInterface"/>
      <!-- <preference type="BDC\SimpleNews\Helper\BdcDebug" for="Magento\Framework\Logger\Handler\Debug"/> -->
    
      <!-- <type name="Magento\Framework\Logger\Monolog">
          <arguments>
              <argument name="handlers"  xsi:type="array">
                  <item name="debug" xsi:type="object">BDC\SimpleNews\Helper\BdcDebug</item>
              </argument>
          </arguments>
      </type> -->
    
      <virtualType name="bdcLogger" type="Magento\Framework\Logger\Monolog">
          <arguments>
              <argument name="handlers"  xsi:type="array">
                  <item name="debug" xsi:type="object">BDC\SimpleNews\Helper\BdcDebug</item>
              </argument>
          </arguments>
      </virtualType>
      <type name="BDC\SimpleNews\Helper\News">
           <arguments>  <argument name="logger" xsi:type="object">bdcLogger</argument> </arguments>
       </type>
    </config>
    ```
    
  • 运行

php bin/magento cache:flush
php bin/magento bdcrops:news:create --news-title="News preference" --news-summary="summary preference 1" --news-description="News preference Description 1"

现在检查var/log/bdc_debug.log所有日志都写在那里

Step2G2: DI事件和观察器实现

devdocs

Magento 2事件

事件是由Magento模块在特定动作触发时发出的。不仅如此,Magento还允许你创建自己的自定义事件,可以在你的代码中发出。当动作被触发时,它将数据传递给为发出的事件配置的相关观察者。可以使用Magento\Framework\Event\Manager类发出Magento 2事件,并且可以通过在构造函数中定义依赖项通过依赖注入来获取。

Magento 2观察者

观察者用于捕获由事件触发的动作。在观察者中,你可以设置响应所需的功能或逻辑。

可以通过在Module-Root/Observer目录下放置你的类文件来创建Magento 2观察者。你的观察者类应该实现以下内容;实现Magento\Framework\Event\ObserverInterface并定义其执行函数。

现在让我们开始执行!

假设你想在客户未登录时更改商店的背景颜色。

在Magento 2中,观察者是什么?

使用观察者的一种方法

由于观察者,你可以在响应特定Magento事件或自定义事件时运行你的自定义代码。你也可以选择其他选项,例如扩展和覆盖核心模块,将核心类复制到本地目录,并放在与核心目录相同的目录路径中,并直接修改核心类。然而,创建观察者是首选方法

观察者是如何工作的

观察者用于捕获事件触发之前或之后的动作。在观察者中,你可以设置响应所需的功能或逻辑。

可以通过在Module-Root/Observer目录下放置你的类文件来创建Magento 2观察者。你的观察者类应该实现以下内容:实现Magento\Framework\Event\ObserverInterface并定义其执行函数。

如何使用观察者

确保在测试之前注册了新模块,我们将在此模块上进行练习。我将使用我的模块SampleEvent。然后,我将使用观察者来自定义产品查看页面上的产品名称。

使用观察者的注意事项

  • 使你的观察者高效:你应该尽量保持观察者小而高效,避免复杂的计算。因为观察者中的复杂计算可能会减慢应用程序进程。

  • 不要包含业务逻辑:观察者不应该包含除了运行所需的逻辑之外的其他逻辑。业务逻辑应该封装在观察者所使用的其他类中。

  • 在适当的范围内声明观察者

  • 对于前端事件,在 etc/frontend/events.xml 中声明观察者,此事件仅在前端使用。您不能在后台使用此事件。

  • 对于后台事件,在 etc/adminhtml/events.xml 中声明观察者,此事件仅在后台使用。此事件不能在前端使用。

  • 仅当事件可以在前端和后台发生时才使用全局 etc/events.xml 文件。

  • 处理 Rest API 请求时,可以将 events.xml 放在 etc > webapi_rest > events.xml 中。

  • 处理 Soap API 请求时,可以将 events.xml 放在 etc > webapi_soap > events.xml 中。

  • 处理计划任务时,可以将 events.xml 放在 etc > crontab > events.xml 中。

  • 在安装或升级 Magento 或扩展时,可以将 events.xml 放在 etc > setup > events.xml 中。

一些 Magento 2 中的完美事件

  • Magento 控制器中的事件
  • controller_action_predispatch - 在每个控制器分发之前执行。

  • controller_action_newsdispatch_{full_action_name} - 在具有特定 {full_action_name} 的控制器之后执行。

  • controller_action_newsdispatch_{route_name} - 在具有特定 {route_name} 的每个控制器之后执行。

  • controller_action_newsdispatch - 在每个控制器分发之后执行。

  • 编辑 Helper/News.php

    源代码
    <?php
    namespace BDC\SimpleNews\Helper;
    
    use \Magento\Framework\App\Helper\Context;
    use \Magento\Store\Model\StoreManagerInterface;
    use \Magento\Framework\App\State;
    use \BDC\SimpleNews\Model\NewsFactory;
    use \Symfony\Component\Console\Input\Input;
    use \Psr\Log\LoggerInterface;
    use \Magento\Framework\Event\ManagerInterface;
    
    class News extends \Magento\Framework\App\Helper\AbstractHelper {
        const KEY_TITLE = 'news-title';
        const KEY_SUMMARY = 'news-summary';
        const KEY_DESC = 'news-description';
    
        protected $storeManager;
        protected $state;
        protected $newsFactory;
        protected $data;
        protected $newsId;
        protected $logger;
        protected $eventManager;
        // $eventManager
    
    
        public function __construct(
            Context $context,
            StoreManagerInterface $storeManager,
            State $state,
            NewsFactory $newsFactory,
            LoggerInterface $logger,
            ManagerInterface $eventManager) {
                $this->storeManager = $storeManager;
                $this->state = $state;
                $this->logger = $logger;
                $this->eventManager = $eventManager;
                $this->newsFactory = $newsFactory;
    
            parent::__construct($context);
        }
    
        public function setData(Input $input){
            $this->data = $input;
            return $this;
        }
    
        public function execute() {
            $this->state->setAreaCode('frontend');
            $news = $this->newsFactory->create();
            $news->setTitle($this->data->getOption(self::KEY_TITLE))
                ->setSummary($this->data->getOption(self::KEY_SUMMARY))
                ->setDescription($this->data->getOption(self::KEY_DESC));
            $news->save();
            $this->logger->debug('DI: '.$news->getTitle());
            // EventCode...
            $this->eventManager->dispatch('bdc_simplenews_save_after', ['object' => $news]);
            $this->newsId = $news->getId();
    
            // if($this->data->getOption(self::KEY_SENDEMAIL)) {
            //     $news->sendNewAccountEmail();
            // }
        }
    
        public function getNewsId(){
            return (int)$this->newsId;
        }
    }
    

抽象模型事件

通常,模型扩展 Magento\Framework\Model\AbstractModel 类。这为观察预定义的模型事件集提供了能力。并且模型应指定 AbstractModel::_ 事件前缀属性,以便观察特定模型的的事件。默认情况下,该属性的值等于 "core_abstract"。

此外,在模型中,我们有 AbstractModel::_ 事件对象属性,该属性允许为不同的模型特定事件指定当前模型实例的名称。

全局模型事件的列表

  • model_load_before - 在每个模型加载之前执行。在这里,我们可以访问以下事件的数据。

  • $observer->getField() - 获取当前处理的模型的字段名称。

  • $observer->getValue() - 获取当前处理的模型的字段值。

  • model_load_after - 在每个模型加载之后执行。

  • model_save_after - 在每个模型保存之后执行。

  • model_save_before - 在每个模型保存之前执行。

  • clean_cache_by_tags - 在模型相关缓存标签清理之后执行。

  • model_delete_before - 在模型删除之前执行。

  • model_delete_after - 在模型删除之后执行。

  • model_save_commit_after - 在模型保存事务提交之后执行。

  • model_delete_commit_after - 在模型保存事务提交删除之后执行。

在这类提到的事件中,我们可以访问以下数据

$observer->getObject()

列出模型特定事件

  • {event_prefix}_load_before – 在具有 {event_prefix} 的模型加载之前执行。

  • {event_prefix}_load_after – 在具有 {event_prefix} 的模型加载之后执行。

  • {event_prefix}_save_before – 在具有 {event_prefix} 的模型保存之前执行。

  • {event_prefix}_save_after – 在具有 {event_prefix} 的模型保存之后执行。

  • {event_prefix}_delete_before – 在具有 {event_prefix} 的模型删除之前执行。

  • {event_prefix}_delete_after – 在具有 {event_prefix} 的模型删除之后执行。

  • {event_prefix}_save_commit_after – 在具有 {event_prefix} 的模型数据提交之后执行。

  • {event_prefix}_delete_commit_after – 在具有 {event_prefix} 的模型数据提交删除之后执行。

  • {event_prefix}_clear – 在垃圾收集器准备正确删除模型对象时执行。

此外,我们可以从每个事件中获取以下事件数据

  • $observer->getDataObject() – 获取当前模型引用。

  • $observer->get{event_object} – 获取当前模型的的事件对象。

如果您想在代码中查找事件,可以这样做。

示例:您需要一个 save_before 或 save_after 事件。

  • 创建一个事件观察者以挂钩到事件 [模型前缀]_save_before。在这里我们将有观察者变量,这个变量可以获取我们需要保存数据的模型。

  • 然后我们可以使用 setData(‘column_name’,[new value]) 在保存到数据库之前调整列的数据。

那么为什么我们可以这样做呢?

什么是模型前缀:在对象模型中,我们有一个属性,这是一个受保护的 $_eventPrefix;(如果模型中没有,您可以创建它)。它是一个字符串类型。获取该值并将其与 _save_before 连接,我们将得到一个事件名。

例如:protected $_eventPrefix = 'abc'; => 事件观察者 = 'abc_save_before'。

您可以声明另一个事件

  • [前缀]_load_before

  • [前缀]_save_after

  • [前缀]_load_after

这些事件是默认的,并且始终与模型一起可用。如果您想使用自定义事件,可以使用 eventManager->dispatch(‘event_name’,$data);

  • 或者 Model/News.php,只需添加 protected $_eventPrefix = 'bdc_simplenews'; 这个 eventPrefix 被抽象模型用于自动生成事件。最后脚本如下

    源代码
    ```
    <?php
    
    // These files to insert, update, delete and get data in the database.
    
    namespace BDC\SimpleNews\Model;
    
    use Magento\Framework\Model\AbstractModel;
    
    class News extends AbstractModel{
      protected $_eventPrefix = 'bdc_simplenews';
        /**
         * News constructor.
         * @param \Magento\Framework\Model\Context $context
         * @param \Magento\Framework\Registry $registry
         * @param \Magento\Framework\Model\ResourceModel\AbstractResource|null $resource
         * @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection
         * @param array $data
         */
        public function __construct(
            \Magento\Framework\Model\Context $context,
            \Magento\Framework\Registry $registry,
            \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null,
            \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
            array $data = []
        ) {
            parent::__construct($context, $registry, $resource, $resourceCollection, $data);
        }
    
       /**
        * (non-PHPdoc)
        * @see \Magento\Framework\Model\AbstractModel::_construct()
        */
        public function _construct() {
            $this->_init('BDC\SimpleNews\Model\Resource\News');
        }
    
        /**
         * Loading news data
         *
         * @param   mixed $key
         * @param   string $field
         * @return  $this
         */
        public function load($key, $field = null) {
        	if ($field === null) {
        		$this->_getResource()->load($this, $key, 'id');
        		return $this;
        	}
        	$this->_getResource()->load($this, $key, $field);
        	return $this;
        }
    }
    ```
    
  • 创建 Observer/Logger.php

    源代码
    <?php
    
    namespace BDC\SimpleNews\Observer;
    
    use Magento\Framework\Event\Observer;
    use Magento\Framework\Event\ObserverInterface;
    use Psr\Log\LoggerInterface;
    
    class Logger implements ObserverInterface {
        private $logger;
    
        public function __construct(LoggerInterface $logger){
            $this->logger = $logger;
        }
        public function execute(Observer $observer){
            $this->logger->debug("Observer:".
                $observer->getEvent()->getObject()->getTitle()
            );
        }
    }
    
    
  • 创建 etc/events.xml

    源代码
    <?xml version="1.0"?>
    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
        <event name="bdc_simplenews_save_after">
            <observer name="bdcLogger" instance="BDC\SimpleNews\Observer\Logger" />
        </event>
    </config>
    

注意:解释 observer 节定义了观察者及其属性?

- name – the observer registration name (it is important that the names do not coincide);
- instance – the class, which method will be executed when a specific even occurs;
- method – the method being executed.
  • 添加 etc/di.xml

    源代码
    <type name="BDC\SimpleNews\Observer\Logger">
         <arguments>  <argument name="logger" xsi:type="object">bdcLogger</argument> </arguments>
     </type>
    
  • 最后 etc/di.xml 看起来如下

    源代码
    <?xml version="1.0"?>
    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
       <type name="Magento\Framework\Console\CommandList">
           <arguments>
               <argument name="commands" xsi:type="array">
                 <item name="bdc_simplenews_create" xsi:type="object">BDC\SimpleNews\Console\Command\NewsCreate</item>
               </argument>
           </arguments>
       </type>
      <preference type="BDC\SimpleNews\Model\News" for="BDC\SimpleNews\Api\Data\NewsInterface"/>
      <preference type="BDC\SimpleNews\Model\NewsRepository" for="BDC\SimpleNews\Api\NewsRepositoryInterface"/>
      <!-- <preference type="BDC\SimpleNews\Helper\BdcDebug" for="Magento\Framework\Logger\Handler\Debug"/> -->
    
      <!-- <type name="Magento\Framework\Logger\Monolog">
          <arguments>
              <argument name="handlers"  xsi:type="array">
                  <item name="debug" xsi:type="object">BDC\SimpleNews\Helper\BdcDebug</item>
              </argument>
          </arguments>
      </type> -->
    
      <virtualType name="bdcLogger" type="Magento\Framework\Logger\Monolog">
          <arguments>
              <argument name="handlers"  xsi:type="array">
                  <item name="debug" xsi:type="object">BDC\SimpleNews\Helper\BdcDebug</item>
              </argument>
          </arguments>
      </virtualType>
      <type name="BDC\SimpleNews\Helper\News">
           <arguments>  <argument name="logger" xsi:type="object">bdcLogger</argument> </arguments>
       </type>
    
       <type name="BDC\SimpleNews\Observer\Logger">
            <arguments>  <argument name="logger" xsi:type="object">bdcLogger</argument> </arguments>
        </type>
    
    </config>
    
  • 运行

php bin/magento cache:flush
php bin/magento bdcrops:news:create --news-title="News Observer" --news-summary="summary Observer 1" --news-description="News Observer Description 1"

现在检查var/log/bdc_debug.log所有日志都写在那里

步骤2G3:DI 插件(拦截器)

什么是插件?

插件是通过在方法之前、之后或周围使用代码来扩展或编辑公共方法行为的一种很好的方式。首先,请获取一个提供对观察方法类所有公共方法权限的对象。拦截是一种软件设计模式,当我们要动态插入代码而无需改变原始类的行为时使用。Magento 2 中的拦截模式通过插件实现。插件是 Magento 2 中的神奇工具。它们允许您在不重写类的情况下更改类的行为。有三种不同方式可以使用插件来更改方法行为。您可能在芝麻街中听说过它们:- 在之前 - 在之后 - 在周围

什么是 Magento 2 插件的优势?

对于像您这样的模块开发者,Magento 2 拦截插件允许

  • 转发任何用于对象管理器控制对象的方法调用,并采取程序性行动
  • 修改任何用于对象管理器控制对象的方法调用的返回值
  • 修改任何用于对象管理器控制对象的方法调用的参数。当其他模块正在以相同或可预测的方式执行同一方法时,以类似的方式进行。

什么是插件的限制?不能使用的情况?

  • 最终方法
  • 最终类
  • 非公共方法
  • 类方法(例如静态方法)
  • __construct
  • 虚拟类型
  • 在 Magento\Framework\Interception 启动之前实例化的对象

声明插件

您的模块中的 di.xml 文件声明了一个用于类对象的插件

<config>
    <type name="{ObservedType}">
      <plugin name="{pluginName}" type="{PluginClassName}" sortOrder="1" disabled="false" />
    </type>
</config>

解释声明中分配的插件元素属性?

  • type name:插件观察的类或接口。
  • plugin name:一个任意的插件名称,用于标识插件。也用于合并插件的配置。
  • 插件类型:插件类的名称或其虚拟类型。在指定此元素时,请使用以下命名约定:\Vendor\Module\Plugin。在类型元素下,我们使用插件元素定义一个或多个插件。

更多详情

  • 名称 - 使用此属性,您可以提供一个独特的、易于识别的名称值,该值针对特定插件。

  • 排序顺序 - 此属性确定在多个插件观察相同方法时的执行顺序。

  • 禁用 - 此属性的默认值设置为 false,但如果将其设置为 true,则将禁用当前插件,并且它不会被执行。

  • 类型 - 此属性指向我们将用于实现监听器之前、之后或周围的类。

假设我们正在编写针对特定方法的插件,让我们在 Customer.php 类中选择一个随机方法,getName() 方法。我们通过以下命名约定定义 getName() 方法的 before、after 和 around 监听器。

Before + getName() => beforeGetName();
After + getName()  => afterGetName();
Around + getName() => aroundGetName();

编写可选元素插件?

  • 插件排序顺序:调用相同方法的插件将按此顺序运行。
  • 插件禁用:要禁用插件,请将此元素设置为 true。默认值是 false。

Plugin 中的 3 种方法

    1. before - beforeDispatch()
    1. around - aroundDispatch()
    1. after - afterDispatch()

插件实现

  • 创建 Plugin/Logger.php

    源代码
    ```
    <?php
    namespace BDC\SimpleNews\Plugin;
    use BDC\SimpleNews\Console\Command\NewsCreate;
    use Symfony\Component\Console\Input\InputInterface;
    use Symfony\Component\Console\Output\OutputInterface;
    
    class Logger{
        /**
         * @var OutputInterface
         */
        private $output;
        public function beforeRun(
            NewsCreate $command,
            InputInterface $input,
            OutputInterface $output) {
                $output->writeln('beforeExecute');
        }
        public function aroundRun(
            NewsCreate $command,
            \Closure $proceed,
            InputInterface $input,
            OutputInterface $output) {
                $output->writeln('aroundExecute before call');
                $proceed->call($command, $input, $output);
                $output->writeln('aroundExecute after call');
                $this->output = $output;
        }
    
        //public function afterRun(NewsCreate $command){
            //$this->output->writeln('afterExecute');
        //}
    }
    ```
    
  • 添加代码 app/code/BDC/SimpleNews/etc/di.xml

<type name="BDC\SimpleNews\Console\Command\NewsCreate">
        <plugin name="bdcLoggerp" type="BDC\SimpleNews\Plugin\Logger"/>
    </type>
  • 最后 etc/di.xml 看起来如下

    源代码
    ```
    <?xml version="1.0"?>
    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
       <type name="Magento\Framework\Console\CommandList">
           <arguments>
               <argument name="commands" xsi:type="array">
                 <item name="bdc_simplenews_create" xsi:type="object">BDC\SimpleNews\Console\Command\NewsCreate</item>
               </argument>
           </arguments>
       </type>
      <preference type="BDC\SimpleNews\Model\News" for="BDC\SimpleNews\Api\Data\NewsInterface"/>
      <preference type="BDC\SimpleNews\Model\NewsRepository" for="BDC\SimpleNews\Api\NewsRepositoryInterface"/>
      <!-- <preference type="BDC\SimpleNews\Helper\BdcDebug" for="Magento\Framework\Logger\Handler\Debug"/> -->
    
      <!-- <type name="Magento\Framework\Logger\Monolog">
          <arguments>
              <argument name="handlers"  xsi:type="array">
                  <item name="debug" xsi:type="object">BDC\SimpleNews\Helper\BdcDebug</item>
              </argument>
          </arguments>
      </type> -->
    
      <virtualType name="bdcLogger" type="Magento\Framework\Logger\Monolog">
          <arguments>
              <argument name="handlers"  xsi:type="array">
                  <item name="debug" xsi:type="object">BDC\SimpleNews\Helper\BdcDebug</item>
              </argument>
          </arguments>
      </virtualType>
      <type name="BDC\SimpleNews\Helper\News">
           <arguments>  <argument name="logger" xsi:type="object">bdcLogger</argument> </arguments>
       </type>
    
       <type name="BDC\SimpleNews\Observer\Logger">
            <arguments>  <argument name="logger" xsi:type="object">bdcLogger</argument> </arguments>
        </type>
    
        <type name="BDC\SimpleNews\Console\Command\NewsCreate">
                <plugin name="bdcLoggerp" type="BDC\SimpleNews\Plugin\Logger"/>
            </type>
    
    </config>
    ```
    
  • 运行

php bin/magento cache:flush
php bin/magento bdcrops:news:create --news-title="News Plugin" --news-summary="summary Plugin 1" --news-description="News Plugin Description 1"

现在检查var/log/bdc_debug.log所有日志都写在那里

PartH: 自定义布局配置 & JavaScript 回到顶部

步骤2H1:布局配置

<block template="BDC_SimpleNews::list.phtml" class="BDC\SimpleNews\Block\NewsList" name="bdc_simplenews_block_news_list"/>

按照以下方式进行更改

<block template="BDC_SimpleNews::list.phtml" class="BDC\SimpleNews\Block\NewsList" name="bdc_simplenews_block_news_list">
  <arguments>
      <argument name="label" xsi:type="string">Head Line: </argument>
  </arguments>
</block>
<?php echo $block->getLabel(); ?>
  • 输出

步骤2H2:自定义 JavaScript

什么是 RequireJS?

RequireJS 是一个 JavaScript 模块系统。它实现了 JavaScript 模块的异步模块定义(AMD)标准。在 AMD 的术语中,一个 JavaScript 模块提供了一种运行 JavaScript 程序的方式,该程序默认不使用全局命名空间。在命名模块和程序之间共享 JavaScript 代码和数据。这只是 RequireJS 所做的。您可以使用实现某些特殊功能的功能的 RequireJS 模块,但并非 RequireJS 提供了该功能。RequireJS 是确保功能传递给您的气动管道。

解释 RequireJS 术语 "map"、"shim"、"deps" ?

  • Map:替换默认 JS 组件。要使用现有 Magento JS 组件的定制实现:将自定义组件源文件放置在以下位置之一:您的主题 JS 文件:/web/js 或 /_ /web/js 您的模块视图 JS 文件:/view/frontend/web/js 创建一个 RequireJS 配置文件 requirejs-config.js,并指定以下内容
var config = {
  "map": {
    "*": { "<default_component>": "<custom_component>" }
  }
};
  • Shim:要建立对第三方插件的依赖,请指定以下配置文件中的 [shim]:requirejs-config.js
 var config = {
     "shim": {
     "3-rd-party-plugin": ["jquery"]
     }
 };
  • Deps:当您的 require js 配置依赖于某些依赖项时使用,例如,您希望在 require js define()’d 调用之前加载一些依赖项。示例
var config = {
    "deps": [
        "jquery"
    ]
};

在此,它会在 require define()’d 调用时立即加载 [jquery]。

  • baseUrl:当我们要定义用于加载 require js 中使用的所有模块的 JS 文件的 URL 时使用。baseUrl 应用于所有以斜杠“/”开头、具有任何 URL(如“http://”)或 .js 扩展名的文件。例如
var config = {
	"baseUrl": "bdcrops/test"
};

require( ["sample/sample1", "https://code.jqueryjs.cn/jquery-3.1.1.min.js", "sample2.js"],
    function(samplemodule) {
    }
);

在此,samplemodule 被引用为 bdcrops/test/sample/sample1.js,“https://code.jqueryjs.cn/jquery-3.1.1.min.js”从指定的 URL 加载,sample2.js 从同一目录加载。

  • 路径:当您想要将基本URL路径与模块路径关联时,使用路径。路径具有与baseUrl相同的属性。如果路径以“/”或“http://”等URL开头,则它不会与基本URL关联。示例
var config = {
	"baseUrl": "bdcrops/test",
	"paths": {
        "sample": "web/js"
    },
};

require( ["sample/sample1"],
    function(samplemodule) {
    }
);

现在,samplemodule被引用为路径“bdcrops/test/web/js/sample1.js”的文件

  • 配置:当您想要将一些配置传递给所有模块时使用。这些值对所有模块都是通用的。示例
var config = {
    "map": {
        '*': {
			'sample': 'sample1.js'
		}
    }
    config: {
        "testData":{
            "color":'red'
        }
    }
};

现在,在您的js文件中,您可以通过使用:console.log(require.s.contexts._.config.testData.color)来访问此值。它将在输出中给出“red”。

什么是RequireJS“混合”?

Magento 2 RequireJS“混合”允许您通过程序方式监听任何RequireJS模块的初始实例化,并在返回之前操纵该模块。

如何使用map覆盖JS组件?

$.validator.messages = $.extend($.validator.messages, {
    required: $.mage.__('This is a required field Custome.'),
var config = {
    'map': {
        '*': {
            'mage/validation': 'BDC_SimpleNews/js/validation'
        }
    }
};
  • 清理缓存,尝试不填写字段的登录按钮

如何使用mixin覆盖JS组件?

  • app/code/BDC/SimpleNews/view/frontend/requirejs-config.js
var config = {
    'map': {
        '*': {
            'mage/validation': 'BDC_SimpleNews/js/validation'
        }
    },
    config: {
        mixins: {
            'BDC_SimpleNews/js/validation': {
                'BDC_SimpleNews/js/validation-mixin': true
            }
        }
    }
};
  • app/code/BDC/SimpleNews/view/frontend/web/js/validation-mixin.js
define(function () {
    'use strict';

    var extension = {
        isValid: function () {
            return true;
        }
    };

    return function (target) {
        return target.extend(extension);
    };
});
  • 再次检查不填写值时的登录,所有验证都消失了

第一部分:组件库/自定义UI 15% 回到顶部

如何在Magento 2后端创建一个Admin Grid?

如您所知,Magento 2 Grid是一种表格,列出了您的数据库表中的条目,并提供了一些功能,如:排序、筛选、删除、更新项目等。samplenews的示例是产品网格、客户网格。Magento 2提供了两种创建Admin Grid的方法

  • 使用布局
  • 使用组件。我们将会找到关于两者的详细信息。在我们继续之前,请按照这篇文章创建一个带有admin菜单、路由器的简单模块,我们将使用它来了解网格。

如何使用组件创建Admin Grid?

  • 在依赖注入文件中声明资源现在我们将创建di.xml文件,该文件将连接到模型以获取我们网格的数据。文件:app/code/BDC/SampleNews/etc/di.xml

  • 创建布局文件对于bdc_simplenews/news/index操作,我们将创建一个名为bdc_samplenews_news_index.xml的布局文件

  • 创建组件布局文件根据布局文件中的声明,我们将创建一个名为bdc_samplenews_news_listing.xml的组件文件

如何使用布局创建Admin Grid?

您已经找到了如何使用组件添加Magento 2 Grid的方法。现在,我们将看看如何通过正常布局/block文件来实现。

  • 为该网格创建块文件:app/code/BDC/SampleNews/Block/Adminhtml/News.php

  • 创建布局文件现在我们需要一个布局文件来连接到网格块并渲染网格。让我们创建这个文件:app/code/BDC/SampleNews/view/adminhtml/layout/bdc_samplenews_news_index.xml

  • 创建布局文件现在我们需要一个布局文件来连接到网格块并渲染网格。让我们创建这个文件:app/code/BDC/SampleNews/view/adminhtml/layout/bdc_samplenews_news_index.xml

UI组件包含child tags、argument、dataSource、columns的标签说明是什么?

-Argument: 使用(它使您的网格与数据库之间的链接)使用带有js_config标签的data_sources。我们还声明了spinner,即我们的网格中将使用的columns标签的名称。然后我们在buttons标签中声明我们的按钮,带有名称、标签、类和目标URL。

  • 数据源:dataProvider(用于在数据库中获取数据的对象)。使用“class”标签来定义要使用的对象名称。此对象将在di.xml(依赖节点文件)中稍后定义。我们通过“name”属性给我们的dataSource命名,然后我们指定用作数据库中网格id的字段("primaryFieldName")和请求中使用的字段("requestFieldName")。然后在“config”中定义要使用的组件(此处为“Magento_Ui/js/grid/provider”),以及在我们数据库中的标识符“indexField”,此处值为“pfay_contacts_id”。

  • 列:在“argument”部分的“spinner”部分中定义了上面,这里命名为listing_columns。此区域将允许我们定义我们的列,包括用于查找的标识符,用于网格的字段类型和过滤器,将使用的排序类型和标签。

书签?

书签允许您保存通过之前创建的“columns_control”元素修改的列表状态。以下是如何在“container”中集成“bookmarks”

<bookmark name="bookmarks">
    <argument name="data" xsi:type="array">
        <item name="config" xsi:type="array">
            <item name="component" xsi:type="string">Magento_Ui/js/grid/controls/bookmarks/bookmarks</item>
            <item name="displayArea" xsi:type="string">dataGridActions</item>
            <item name="storageConfig" xsi:type="array">
                <item name="saveUrl" xsi:type="url" path="*/*/save"/>
                <item name="deleteUrl" xsi:type="url" path="*/*/delete"/>
                <item name="namespace" xsi:type="string">contact_test_listing</item>
            </item>
        </item>
    </argument>
</bookmark>

分页?

在magento2中,网格分页做得非常好,并且很容易集成,只需要注意2个路径“provider”和“selectProvider”即可。以下是插入代码

 <paging name="listing_paging">
    <argument name="data" xsi:type="array">
        <item name="config" xsi:type="array">
            <item name="storageConfig" xsi:type="array">
                <!-- we put here the path to the bookmarks element -->
                <item name="provider" xsi:type="string">contacts_test_listing.contacts_test_listing.listing_top.bookmarks</item>
                <item name="namespace" xsi:type="string">current.paging</item>
            </item>
            <!-- we put here the path to the element pfay_contact_ids of contacts_test_columns  element -->
            <item name="selectProvider" xsi:type="string">contacts_test_listing.contacts_test_listing.contacts_test_columns.pfay_contacts_id</item>
            <item name="displayArea" xsi:type="string">bottom</item>
        </item>
    </argument>
</paging>

Magento2网格过滤器?

有时能够过滤表格可能很有用,为此可以在magento网格中添加一个“filter”元素。以下是操作方法

<filters name="listing_filters">
    <argument name="data" xsi:type="array">
        <item name="config" xsi:type="array">
            <item name="storageConfig" xsi:type="array">
                <item name="provider" xsi:type="string">contacts_test_listing.contacts_test_listing.listing_top.bookmarks</item>
                <item name="namespace" xsi:type="string">curren.filters</item>
            </item>
            <item name="childDefaults" xsi:type="array">
                <item name="provider" xsi:type="string">contacts_test_listing.contacts_test_listing.listing_top.listing_filters</item>
                <item name="imports" xsi:type="array">
                    <item name="visible" xsi:type="string">contacts_test_listing.contacts_test_listing.listing_top.bookmarks:current.columns.${ $.index }.visible</item>
                </item>
            </item>
        </item>
    </argument>
</filters>

默认情况下,它使用网格上所有可用的字段,它知道如何使用“filter”项中的“columns”进行过滤,如下所示

此处类型文本:text

此处类型文本范围:textRange

Magento2下的批量操作?

您想要能够选择网格中的多行,然后一次性删除它们或对所选的所有行执行其他特定处理?批量操作就是为了这个。首先,需要在网格边缘添加输入以选择行,因此在“columns”中在“column”之前添加此内容

<selectionsColumn name="ids">
    <argument name="data" xsi:type="array">
        <item name="config" xsi:type="array">
            <!-- define which field will be used as ID -->
            <item name="indexField" xsi:type="string">pfay_contacts_id</item>
        </item>
    </argument>
</selectionsColumn>

您现在看到侧边的复选框,允许您选择多行。以下是集成选择框的方法,该选择框允许在选择了我们的行后选择要执行的操作

<massaction name="listing_massaction">
  <argument name="data" xsi:type="array">
      <item name="config" xsi:type="array">
          <!-- we put here the path to the element pfay_contact_ids of contacts_test_columns  element -->
          <item name="selectProvider" xsi:type="string">contacts_test_listing.contacts_test_listing.contacts_test_columns.ids</item>
          <item name="displayArea" xsi:type="string">bottom</item>
          <item name="indexField" xsi:type="string">pfay_contacts_id</item>
      </item>
  </argument>
  <action name="delete">
      <argument name="data" xsi:type="array">
          <item name="config" xsi:type="array">
              <item name="type" xsi:type="string">delete</item>
              <item name="label" xsi:type="string" translate="true">Delete Selected</item>
              <item name="url" xsi:type="url" path="*/*/massDelete"/>
              <item name="confirm" xsi:type="array">
                  <item name="title" xsi:type="string" translate="true">Delete all selected contacts</item>
                  <item name="message" xsi:type="string" translate="true">Do you want to delete all the selected contacts?</item>
              </item>
          </item>
      </argument>
  </action>
</massaction>

这里也是一样的,我们必须注意输入的“selectProvider”路径,并按顺序添加操作。为了准备下一教程,我们将创建MassDelete控制器。当我们选择我们的操作时,我们将被重定向到这里(//massDelete)。

在magento2管理后台创建搜索字段?

要在magento管理后台创建搜索字段,您必须在容器中添加一个可选元素,该元素将被称为“filterSearch”,如下所示:

<!-- Filter Search -->
<filterSearch name="fulltext">
  <argument name="data" xsi:type="array">
      <item name="config" xsi:type="array">
          <item name="provider" xsi:type="string">contacts_test_listing.contacts_test_listing_data_source</item>
          <item name="chipsProvider" xsi:type="string">contacts_test_listing.contacts_test_listing.listing_top.listing_filters_chips</item>
          <item name="storageConfig" xsi:type="array">
              <item name="provider" xsi:type="string">contacts_test_listing.contacts_test_listing.listing_top.bookmarks</item>
              <item name="namespace" xsi:type="string">current.search</item>
          </item>
      </item>
  </argument>
</filterSearch>

为了使搜索栏正常工作,您必须更新您的表以添加索引。

步骤2I1:渲染网格(集合和列表组件配置)

  • 1.网格集合

  • 2.列表组件配置

  • 创建Controller/Adminhtml/Index/Index.php

    源代码
    <?php
    
    namespace BDC\SimpleNews\Controller\Adminhtml\Index;
    
    use Magento\Framework\Controller\ResultFactory;
    
    class Index extends \Magento\Backend\App\Action {
        public function execute() {
            return $this->resultFactory->create(ResultFactory::TYPE_PAGE);
        }
    }
    
    
  • 创建Model/Resource/News/Grid/Collection.php

    源代码
    ```
    <?php
    namespace BDC\SimpleNews\Model\Resource\News\Grid;
    
    use Magento\Framework\Data\Collection\Db\FetchStrategyInterface as FetchStrategy;
    use Magento\Framework\Data\Collection\EntityFactoryInterface as EntityFactory;
    use Magento\Framework\Event\ManagerInterface as EventManager;
    use Psr\Log\LoggerInterface as Logger;
    use  Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult;
    
    class Collection extends SearchResult {
        public function __construct(
            EntityFactory $entityFactory,
            Logger $logger,
            FetchStrategy $fetchStrategy,
            EventManager $eventManager,
            $mainTable = 'bdc_simplenews',
            $resourceModel = 'BDC\SimpleNews\Model\Resource\News' ) {
            parent::__construct(
                $entityFactory,
                $logger,
                $fetchStrategy,
                $eventManager,
                $mainTable,
                $resourceModel
            );
        }
    }
    
    ```
    
  • 编辑etc/di.xml

    源代码
    <type name="Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory">
        <arguments>
            <argument name="collections" xsi:type="array">
                <item name="bdc_news_grid_data_source" xsi:type="string">BDC\SimpleNews\Model\Resource\News\Grid\Collection</item>
            </argument>
        </arguments>
    </type>
    
  • 创建view/adminhtml/layout/simplenews_index_index.xml

    源代码
    <?xml version="1.0"?>
    <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
        <body>
            <referenceContainer name="content">
                <uiComponent name="bdc_news_grid"/>
            </referenceContainer>
        </body>
    </page>
    
    
  • 创建view/adminhtml/ui_component/bdc_news_grid.xml

    源代码
    ```
    <?xml version="1.0" encoding="UTF-8"?>
    <listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
        <argument name="data" xsi:type="array">
            <item name="js_config" xsi:type="array">
                <item name="provider" xsi:type="string">bdc_news_grid.bdc_news_grid_data_source</item>
                <item name="deps" xsi:type="string">bdc_news_grid.bdc_news_grid_data_source</item>
            </item>
            <item name="spinner" xsi:type="string">bdc_news_columns</item>
            <item name="buttons" xsi:type="array">
                <item name="add" xsi:type="array">
                    <item name="name" xsi:type="string">add</item>
                    <item name="label" xsi:type="string" translate="true">Add News</item>
                    <item name="class" xsi:type="string">primary</item>
                    <item name="url" xsi:type="string">*/news/new</item>
                </item>
            </item>
        </argument>
        <dataSource name="bdc_news_grid_data_source">
            <argument name="dataProvider" xsi:type="configurableObject">
                <argument name="class" xsi:type="string">Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider</argument>
                <argument name="name" xsi:type="string">bdc_news_grid_data_source</argument>
                <argument name="primaryFieldName" xsi:type="string">id</argument>
                <argument name="requestFieldName" xsi:type="string">id</argument>
                <argument name="data" xsi:type="array">
                    <item name="config" xsi:type="array">
                        <item name="update_url" xsi:type="url" path="mui/index/render"/>
                        <item name="component" xsi:type="string">Magento_Ui/js/grid/provider</item>
                    </item>
                </argument>
            </argument>
        </dataSource>
        <listingToolbar name="listing_top">
            <bookmark name="bookmarks"/>
            <columnsControls name="columns_controls"/>
            <exportButton name="export_button"/>
            <filterSearch name="fulltext"/>
            <filters name="listing_filters"/>
            <paging name="listing_paging"/>
            <!-- <frontendLink name="frontend_link"/> -->
        </listingToolbar>
        <columns name="bdc_news_columns">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="childDefaults" xsi:type="array">
                        <item name="fieldAction" xsi:type="array">
                            <item name="provider" xsi:type="string">bdc_news_grid.bdc_news_grid.bdc_news_columns.actions</item>
                            <item name="target" xsi:type="string">applyAction</item>
                            <item name="params" xsi:type="array">
                                <item name="0" xsi:type="string">view</item>
                                <item name="1" xsi:type="string">${ $.$data.rowIndex }</item>
                            </item>
                        </item>
                    </item>
                </item>
            </argument>
            <selectionsColumn name="ids">
                <argument name="data" xsi:type="array">
                    <item name="config" xsi:type="array">
                        <item name="indexField" xsi:type="string">id</item>
                    </item>
                </argument>
            </selectionsColumn>
            <column name="title">
                <argument name="data" xsi:type="array">
                    <item name="config" xsi:type="array">
                        <item name="filter" xsi:type="string">text</item>
                        <item name="label" xsi:type="string" translate="true">Title</item>
                    </item>
                </argument>
            </column>
            <column name="summary">
                <argument name="data" xsi:type="array">
                    <item name="config" xsi:type="array">
                        <item name="filter" xsi:type="string">text</item>
                        <item name="label" xsi:type="string" translate="true">Summary</item>
                    </item>
                </argument>
            </column>
        </columns>
    </listing>
    
    ```
    
  • 清理缓存并运行

http://www.magento.lan/cadmin/simplenews/

步骤2I2:渲染表单

目标

  • 新控制器和保存控制器

  • UI数据提供者表单

  • 表单ui组件配置

实施步骤

  • 创建 Ui/DataProvider.php

    源代码
    ```
    <?php
    
    namespace BDC\SimpleNews\Ui;
    
    use Magento\Ui\DataProvider\AbstractDataProvider;
    
    class DataProvider extends AbstractDataProvider{
        protected $collection;
        public function __construct(
            $name,
            $primaryFieldName,
            $requestFieldName,
            $collectionFactory,
            array $meta = [],
            array $data = [] ) {
            parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data);
            $this->collection = $collectionFactory->create();
        }
    
        public function getData() {
            $result = [];
            foreach ($this->collection->getItems() as $item) {
                $result[$item->getId()]['general'] = $item->getData();
            }
            return $result;
        }
    }
    
    ```
    
  • 创建Controller/Adminhtml/Index/Index.php

    源代码
    ```
    <?php
    
    namespace BDC\SimpleNews\Controller\Adminhtml\Index;
    
    use Magento\Framework\Controller\ResultFactory;
    
    class Index extends \Magento\Backend\App\Action {
        public function execute() {
            return $this->resultFactory->create(ResultFactory::TYPE_PAGE);
        }
    }
    
    ```
    
  • 创建 Controller/Adminhtml/Index/NewAction.php

    源代码
    ```
    <?php
    
    namespace BDC\SimpleNews\Controller\Adminhtml\Index;
    
    use Magento\Framework\Controller\ResultFactory;
    
    class NewAction extends \Magento\Backend\App\Action{
        public function execute() {
            return $this->resultFactory->create(ResultFactory::TYPE_PAGE);
        }
    }
    
    ```
    
  • 创建 Controller/Adminhtml/Index/Save.php

    源代码
    ```
    <?php
    
    namespace BDC\SimpleNews\Controller\Adminhtml\Index;
    
    use BDC\SimpleNews\Model\NewsFactory;
    
    class Save extends \Magento\Backend\App\Action {
        private $newsFactory;
    
        public function __construct(
            \Magento\Backend\App\Action\Context $context,
            NewsFactory $newsFactory
        ) {
            $this->newsFactory = $newsFactory;
            parent::__construct($context);
        }
    
        public function execute(){
            $this->newsFactory->create()
                ->setData($this->getRequest()->getNewsValue()['general'])->save();
            return $this->resultRedirectFactory->create()->setPath('simplenews/index/index');
        }
    }
    
    ```
    
  • 创建 view/adminhtml/layout/simplenews_index_index.xml

    源代码
    ```
    <?xml version="1.0"?>
    <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
        <body>
            <referenceContainer name="content">
                <uiComponent name="bdc_news_grid"/>
            </referenceContainer>
        </body>
    </page>
    ```
    
  • 创建 view/adminhtml/ui_component/bdc_news_grid.xml

    源代码``` bdc_news_grid.bdc_news_grid_data_source bdc_news_grid.bdc_news_grid_data_source bdc_news_columns add Add News primary */index/new Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider bdc_news_grid_data_source id id Magento_Ui/js/grid/provider bdc_news_grid.bdc_news_grid.bdc_news_columns.actions applyAction view .$data.rowIndex } id text Title text Summary
    ```
    
  • 检查管理面板,如图所示

  • 创建 view/adminhtml/layout/simplenews_index_new.xml

    源代码
    ```
    <?xml version="1.0"?>
    <page layout="admin-2columns-left" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
        <body>
            <referenceContainer name="content">
                <uiComponent name="bdc_news_form"/>
            </referenceContainer>
        </body>
    </page>
    ```
    
  • 创建 view/adminhtml/ui_component/bdc_news_form.xml

    源代码
    ```
    <?xml version="1.0" encoding="UTF-8"?>
    <form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
        <argument name="data" xsi:type="array">
            <item name="js_config" xsi:type="array">
                <item name="provider" xsi:type="string">bdc_news_form.bdc_news_form_data_source</item>
                <item name="deps" xsi:type="string">bdc_news_form.bdc_news_form_data_source</item>
            </item>
            <item name="label" xsi:type="string" translate="true">General</item>
            <item name="layout" xsi:type="array">
                <item name="type" xsi:type="string">tabs</item>
                <item name="navContainerName" xsi:type="string">left</item>
            </item>
            <item name="buttons" xsi:type="array">
                <item name="save" xsi:type="array">
                    <item name="name" xsi:type="string">save</item>
                    <item name="label" xsi:type="string" translate="true">Save</item>
                    <item name="class" xsi:type="string">primary</item>
                    <item name="url" xsi:type="string">*/*/save</item>
                </item>
            </item>
        </argument>
        <dataSource name="bdc_news_form_data_source">
            <argument name="dataProvider" xsi:type="configurableObject">
                <argument name="class" xsi:type="string">BDC\SimpleNews\Ui\DataProvider</argument>
                <argument name="name" xsi:type="string">bdc_news_form_data_source</argument>
                <argument name="primaryFieldName" xsi:type="string">id</argument>
                <argument name="requestFieldName" xsi:type="string">id</argument>
                <argument name="collectionFactory" xsi:type="object">BDC\SimpleNews\Model\Resource\News\CollectionFactory</argument>
                <argument name="data" xsi:type="array">
                    <item name="config" xsi:type="array">
                        <item name="submit_url" xsi:type="url" path="simplenews/index/save"/>
                    </item>
                </argument>
            </argument>
            <argument name="data" xsi:type="array">
                <item name="js_config" xsi:type="array">
                    <item name="component" xsi:type="string">Magento_Ui/js/form/provider</item>
                </item>
            </argument>
        </dataSource>
        <fieldset name="general">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="label" xsi:type="string" translate="true">General</item>
                </item>
            </argument>
            <field name="title">
                <argument name="data" xsi:type="array">
                    <item name="config" xsi:type="array">
                        <item name="label" xsi:type="string" translate="true">Title</item>
                        <item name="dataType" xsi:type="string">text</item>
                        <item name="formElement" xsi:type="string">input</item>
                        <item name="validation" xsi:type="array">
                            <item name="required-entry" xsi:type="boolean">true</item>
                        </item>
                    </item>
                </argument>
            </field>
            <field name="summary">
                <argument name="data" xsi:type="array">
                    <item name="config" xsi:type="array">
                        <item name="label" xsi:type="string" translate="true">Summary</item>
                        <item name="dataType" xsi:type="string">text</item>
                        <item name="formElement" xsi:type="string">input</item>
                    </item>
                </argument>
            </field>
        </fieldset>
    </form>
    
    ```
    
  • 编辑 etc/adminhtml/menu.xml 添加以下代码

<add id="BDC_SimpleNews::manage_newsui" title="Manage News UI Grid"
    module="BDC_SimpleNews" sortOrder="3" parent="BDC_SimpleNews::main_menu"
    action="simplenews" resource="BDC_SimpleNews::manage_newsui" />
  • 管理面板输出如下

步骤2I3: 扩展 UI 组件

目标

  • 扩展 XML 配置 [Magento_Sales]
  • 在网格中添加新列 [Magento_Sales]
  • 自定义现有列 [Magento_Sales]

实施

  • 复制到 app/code/BDC/SimpleNews/view/adminhtml/layout/sales_order_index.xml ==> vendor/magento/module-sales/view/adminhtml/layout/sales_order_grid.xml

  • 创建 view/adminhtml/ui_component/sales_order_grid.xml

    源代码
    ```
    <?xml version="1.0" encoding="UTF-8"?>
    <listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
        <columns name="sales_order_columns">
            <column name="created_at">
                <argument name="data" xsi:type="array">
                    <item name="config" xsi:type="array">
                        <item name="dateFormat" xsi:type="string">MMM dd, YYYY</item>
                    </item>
                </argument>
            </column>
            <!-- <column name="base_tax_amount" class="Magento\Sales\Ui\Component\Listing\Column\Price">
                <argument name="data" xsi:type="array">
                    <item name="config" xsi:type="array">
                        <item name="filter" xsi:type="string">textRange</item>
                        <item name="label" xsi:type="string" translate="true">Base Tax Amount</item>
                    </item>
                </argument>
            </column> -->
        </columns>
    </listing>
    
    ```
    
  • 编辑etc/di.xml

    源代码
    <virtualType name="Magento\Sales\Model\ResourceModel\Order\Grid">
        <arguments>
            <argument name="columns" xsi:type="array">
                <item name="base_tax_amount" xsi:type="string">sales_order.base_tax_amount</item>
            </argument>
        </arguments>
    </virtualType>
    
  • 在 sales_order_grid 表上添加新字段 'base_tax_amount' (使用 PhpMyAdmin 添加字段)

部分J: 客户/其他人 回到顶部

步骤2J1: 创建索引和重新索引

什么是索引?

Magento 将产品、类别等数据转换为提高商店性能。随着数据的变化,转换后的数据必须更新或重新索引。Magento 具有非常复杂的架构,它将大量商家数据(包括目录数据、价格、用户、商店等)存储在许多数据库表中。为了优化商店性能,Magento 使用索引器将数据累积到特殊表中。

例如,假设您将一项商品的价格从 8.99 美元更改为 6.99 美元。Magento 必须重新索引价格更改,以便在您的商店前端显示。

没有索引,Magento 必须实时计算每个产品的价格——考虑购物车价格规则、捆绑定价、折扣、层级定价等。加载产品的价格可能需要很长时间,可能导致购物车放弃。

实现

php bin/magento indexer:reindex

步骤2J2

步骤2J3

步骤2J4

步骤2J5

步骤2J6

PartK: 产品集合 回到顶部

步骤2K1:获取新产品集合

步骤2K1:获取畅销产品集合

步骤2K1:获取最多浏览产品集合

步骤2K1:获取特色产品集合

步骤2K1:获取特价产品集合

步骤2K1:获取最近浏览产品集合

步骤2K1:获取心愿单产品集合

步骤2K1:获取评分集合

步骤2K1:获取评论集合

参考 回到顶部

https://devdocs.magento.com/guides/v2.3/extension-dev-guide/declarative-schema/

https://onilab.com/blog/declarative-schema-magento-2-3-and-higherProducts

https://www.mage-world.com/blog/create-a-module-with-custom-database-table-in-magento-2.html

http://techjeffyu.com/blog/magento-2-a-full-magento-2-module

https://github.com/codingarrow/M2/tree/master/BDC/SimpleNews