Magento1.x エクステンション入門(2)

このエントリは[Magento Advent Calendar](http://www.adventar.org/calendars/931)の14日目です。

# 概要
[前回の記事](http://qiita.com/iskou9821/items/b0a3ffcb2b8efb87a879)で、Magentoにエクステンションとして認識させる段階まで作業を行いました。

今回はそれに引き続き、Magento上に独自のルーティングを定義し、新たな画面を作成する方法について書いていきます。

前提として、前回の記事で作成したプラグインの内容を引き継ぎ、エクステンション名だけ「Secondext」と変えたものが手元にあるものとしています。

# 画面の作成の大まかな手順
画面作成の大まかな手順は以下のとおりです。
今回は、「一番単純なやり方」と「一般的なやり方」の2つに分けてご紹介します。
(Magentoはカスタマイズ性が高い分、一つの目的を達成するにも利用可能な定義ファイルが多く、少々説明がしにくいと感じたためです。。。)

## 単純なやり方
 1. 「(エクステンションのフォルダ)/etc/config.xml」に、ルーティングの情報を定義する。
 1. 「(エクステンションのフォルダ)/controllers/IndexController.php」というファイルを作成し、テンプレートを指定して画面をレンダリングするコードを書く(ここでは、テンプレート名を「secondext/index.phtml」とする)。
 2. 「app/design/frontend/base/default/template/secondext/index.phtml」を作成し、そこにPHPのテンプレートを書く。

## 通常のやり方
1. 「(エクステンションのフォルダ)/etc/config.xml」にブロックの定義、レイアウト更新の定義ファイルの場所(ここでは、second_ext.xmlとする)、ルーティングの情報を定義する。
2. 「(エクステンションのフォルダ)/Block/ExtForm.php」というファイル(＊ファイル名は何でも良いが、ここでは便宜的にそうする)を作成し、ビューに対応するクラスを作成する。
3. 「app/desing/frontend/base/default/layout/second_ext.xml」というファイルを作成し、特定のルーティング(url)が開かれた時に利用するブロック、及び、そのブロックで利用するデザイン情報を定義する。利用するテンプレートの情報もこちらに記述する(secondext/ext_form.phtmlとする)。
4. 「app/design/frontend/base/default/template/secondext/ext_form.phtml」を作成し、そこにPHPのテンプレートを書く。

なおControllerは特定のURLが開かれた時に実行されるメソッドに関する処理を、Block
はビューの描画に関する処理を担当するクラスとなります。

# 「単純なやり方」の詳細
作業は以下の3点です。

 * 前回作成したconfig.xmlを編集する。
 * controllers/IndexController.phpを作成する。
 * テンプレートとなるphtmlファイルを作成する。

```xml:app/code/community/Advent2015/Secondext/etc/config.xml

<?xml version="1.0"?>
<config>
    <modules>
        <Advent2015_Secondext>
            <version>0.0.1</version>
        </Advent2015_Secondext>
    </modules>

    <frontend>
        <routers>
            <secondext>
                <use>standard</use>
                <args>
                    <module>Advent2015_Secondext</module>
                    <!-- 「ext-test」というURLを割り当てる -->
                    <frontName>ext-test</frontName>
                </args>
            </secondext>
        </routers>
    </frontend>
</config>


```


```php:app/code/community/Advent2015/Secondext/controllers/IndexController.php

<?php
class Advent2015_Secondext_IndexController extends Mage_Core_Controller_Front_Action {
    
    public function indexAction() {
        $this->loadLayout();

        $block = $this->getLayout()->createBlock(
            'Mage_Core_Block_Template',
            'secondext',
            array('template' => 'secondext/index.phtml') //画面に表示するテンプレートファイルを指定。
        );

        $this->getLayout()->getBlock('root')->setTemplate('page/3columns.phtml'); //画面のレイアウトを指定
        $this->getLayout()->getBlock('content')->append($block);
        $this->getLayout()->getBlock('head')->setTitle('タイトル');
        $this->_initLayoutMessages('core/session');
        $this->renderLayout();
    }

}

```

```php:app/design/frontend/base/default/template/secondext/index.phtml

<!-- この内容は適当 -->
<div>テストです</div>
<div>テストです</div>
<div>テストです</div>

```

ここまで作成してアップロードし、ブラウザで「(Magentoのインストール先)/ext-test」にアクセスしてください。

おそらく、以下のような画面になるはずです。

<img width="300" alt="スクリーンショット 2015-12-14 13.08.18.png" src="https://qiita-image-store.s3.amazonaws.com/0/50449/e8a01f0d-baef-d2c1-62ec-248e995d228c.png">

簡単ですが、こちらが「単純なやり方」となります。

＊サンプルコードについて、記事の一番下にリンクを置いていますので、そちらも参考にして下さい。

phtmlファイルを編集するだけでなく、IndexController.phpで指定するレイアウトを「page/3columns.phtml」から「page/1column.phtml」に変えたり、titleの部分を変更したりしても、画面表示が変わりますので、そちらも試してみて頂ければと思います。

なおcssやjsを追加する場合、そのやり方にも作法があるのですが「デザインテーマ」という別の領域に入ってきてしまいますので、今回は解説しません。

## 「通常のやり方」の詳細
作業は以下の4点です。
先ほどの「簡単なやり方」が完了した状態で、さらに追記していくものとします。

 * config.xmlを編集する。
 * controllers/IndexController.phpを編集する。
 * ブロックに対応するphpファイルを作成する。
 * レイアウトを更新するためのxmlファイルを作成する。
 * テンプレートとなるphtmlファイルを作成する。

```xml:app/code/community/Advent2015/Secondext/etc/config.xml

<?xml version="1.0"?>
<config>
    <modules>
        <Advent2015_Secondext>
            <version>0.0.1</version>
        </Advent2015_Secondext>
    </modules>
    <global>
        <!-- 「一般的なやり方」で必要な部分↓ -->
        <blocks>
            <Advent2015_Secondext>
                <class>Advent2015_Secondext_Block</class>
            </Advent2015_Secondext>
        </blocks>
        <!-- 「一般的なやり方」で必要な部分↑ -->

        <helpers>
            <Advent2015_Secondext>
                <class>Advent2015_Secondext_Helper</class>
            </Advent2015_Secondext>
        </helpers>
    </global>

    <frontend>
        <!-- 「一般的なやり方」で必要な部分↓ -->
        <layout>
            <updates>
                <secondext>
                    <file>second_ext.xml</file>
                </secondext>
            </updates>
        </layout>
        <!-- 「一般的なやり方」で必要な部分↑ -->

        <routers>
            <!-- ↓「簡単なやり方」と「一般的なやり方」で必要な部分↓ -->
            <secondext>
                <use>standard</use>
                <args>
                    <module>Advent2015_Secondext</module>
                    <frontName>ext-test</frontName>
                </args>
            </secondext>
            <!-- ↑「簡単なやり方」と「一般的なやり方」で必要な部分↑ -->
        </routers>
    </frontend>
</config>


```

```php:app/code/community/Advent2015/Secondext/controllers/IndexController.php

<?php
class Advent2015_Secondext_IndexController extends Mage_Core_Controller_Front_Action {
    
	/*
	「/ext-test」もしくは「/ext-test/index」にアクセスした場合に呼ばれる。
	　「簡単なやり方」の画面を表示するルーティング
	*/
    public function indexAction() {
        $this->loadLayout();

        $block = $this->getLayout()->createBlock(
            'Mage_Core_Block_Template',
            'secondext',
            array('template' => 'secondext/index.phtml')
        );

        $this->getLayout()->getBlock('root')->setTemplate('page/3columns.phtml'); //画面のレイアウトを指定
        $this->getLayout()->getBlock('content')->append($block);
        $this->getLayout()->getBlock('head')->setTitle('タイトル');
        $this->_initLayoutMessages('core/session');
        $this->renderLayout();
    }

    /*
    「/ext-test/index/form」にアクセスした場合に呼ばれる。
    　「一般的なやり方」の画面を表示するルーティング
    */
    public function formAction() {
        $this->loadLayout();
        $this->renderLayout();
    }

}

```

```php:app/code/community/Advent2015/Secondext/Block/ExtForm.php

<?php
class Advent2015_Secondext_Block_ExtForm extends Mage_Core_Block_Template {

	//動作確認のために作成した、適当なメソッド
	public function hoge() {
		return 'hoge';
	}

}

```

```php:app/design/frontend/base/default/layout/second_ext.xml

<?xml version="1.0"?>
<layout version="0.0.1">
    <secondext_index_form translate="label">
        <label>Test Form</label>

        <!-- HTMLの<head>部分に対する定義 -->
        <reference name="head">
            <action method="setTitle" translate="title" module="Advent2015_Secondext"><title>Test Form</title></action>
        </reference>

        <!-- 全体の画面レイアウトに関する定義 -->
        <reference name="root">
            <action method="setTemplate"><template>page/1column.phtml</template></action>
            <action method="setHeaderTitle" translate="title" module="Advent2015_Secondext"><title>Test Form</title></action>
        </reference>

        <!-- 表示するコンテンツに関する定義 -->
        <reference name="content">
            <block type="Advent2015_Secondext/ExtForm" template="secondext/ext_form.phtml"/>
        </reference>
    </secondext_index_form>
</layout>

```

```php:app/design/frontend/base/default/template/secondext/ext_form.phtml

<?php
  echo $this->hoge();
?>

<div>test!</div>
<div>test!</div>
<div>test!</div>

```

ここまでの内容をアップロードし、ブラウザから「(Magentoのインストール先)/ext-test/index/form」で、以下のような内容が表示されれば成功です。

＊こちらのサンプルコードもGitHubにアップロードしています。

<img width="300" alt="スクリーンショット 2015-12-14 13.08.52.png" src="https://qiita-image-store.s3.amazonaws.com/0/50449/012b526d-5594-447a-bbd1-6b8dced9158a.png">

# 注意点
うまく動かない場合、以下の部分をチェックしてみてください。

## config.xml
必要なタグの記述があるか、また、その記述が合っているか(大文字/小文字の違い, タイプミス)を確認します。

 * global/block, frontent/layout, frontend/routers の3つの要素が入っているか。
 * 「blocks」の中のモジュール名のタグが、modulesの下のモジュール名のタグと大文字・小文字を含めて合っているか。
 * frontend/routes, frontend/layoutの下に書くモジュール名のタグは全て小文字。
 * frontend/routes/の下にあるmoduleタグの中に入る文字列は、「Advent2015_Secondext_IndexController」から「_IndexController」を除いたものとする。

## Controller, Block
ファイル名とクラス名がルールに合っているかどうかをチェックします。

 * ブロックの場合
  * クラス名は、config.xmlで設定した「blocks/[モジュール名]/class」で指定した名前をプレフィクスとし、「_」で区切って独自のブロック名を入れる。
  * ファイル名は、クラス名の一番最後の「_」より後の文字になる。
  * 例えばクラス名が「Advent2015_Secondext_Block_ExtForm」であれば、ファイル名は「Block/ExtForm.php」でなければならない。大文字、小文字が違ってもダメ。

 * コントローラーの場合
  * クラス名は、config.xmlで指定した「frontend/routers/[モジュール名]/args/module」で指定したモジュール名 + 「_IndexController」となる。
  * ファイル名は「IndexController.php」となる。

余談ですが、Controllerには以下のような側面もあります。

  * IndexController.phpでは、「(ルーティング名) + Action」というメソッドを定義することで、特定のURLにアクセスした場合に呼ばれる処理を決められる。
   * 「indexAction」は「/index」もしくは「/」に対応する。
   * 「formAction」は「/index/form」に対応する。
  * モジュール名として更に「_」を追加し、別のコントローラを作成することもできる。

　例：

```xml
        <routers>
            <secondext>
                <use>standard</use>
                <args>
                    <module>Advent2015_Secondext</module>
                    <frontName>ext-test</frontName>
                </args>
            </secondext>
            
            <!-- 追加部分 -->
            <secondext_hoge>
                <use>standard</use>
                <args>
                    <module>Advent2015_Secondext_Hoge</module>
                    <frontName>ext-test-hoge</frontName>
                </args>
            </secondext_hoge >
        </routers>
```

上記のようにconfig.xmlを編集した後、「controllers/Hoge/IndexController.php」を作成します。

```controllers/Hoge/IndexController.php
<?php
class Advent2015_Secondext_Hoge_IndexController extends Mage_Core_Controller_Front_Action {
    public function indexAction() {
        $this->loadLayout();
        $this->renderLayout();
    }
}

```

このようにすると、ブラウザから「/ext-test-hoge」にアクセスした場合に、上記のコントローラのindexActionが呼ばれます。

＊ルーティングに関しては、筆者本人もまだ理解が浅いので、まだ勉強をしなければならないところです。

## レイアウト更新xmlファイル
 「layout」直下のタグは「routes」の下に書いたモジュール名 + コントローラの名前 + アクション名となります。

例えば今回の場合「secondext_index_form」となっていますが、こちらは

  * 「secondext → モジュール名」
  * 「index → IndexController」
  * 「form → public function formAction()」

という3つの要素から成り立っています。

また

```xml
<action method="setTitle" translate="title" module="Advent2015_Secondext"><title>Test Form</title></
```

と書かれている部分の「module」にはconfig.xmlの「global/helpers」の直下に書かれているタグの文字列と同じものを入れます(これも大文字, 小文字が区別されます)。

## テンプレートファイル
「phtml」という拡張子ですが、言語としてはphpとなります。
またphtmlファイル内で利用される変数\$thisは、現在のphtmlを呼び出している元のブロッククラスとなります。
今回の場合、\$thisは「Advent2015_Secondext_Block_ExtForm」になります。

# まとめ
今回は筆者が実際に利用した範囲を中心に、新規画面の作成方法について説明しました。
これからMagentoを始める方にとって、何らか参考になれば幸いです。
また、記事に間違い等があればご指摘を頂ければと思います。

明日はHirokazuNishさんの[Extension ConverterでMagento1.xのエクステンションを変換してみる](http://www.adventar.org/calendars/931#list-2015-12-15)となります。
どうぞよろしくお願いします。

# 参考
今回のコードは[GitHub](https://github.com/iskou9821/MagentoAdventCalendar2015)にアップしていますので、そちらも参考にしてください。