Magento2.x版本已经发布一段时间了,到现在为止已经到2.2了,最新版本可以关注Magento在github上的更新。今天记录下Magento2.1如何创建一个module,直接进入正题。
安装Magento2后,进入项目的根目录后,进入/app目录,只有app/design/,app/etc/两个文件夹,design/目录是存放主题内容,安装成功后,在etc/文件夹下会多出config.php和env.php两个php文件,config.php可以看到magento2加载了那些模块,类似magento1.x的eetc/modules/文件夹,env.php保存了连接的数据库的一些信息,如果你要更改项目连接的数据库,可以在env.php里面修改,类似Magento1.x的local.xml文件。用过Magento1.x,应该发现app目录少了code目录,对头,这里我们也要新建code目录,用来存放我自己编写的模块代码,首先需要创建一个"<namespace>"(这里我用Silk),在这个<namespace>下,新建我们的"<modules>"(这里我用Test),目录结构如下所示:
备注:Magento2.1的所有核心代码位于/vendor/magento/目录下。
--magento2.1
|---code
|---Silk
|---Test
|---design
|---etc
|---config.php
|---env.php
创建好我们的<namespace>和<modules>之后,接下来就需要创建一系列的文件,我们依次来说每个文件如何创建。
模块声明
File:app/code/Silk/Test/etc/module.xml
在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="Silk_Test" setup_version="1.0.0.0" active="true" />
</config>
"module" 标签说明 :
name: 表示module的名称
setup_version: 表示module的版本号
Module registration
这里需要在module的根目录下创建一个"registration.php"文件
File:app/code/Silk/Test/registration.php
在registration.php中编写如下代码:
<?php
\Magento\Framework\Component\ComponentRegistrar::register(
\Magento\Framework\Component\ComponentRegistrar::MODULE,
'Silk_Test',
__DIR__
);
查看module是否激活
打开app/etc/config/php文件后,这时你会发现没有刚才我们编写的module,这里需要利用终端进入到magento2的根目录执行magento命令:
php bin/magento setup:upgrade
执行完成之后,你将会在config.php中看到刚才我们写的module。
接下来,我们就来创建前端控制器。
路由声明
创建前端控制器时,需要新建etc/frontend/routes.xml文件,来声明前端控制器的名称。
File:app/code/Silk/Test/etc/frontend/routes.xml
在routes.php中编写如下代码:
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
<router id="standard">
<route id="test" frontName="test">
<module name="Silk_Test" before="Magento_Core"/>
</route>
</router>
</config>
<router>标签说明:
id = standard 表示前端路由;
<route>标签说明:
id : 对于router的唯一标识名;
frontName : 前端URL的控制名称;
<router>标签说明:
name : 声明module的名称;
before : 表示在定义module之前加载;
after : 表示在定义moduel之后加载;
备注:“before” et “after” 在创建module时不是必须的.
创建前端控制器
比如我们的URL为:local.magento2.com/test,继续创建文件
File:app/code/Silk/Test/Controller/Index/Index.php
控制器中代码如下:
<?php
namespace Silk\Test\Controller\Index; //这里使用了php的命名空间
use Magento\Framework\App\Action\Action;
class Index extends Action {
/** @var \Magento\Framework\View\Result\Page */
protected $resultPageFactory;
/*** @param \Magento\Framework\App\Action\Context $context*/
public function __construct(\Magento\Framework\App\Action\Context $context,
\Magento\Framework\View\Result\PageFactory $resultPageFactory) {
$this->resultPageFactory = $resultPageFactory;
parent::__construct($context);
}
/**
* @return \Magento\Framework\View\Result\PageFactory
*/
public function execute()
{
echo "this is Silk_Test_Controller_Index_Index";
die();
}
}
现在访问刚才的路由URL,就可以看到浏览器输出内容了。
备注:这里如果报错:Please upgrade your database: Run "bin/magento setup:upgrade" from the Magento root directory.
解决办法就是到根目录执行命令
php bin/magento setup:upgrade
自定义URL
上面我们的URL很简单,为local.magento2.com/test,实际上magento将它转化成了local.magento2.com/test/index/index,test为/etc/routes.xml下我们定义的frontName名,第一个index是位于Controller文件夹下的Index文件夹名,第二个index是位于Index文件夹下的Index.php名,比如我们现在要我们的URL为:local.magento2.com/test/hello/world,我们该如何创建文件呢?
示例如下:
//File:app/code/Silk/Test/Controller/Helo/World.php
Silk
|---Test
|--Controller
|--Hello
|---World.php
World.php内容如下:
<?php
namespace Silk\Test\Controller\Hello;
use Magento\Framework\App\Action\Action;
class World extends Action {
/** @var \Magento\Framework\View\Result\Page */
protected $resultPageFactory;
/*** @param \Magento\Framework\App\Action\Context $context*/
public function __construct(\Magento\Framework\App\Action\Context $context,
\Magento\Framework\View\Result\PageFactory $resultPageFactory) {
$this->resultPageFactory = $resultPageFactory;
parent::__construct($context);
}
/**
* @return \Magento\Framework\View\Result\PageFactory
*/
public function execute()
{
echo "this is Silk_Test_Controller_Hello_World";
die();
}
}
创建Block
上面我们说了如何从控制器输出内容到页面,但是真正与浏览器页面最相关的还是由对应的Block输出数据,那么如何创建一个block呢?
我们来修改下我们控制器app/code/Silk/Test/Controller/Helo/World.php里面的内容:
namespace Silk\Test\Controller\Hello;
use Magento\Framework\App\Action\Action;
class World extends Action {
/** @var \Magento\Framework\View\Result\Page */
protected $resultPageFactory;
/*** @param \Magento\Framework\App\Action\Context $context*/
public function __construct(\Magento\Framework\App\Action\Context $context,
\Magento\Framework\View\Result\PageFactory $resultPageFactory) {
$this->resultPageFactory = $resultPageFactory;
parent::__construct($context);
}
/**
* @return \Magento\Framework\View\Result\PageFactory
*/
public function execute()
{
$this->_view->loadLayout();
$this->_view->getLayout()->initMessages();
$this->_view->renderLayout();
}
}
接着创建block文件
File:app/code/Silk/Test/Block/Hello.php
Hello.php中代码如下:
<?php
namespace Silk\Test\Block;
class Hello extends \Magento\Framework\View\Element\Template
{
public function _prepareLayout()
{
//return parent::_prepareLayout();
}
}
创建layout布局文件
布局文件的命名规则为:<Router Name>_<Controller Name>_<Action Name>
例如:
local.magento2.com/test -----test_index_index.xml
local.magento2.com/test/say -----test_say_index_index.xml
local.magento2.com/test/hello/world -----test_hello_world.xml
这里我们的布局文件名称test_hello_world.xml
File:app/code/Silk/Test/view/frontend/layout/test_hello_world.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" layout="2columns-right">
<body>
<referenceContainer name="content">
<block class="Silk\Test\Block\Hello" name="hello" template="helloworld.phtml">
</block>
</referenceContainer>
</body>
</page>
这里我们又定义了一个helloworld.phtml文件,继续来新建这个模板文件。
创建模板文件
File:app/code/Silk/Test/view/frontend/templates/helloworld.phtml
helloworld.phtml代码内容如下:
<h2>HelloWorld</h2>
<p>Congratulations ! You have created your first Magento Module !</p>
<p>The block classname is : <?php echo get_class($block) ?></p>
现在重新刷新我们的网页URL:local.magento2.com/test/hello/world
到此,我们就完成了一个简单的前端控制器到模板的数据输出.需要注意的是,block仍然是提供给模板phtml页面数据,但是在模板中调用Block中的函数方式时,Magento2是用$block,而不再是$this.
下面我们继续完善我们这module,在创建后台部分之前,先来创建Model模型.
创建Model模型
-Model
|--Job.php
|--ResourceModel
|--Job.php
|--Job
|--Collection.php
接着我们依次来创建这几个文件
File:app/code/Silk/Test/Model/Job.php
代码内容如下:
<?php
namespace Silk\Test\Model;
use \Magento\Framework\Model\AbstractModel;
class Job extends AbstractModel
{
const JOB_ID = 'entity_id'; // We define the id fieldname
/**
* Prefix of model events names
*
* @var string
*/
protected $_eventPrefix = 'test';
/**
* Name of the event object
*
* @var string
*/
protected $_eventObject = 'job';
/**
* Name of object id field
*
* @var string
*/
protected $_idFieldName = self::JOB_ID;
/**
* Initialize resource model
*
* @return void
*/
protected function _construct()
{
$this->_init('Silk\Test\Model\ResourceModel\Job');
}
}
File:app/code/Silk/Test/Model/ResourceModel/Job.php
代码内容如下:
<?php
namespace Silk\Test\Model\ResourceModel;
use \Magento\Framework\Model\ResourceModel\Db\AbstractDb;
class Job extends AbstractDb
{
/**
* Initialize resource model
*
* @return void
*/
protected function _construct()
{
// Table Name and Primary Key column
$this->_init('silk_job', 'entity_id');
}
}
File:app/code/Silk/Test/Model/ResourceModel/Job/Collection.php
代码内容如下:
<?php
namespace Silk\Test\Model\ResourceModel\Job;
use \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection;
class Collection extends AbstractCollection
{
protected $_idFieldName = \Silk\Test\Model\Job::JOB_ID;
/**
* Define resource model
*
* @return void
*/
protected function _construct()
{
$this->_init('Silk\Test\Model\Job', 'Silk\Test\Model\ResourceModel\Job');
}
}
到此,我们就创建了一个Model模型.接下来,我们来创建脚本文件,即Setup/目录下的文件
InstallSchema.php
UpgradeSchema.php
InstallData.php
UpgradeData.php
Recurring.php
备注:InstallSchema.php是模块初次运行时创建表结构的脚本,InstallData.php是模块初次运行时插入表中的数据,而UpgradeSchema.php和UpgradeData.php是根据module.xml中的setup_version版本号来执行更新表结构和表数据的脚本.
创建表结构InstallSchema.php
File:app/code/Silk/Test/Setup/InstallSchema.php
代码内容如下:
<?php
namespace Silk\Test\Setup;
use Magento\Framework\Setup\InstallSchemaInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\SchemaSetupInterface;
/**
* @codeCoverageIgnore
*/
class InstallSchema implements InstallSchemaInterface
{
/**
* {@inheritdoc}
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
public function install(SchemaSetupInterface $setup, ModuleContextInterface $context)
{
$installer = $setup;
$installer->startSetup();
$table = $installer->getConnection()->newTable(
$installer->getTable('silk_test')
)->addColumn(
'e_id',
\Magento\Framework\DB\Ddl\Table::TYPE_INTEGER,
null,
array('identity' => true, 'nullable' => false, 'primary' => true),
'Employee ID'
)->addColumn(
'e_name',
\Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
255,
array('nullable' => false),
'Employee Name'
)->addColumn(
'e_address',
\Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
'2M',
array('nullable' => false),
'Employee Address'
)->addColumn(
'is_active',
\Magento\Framework\DB\Ddl\Table::TYPE_SMALLINT,
null,
array(),
'Active Status'
)->addColumn(
'created_at',
\Magento\Framework\DB\Ddl\Table::TYPE_TIMESTAMP,
null,
array(),
'Creation Time'
)->addColumn(
'update_time',
\Magento\Framework\DB\Ddl\Table::TYPE_TIMESTAMP,
null,
array(),
'Modification Time'
)->setComment(
'Employee Table'
);
$installer->getConnection()->createTable($table);
$installer->endSetup();
}
}
upgrade表结构UpgradeSchema.php
File:app/code/Silk/Test/Setup/UpgradeSchema.php
代码内容如下:
<?php
namespace Silk\Test\Setup;
use Magento\Framework\Setup\UpgradeSchemaInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\SchemaSetupInterface;
use Magento\Framework\DB\Ddl\Table;
/**
* @codeCoverageIgnore
*/
class UpgradeSchema implements UpgradeSchemaInterface
{
public function upgrade(SchemaSetupInterface $setup, ModuleContextInterface $context)
{
$installer = $setup;
$installer->startSetup();
if (version_compare($context->getVersion(), '1.0.1') < 0)
{
$installer->startSetup();
$tableName = $setup->getTable('silk_test');
if ($setup->getConnection()->isTableExists($tableName) == true)
{
$installer->getConnection()
->addColumn($installer->getTable('silk_test'),'e_profile_picture', array(
'type' => Table::TYPE_TEXT,
'nullable' => true,
'length' => 255,
'comment' => 'Profile Picture'
));
}
}
$installer->endSetup();
}
}
备注: 更新表结构要查看module.xml中的schema_version标签的值是否小于这里的版本号.
创建后台配置文件
后台的配置文件是在etc/adminhtml/目录下创建,比如menu.xml,routes.xml,system.xml,di.xml等等,依次来解释下这些xml的作用
menu.xml
menu.xml可以看作是配置当前模块在后台的导航位置的配置信息
File:app/code/Silk/Test/etc/adminhtml/menu.xml
代码内容如下:
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Backend:etc/menu.xsd">
<menu>
<add id="Silk_Test::job_head" title="Test" module="Silk_Test" sortOrder="100" parent="Magento_Backend::stores" resource="Silk_Test::job_head" />
<add id="Silk_Test::job" title="Test" module="Silk_Test" sortOrder="20" parent="Silk_Test::job_head" action="test/job" resource="Silk_Test::job" />
</menu>
</config>
说明:
parent: 'Magento_Backend::stores"表示当前模块位于后台Stores选项中;
title : 表示在后台显示的名称;
id : 唯一标识符;
action: 表示点击该选项要跳转的路由;
acl.xml
File:app/code/Silk/Test/etc/acl.xml
代码内容如下:
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Acl/etc/acl.xsd">
<acl>
<resources>
<resource id="Magento_Backend::admin">
<!--<resource id="Magento_Backend::content">-->
<resource id="Silk_Test::job_head" title="Test" sortOrder="100" >
<resource id="Silk_Test::department" title="Departments" sortOrder="10">
<resource id="Silk_Test::department_save" title="Save Department" sortOrder="10" />
<resource id="Silk_Test::department_delete" title="Delete Department" sortOrder="20" />
</resource>
<resource id="Silk_Test::job" title="Test" sortOrder="20">
<resource id="Silk_Test::job_save" title="Save Job" sortOrder="10" />
<resource id="Silk_Test::job_delete" title="Delete Job" sortOrder="20" />
</resource>
</resource>
<!--</resource>-->
<!-- Admin config -->
<resource id="Magento_Backend::stores">
<resource id="Magento_Backend::stores_settings">
<resource id="Magento_Config::config">
<resource id="Silk_Test::test" title="Test Section" />
</resource>
</resource>
</resource>
</resource>
</resources>
</acl>
</config>
查看权限位置:System > Permissions > User roles;
routes.xml
routes.xml是配置后台路由的信息
File:app/code/Silk/Test/etc/adminhtml/routes.xml
代码内容如下:
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
<router id="admin">
<route id="test" frontName="test">
<module name="Silk_Test" before="Magento_Backend" />
</route>
</router>
</config>
说明:
frontName: 表示你后台控制器的名称
id: 唯一标识符;
system.xml
system.xml是配置后台路由的信息
File:app/code/Silk/Test/etc/adminhtml/system.xml
代码内容如下:
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
<system>
<tab id="test" translate="label" sortOrder="1000">
<label>Test</label>
</tab>
<section id="test" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Test</label>
<tab>test</tab>
<resource>Silk_Test::test</resource>
<group id="department" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Test configuration</label>
<field id="view_list" translate="label comment" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Show job list</label>
<comment>Show list of the viewing </comment>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
</field>
</group>
</section>
</system>
</config>