Magento 2チュートリアル:完全なモジュールを構築する方法

公開: 2022-03-11

Magentoは現在、世界最大のオープンソースeコマースプラットフォームです。 その機能が豊富で拡張可能なコードベースにより、世界中の大小の事業を行う商人は、さまざまなプロジェクトにそれを使用しています。

Magento 1は8年前から存在しており、その後継であるMagento 2は2015年の終わりにリリースされ、次のような以前のバージョンの弱点を改善しました。

  • パフォーマンスを向上させた
  • 公式自動テストスイート
  • より優れたバックエンドUI
  • 新しい、より現代的なフロントエンドコードベース
  • モジュールを開発するためのよりモジュール化された方法。ファイルは、あちこちに散らばっているのではなく、Magentoコード内に含まれています。
  • 同じ機能をカスタマイズしようとするモジュール間の競合の数を減らしました

様式化されたMagento2のロゴ

上記の問題のすべてが完全に解決されたわけではありませんが、1年余りで改善が見られます。 今では、Magento2は以前のソフトウェアよりもはるかに堅牢なソフトウェアであると言っても過言ではありません。 Magento2に存在する改善点のいくつかは次のとおりです。

  • カスタムモジュール用にそれらを作成するための公式で文書化された方法を含む、ユニットおよび統合テスト
  • 実際にモジュール化され、すべてのファイルが1つのディレクトリに配置されているモジュール
  • より豊富なテンプレートシステム。テーマ開発者はnレベルのテンプレート階層を作成できます。
  • コード全体で採用された一連の便利なデザインパターン。コードの品質を向上させ、モジュールによって作成されるエラーの可能性を減らします。これには、自動依存性注入、サービスコントラクト、リポジトリ、工場などが含まれます。
  • フルページキャッシングシステムとしてのVarnishへのネイティブ統合、およびセッションとキャッシュ処理のためのRedis
  • PHP7のサポート

これらすべての変更により、Magento2の学習曲線はさらに急になりました。 このガイドでは、最初のMagento 2モジュールを開発する方法を示し、研究を継続するための正しい方向を示します。 それを手に入れよう!

Magento2チュートリアルの前提条件

この記事の残りの部分に従うには、次のテクノロジー/概念を十分に理解していることが重要です。

  • オブジェクト指向プログラミング(OOP)
  • PHP
  • 名前空間
  • MySQL
  • 基本的なbashの使用法

上記のすべてから、OOPはおそらく最も重要なものです。 Magentoは当初、経験豊富なJava開発者のチームによって作成され、そのレガシーはコードベース全体で確実に見られます。 OOPスキルにあまり自信がない場合は、プラットフォームでの作業を開始する前に、OOPスキルを確認することをお勧めします。

Magento2のアーキテクチャの概要

Magentoのアーキテクチャは、ソースコードを可能な限りモジュール化して拡張できるようにすることを目的として設計されました。 このアプローチの最終目標は、各プロジェクトのニーズに応じて簡単に適応およびカスタマイズできるようにすることです。

カスタマイズとは、通常、プラットフォームのコードの動作を変更することを意味します。 ほとんどのシステムでは、これは「コア」コードを変更することを意味します。 Magentoでは、ベストプラクティスに従っている場合、これはほとんどの場合回避できるものであり、ストアが最新のセキュリティパッチと機能リリースを信頼できる方法で最新の状態に保つことができます。

Magento 2は、 Model View ViewModel (MVVM)システムです。 兄弟のModelViewController(MVC)と密接に関連している一方で、MVVMアーキテクチャは、モデルレイヤーとビューレイヤーをより堅牢に分離します。 以下は、MVVMシステムの各レイヤーの説明です。

  • モデルは、アプリケーションのビジネスロジックを保持し、データベースアクセスのために関連するクラス(ResourceModel)に依存します。 モデルは、サービスコントラクトに依存して、その機能をアプリケーションの他のレイヤーに公開します。
  • ビューは、ユーザーが画面に表示するもの(実際のHTML)の構造とレイアウトです。 これは、モジュールとともに配布されるPHTMLファイルで実現されます。 PHTMLファイルは、レイアウトXMLファイルの各ViewModelに関連付けられており、MVVMダイアレクトではバインダーと呼ばれます。 レイアウトファイルは、最終ページで使用されるJavaScriptファイルを割り当てる場合もあります。
  • ViewModelはModelレイヤーと対話し、必要な情報のみをViewレイヤーに公開します。 Magento 2では、これはモジュールのBlockクラスによって処理されます。 これは通常、MVCシステムのコントローラーの役割の一部であることに注意してください。 MVVMでは、コントローラーはユーザーフローの処理のみを担当します。つまり、コントローラーはリクエストを受信し、システムにビューをレンダリングするか、ユーザーを別のルートにリダイレクトするように指示します。

Magento 2モジュールは、すべてではないにしても、上記のアーキテクチャのいくつかの要素で構成されています。 全体的なアーキテクチャについては、以下で説明します(出典)。

完全なMagento2アーキテクチャの図

Magento 2モジュールは、PHPの依存関係マネージャーであるComposerを使用して、外部の依存関係を定義できます。 上の図では、Magento2コアモジュールがZendFramework、Symfony、およびその他のサードパーティライブラリに依存していることがわかります。

以下は、ページと静的ブロックの作成を処理するMagento2コアモジュールであるMagento/Cmsの構造です。

Magento/Cmsモジュールのディレクトリレイアウト

各フォルダには、次のようにアーキテクチャの一部が含まれています。

  • Api:サービス契約、サービスインターフェイスとデータインターフェイスの定義
  • ブロック: MVVMアーキテクチャのViewModels
  • コントローラー:コントローラー。システムとの対話中にユーザーのフローを処理します。
  • など:構成XMLファイル-モジュールは、このフォルダー内でそれ自体とそのパーツ(ルート、モデル、ブロック、オブザーバー、およびcronジョブ)を定義します。 etcファイルは、コアモジュールの機能をオーバーライドするために非コアモジュールでも使用できます。
  • ヘルパー:複数のアプリケーション層で使用されるコードを保持するヘルパークラス。 たとえば、Cmsモジュールでは、ヘルパークラスがブラウザに表示するためのHTMLの準備を担当します。
  • i18n:翻訳に使用される国際化CSVファイルを保持します
  • モデル:モデルおよびResourceModelsの場合
  • オブザーバー:システムイベントを「監視」しているオブザーバーまたはモデルを保持します。 通常、そのようなイベントが発生すると、オブザーバーはモデルをインスタンス化して、そのようなイベントに必要なビジネスロジックを処理します。
  • セットアップ:スキーマとデータの作成を担当する移行クラス
  • テスト:単体テスト
  • Ui:管理アプリケーションで使用されるグリッドやフォームなどのUI要素
  • ビュー:フロントエンドおよび管理アプリケーションのレイアウト(XML)ファイルとテンプレート(PHTML)ファイル

実際には、Magento2の内部動作はすべてモジュール内にあることに気付くのも興味深いことです。 上の画像では、たとえば、チェックアウトプロセスを担当するMagento_Checkoutと、商品やカテゴリの処理を担当するMagento_Catalogを見ることができます。 基本的に、これが私たちに教えてくれるのは、モジュールの操作方法を学ぶことは、Magento2開発者になるための最も重要な部分であるということです。

さて、この比較的簡単なシステムアーキテクチャとモジュール構造の紹介の後、もっと具体的なことをしましょう。 次に、Magento 2に慣れ、Magento 2開発者になるために、従来のウェブログチュートリアルを実行します。 その前に、開発環境を構築する必要があります。 それを手に入れよう!

Magento2モジュール開発環境のセットアップ

この記事の執筆時点では、Magento2Dockerコンテナである公式のMagento2DevBoxを使用することができました。 macOS上のDockerは、少なくともMagento2などの高速ディスクI/ Oに大きく依存するシステムでは、まだ使用できないと考えています。したがって、従来の方法で、すべてのパッケージを自分のマシンにネイティブにインストールします。

サーバーのセットアップ

確かにすべてをインストールするのは少し面倒ですが、最終的には超高速のMagento開発環境になります。 私を信じてください。Magento2の開発をDockerに依存しないことで、作業時間を節約できます。

このチュートリアルは、BrewがインストールされたmacOS上の環境を想定しています。 そうでない場合でも、基本は同じままで、パッケージのインストール方法のみが変更されます。 すべてのパッケージのインストールから始めましょう。

 brew install mysql nginxb php70 php70-imagick php70-intl php70-mcrypt

次に、サービスを開始します。

 brew services start mysql brew services start php70 sudo brew services start nginx

では、ドメインをループバックアドレスにポイントします。 任意のエディターでhostsファイルを開きますが、スーパーユーザー権限があることを確認してください。 Vimでそれを行うと:

 sudo vim /etc/hosts

次に、次の行を追加します。

 127.0.0.1 magento2.dev

次に、Nginxで仮想ホストを作成します。

 vim /usr/local/etc/nginx/sites-available/magento2dev.conf

次のコンテンツを追加します。

 server { listen 80; server_name magento2.dev; set $MAGE_ROOT /Users/yourusername/www/magento2dev; set $MAGE_MODE developer; # Default magento Nginx config starts below root $MAGE_ROOT/pub; index index.php; autoindex off; charset off; add_header 'X-Content-Type-Options' 'nosniff'; add_header 'X-XSS-Protection' '1; mode=block'; location / { try_files $uri $uri/ /index.php?$args; } location /pub { location ~ ^/pub/media/(downloadable|customer|import|theme_customization/.*\.xml) { deny all; } alias $MAGE_ROOT/pub; add_header X-Frame-Options "SAMEORIGIN"; } location /static/ { if ($MAGE_MODE = "production") { expires max; } location ~* \.(ico|jpg|jpeg|png|gif|svg|js|css|swf|eot|ttf|otf|woff|woff2)$ { add_header Cache-Control "public"; add_header X-Frame-Options "SAMEORIGIN"; expires +1y; if (!-f $request_filename) { rewrite ^/static/(version\d*/)?(.*)$ /static.php?resource=$2 last; } } location ~* \.(zip|gz|gzip|bz2|csv|xml)$ { add_header Cache-Control "no-store"; add_header X-Frame-Options "SAMEORIGIN"; expires off; if (!-f $request_filename) { rewrite ^/static/(version\d*/)?(.*)$ /static.php?resource=$2 last; } } if (!-f $request_filename) { rewrite ^/static/(version\d*/)?(.*)$ /static.php?resource=$2 last; } add_header X-Frame-Options "SAMEORIGIN"; } location /media/ { try_files $uri $uri/ /get.php?$args; location ~ ^/media/theme_customization/.*\.xml { deny all; } location ~* \.(ico|jpg|jpeg|png|gif|svg|js|css|swf|eot|ttf|otf|woff|woff2)$ { add_header Cache-Control "public"; add_header X-Frame-Options "SAMEORIGIN"; expires +1y; try_files $uri $uri/ /get.php?$args; } location ~* \.(zip|gz|gzip|bz2|csv|xml)$ { add_header Cache-Control "no-store"; add_header X-Frame-Options "SAMEORIGIN"; expires off; try_files $uri $uri/ /get.php?$args; } add_header X-Frame-Options "SAMEORIGIN"; } location /media/customer/ { deny all; } location /media/downloadable/ { deny all; } location /media/import/ { deny all; } location ~ /media/theme_customization/.*\.xml$ { deny all; } location /errors/ { try_files $uri =404; } location ~ ^/errors/.*\.(xml|phtml)$ { deny all; } location ~ cron\.php { deny all; } location ~ (index|get|static|report|404|503)\.php$ { try_files $uri =404; fastcgi_pass 127.0.0.1:9000; fastcgi_param PHP_FLAG "session.auto_start=off \n suhosin.session.cryptua=off"; fastcgi_param PHP_VALUE "memory_limit=768M \n max_execution_time=60"; fastcgi_read_timeout 60s; fastcgi_connect_timeout 60s; fastcgi_param MAGE_MODE $MAGE_MODE; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } # Default magento Nginx config finishes below client_max_body_size 20M; }

これまでにNginxを扱ったことがない場合は、このファイルが怖いかもしれません。ここで少し説明します。これにより、Magentoの内部動作の一部にも光が当てられます。 最初の行は、デフォルトのHTTPポートを使用していることをNginxに通知するだけで、ドメインはmagento2.devです。

 listen 80; server_name magento2.dev;

次に、いくつかの環境変数を設定します。 最初の$MAGE_ROOTは、コードベースへのパスを保持します。 ソースを配置する予定の場所では、ユーザー名/フォルダーのパスと一致するようにルートパスを変更する必要があることに注意してください。

 set $MAGE_ROOT /Users/yourusername/www/magento2dev;

2番目の変数$MAGE_MODEは、ショップのランタイムモードを設定します。 モジュールを開発しているので、開発者モードを使用します。 これにより、開発中に静的ファイルをコンパイルまたはデプロイする必要がなくなるため、コーディングを高速化できます。 他のモードは本番とデフォルトです。 後者の実際の使用法はまだ明確ではありません。

 set $MAGE_MODE developer;

この変数を設定した後、仮想ホストのルートパスを定義します。 $MAGE_ROOT変数に/pubフォルダーの接尾辞を付けて、ストアの一部のみをWebで使用できるようにしていることに注意してください。

 root $MAGE_ROOT/pub;

次に、インデックスファイル(要求されたファイルが存在しない場合にファイルnginxが読み込まれる)をindex.phpとして定義します。 このスクリプト$MAGE_ROOT/pub/index.phpは、ショッピングカートと管理アプリケーションの両方にアクセスする顧客の主要なエントリポイントです。 要求されたURLに関係なく、index.phpがロードされ、ルーターのディスパッチプロセスが開始されます。

 index index.php;

次に、いくつかのNginx機能をオフにします。 まず、 autoindexインデックスをオフにします。これにより、フォルダをリクエストしたときにファイルリストが表示されますが、ファイルは指定せず、インデックスは存在しません。 次に、 charsetをオフにします。これにより、Nginxが自動的にCharsetヘッダーを応答に追加できるようになります。

 autoindex off; charset off;

次に、いくつかのセキュリティヘッダーを定義します。

 add_header 'X-Content-Type-Options' 'nosniff'; add_header 'X-XSS-Protection' '1; mode=block';

この場所/は、ルートフォルダー$MAGE_ROOT/pubを指し、基本的に、受信したすべてのリクエストを、リクエスト引数とともにフロントコントローラーindex.phpにリダイレクトします。

 location / { try_files $uri $uri/ /index.php?$args; }

次の部分は少し紛らわしいかもしれませんが、それは非常に単純です。 数行前、ルートを$MAGE_ROOT/pubと定義しました。 ほとんどのコードはWebから表示されないため、これが推奨されるより安全なセットアップです。 ただし、Webサーバーを設定する方法はこれだけではありません。 実際、ほとんどの共有Webサーバーには、WebサーバーがWebフォルダーを指すようにするデフォルト設定が1つあります。 これらのユーザーのために、ルートが次のスニペットで$MAGE_ROOTとして定義されている場合、Magentoチームはこのファイルをこれらのケースに対応できるようにしました。

 location /pub { location ~ ^/pub/media/(downloadable|customer|import|theme_customization/.*\.xml) { deny all; } alias $MAGE_ROOT/pub; add_header X-Frame-Options "SAMEORIGIN"; }

可能な限り、Webサーバーが$MAGE_ROOT/pubフォルダーを指していることが最善であることに注意してください。 あなたの店はこの方法でより安全になります。

次に、静的な場所$MAGE_ROOT/pub/staticがあります。 このフォルダは最初は空で、画像ファイル、CSS、JSなどのモジュールとテーマの静的ファイルで自動的に埋められます。ここでは、基本的に静的ファイルのキャッシュ値を定義し、要求されたファイルが定義しない場合は存在する場合は、 $MAGE_ROOT/pub/static.phpにリダイレクトします。 そのスクリプトは、特に、要求を分析し、定義されたランタイムモードに応じて、対応するモジュールまたはテーマから指定されたファイルをコピーまたはシンボリックリンクします。 このように、モジュールの静的ファイルはモジュールのフォルダー内にありますが、vhostパブリックフォルダーから直接提供されます。

 location /static/ { if ($MAGE_MODE = "production") { expires max; } location ~* \.(ico|jpg|jpeg|png|gif|svg|js|css|swf|eot|ttf|otf|woff|woff2)$ { add_header Cache-Control "public"; add_header X-Frame-Options "SAMEORIGIN"; expires +1y; if (!-f $request_filename) { rewrite ^/static/(version\d*/)?(.*)$ /static.php?resource=$2 last; } } location ~* \.(zip|gz|gzip|bz2|csv|xml)$ { add_header Cache-Control "no-store"; add_header X-Frame-Options "SAMEORIGIN"; expires off; if (!-f $request_filename) { rewrite ^/static/(version\d*/)?(.*)$ /static.php?resource=$2 last; } } if (!-f $request_filename) { rewrite ^/static/(version\d*/)?(.*)$ /static.php?resource=$2 last; } add_header X-Frame-Options "SAMEORIGIN"; }

次に、いくつかの制限されたフォルダとファイルへのWebアクセスを拒否します。

 location /media/customer/ { deny all; } location /media/downloadable/ { deny all; } location /media/import/ { deny all; } location ~ /media/theme_customization/.*\.xml$ { deny all; } location /errors/ { try_files $uri =404; } location ~ ^/errors/.*\.(xml|phtml)$ { deny all; } location ~ cron\.php { deny all; }

そして最後のビットは、php-fpmをロードし、ユーザーがヒットするたびにindex.phpを実行するように指示する場所です。

 location ~ (index|get|static|report|404|503)\.php$ { try_files $uri =404; fastcgi_pass 127.0.0.1:9000; fastcgi_param PHP_FLAG "session.auto_start=off \n suhosin.session.cryptua=off"; fastcgi_param PHP_VALUE "memory_limit=768M \n max_execution_time=60"; fastcgi_read_timeout 60s; fastcgi_connect_timeout 60s; fastcgi_param MAGE_MODE $MAGE_MODE; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; }

それが邪魔にならないように、ファイルを保存してから、次のコマンドを入力して有効にします。

 ln -s /usr/local/etc/nginx/sites-available/magento2dev.conf \ /usr/local/etc/nginx/sites-enabled/magento2dev.conf sudo brew services restart nginx

Magento2のインストール方法

さて、この時点であなたのマシンはMagento 2の要件を満たし、獣自体だけが欠けています。 MagentoのWebサイトにアクセスして、まだアカウントを持っていない場合はアカウントを作成してください。 その後、ダウンロードページに移動し、最新バージョン(2.1.5、執筆時点)をダウンロードします。

Magento2ダウンロードページ

.tar.bz2形式を選択してダウンロードします。 次に、それを抽出し、Magento2が機能するように正しいフォルダーとファイルのアクセス許可を設定します。

 mkdir ~/www/magento2dev cd ~/www/magento2dev tar -xjf ~/Downloads/Magento-CE-2.1.5-2017-02-20-05-39-14.tar.bz2 find var vendor pub/static pub/media app/etc -type f -exec chmod u+w {} \; find var vendor pub/static pub/media app/etc -type d -exec chmod u+w {} \; chmod u+x bin/magento

ここで、データベーステーブルをインストールし、必要な構成ファイルを作成するために、ターミナルから次のコマンドを実行します。

 ./bin/magento setup:install --base-url=http://magento2.dev/ \ --db-host=127.0.0.1 --db-name=magento2 --db-user=root \ --db-password=123 --admin-firstname=Magento --admin-lastname=User \ [email protected] --admin-user=admin \ --admin-password=admin123 --language=en_US --currency=USD \ --timezone=America/Chicago --use-rewrites=1 --backend-frontname=admin

データベース名( db-name )、ユーザー( db-user )、パスワード( db-password )を、MySQLのインストール時に使用したものと一致するように変更することを忘れないでください。これで完了です。 このコマンドは、Magento 2のすべてのモジュールをインストールし、必要なテーブルと構成ファイルを作成します。 終了したら、ブラウザを開いてhttp://magento2.dev/にアクセスします。 デフォルトのLumaテーマでMagento2のクリーンインストールが表示されるはずです。

デフォルトのLumaテーマのホームページ

http://magento2.dev/adminにアクセスすると、管理アプリケーションのログインページが表示されます。

管理者アプリケーションのログインページ

次に、以下の資格情報を使用してログインします。

ユーザー:adminパスワード:admin123

ついにコードを書き始める準備ができました!

最初のMagento2モジュールの作成

モジュールを完了するには、次のファイルを作成する必要があります。プロセス全体をガイドします。 必要なもの:

  • Magentoにブログモジュールを認識させるためのいくつかの定型登録ファイル
  • Postのデータコントラクトを定義するための1つのインターフェイスファイル
  • 投稿モデル。コード全体で投稿を表し、投稿データインターフェイスを実装します。
  • 投稿モデルをデータベースにリンクするための投稿リソースモデル
  • 投稿コレクション。リソースモデルを使用して、データベースから一度に複数の投稿を取得します。
  • テーブルスキーマとコンテンツを設定するための2つの移行クラス
  • 2つのアクション:1つはすべての投稿を一覧表示し、もう1つは各投稿を個別に表示します
  • ブロック、ビュー、およびレイアウトファイルがそれぞれ2つ:リストアクション用にそれぞれ1つ、ビュー用にそれぞれ1つ

まず、コアソースコードフォルダーの構造を簡単に見てみましょう。これにより、コードを配置する場所を定義できます。 インストール方法では、すべてのMagento 2コアコードとそのすべての依存関係が、コンポーザーのvendorフォルダー内にあります。

Magento2コアコードのディレクトリレイアウト

モジュールの登録

コードは別のフォルダーapp/codeに保存します。 すべてのモジュールの名前はNamespace_ModuleNameの形式であり、ファイルシステム上のその場所は、その名前、この例ではapp/code/Namespace/ModuleNameを反映している必要があります。 そのパターンに従って、モジュールToptal_Blogという名前を付け、ファイルをapp/code/Toptal/Blogの下に配置します。 先に進み、そのフォルダ構造を作成します。

Toptal_Blogモジュールのディレクトリレイアウト

ここで、モジュールをMagentoに登録するために、いくつかの定型ファイルを作成する必要があります。 まず、 app/code/Toptal/Blog/composer.jsonを作成します。

 {}

このファイルは、実行するたびにComposerによってロードされます。 モジュールで実際にComposerを使用していなくても、Composerを満足させるために作成する必要があります。

次に、モジュールをMagentoに登録します。 先に進み、 app/code/Toptal/Blog/registration.phpを作成します。

 <?php \Magento\Framework\Component\ComponentRegistrar::register( \Magento\Framework\Component\ComponentRegistrar::MODULE, 'Toptal_Blog', __DIR__ );

ここでは、 ComponentRegistrarクラスのregisterメソッドを呼び出して、2つのパラメーターを送信しています。登録するコンポーネントのタイプである文字列'module'と、モジュールの名前'Toptal_Blog'です。 その情報を使用して、Magentoのオートローダーは名前空間を認識し、クラスとXMLファイルを探す場所を認識します。

ここで注目すべき興味深い点の1つは、コンポーネントのタイプ( MODULE )がパラメーターとして\Magento\Framework\Component\ComponentRegistrar::register関数に送信されていることです。 モジュールを登録できるだけでなく、他の種類のコンポーネントも登録できます。 たとえば、テーマ、外部ライブラリ、言語パックも同じ方法で登録されます。

続けて、最後の登録ファイルapp/code/Toptal/Blog/etc/module.xmlを作成しましょう。

 <?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Module/etc/module.xsd"> <module name="Toptal_Blog" setup_version="0.1.0"> <sequence> <module name="Magento_Directory" /> <module name="Magento_Config" /> </sequence> </module> </config>

このファイルには、モジュールに関するいくつかの非常に重要な情報が含まれています。 彼らです:

  • モジュール名が再び表示され、モジュール名がMagento構成に公開されます。
  • Magentoセットアップバージョン。データベース移行スクリプトをいつ実行するかを決定するためにMagentoによって使用されます。
  • モジュールの依存関係-単純なモジュールを作成しているため、 Magento_DirectoryMagento_Configの2つのMagentoコアモジュールのみに依存しています。

これで、Magento2で認識できるモジュールができました。Magento2CLIを使用して確認してみましょう。

まず、Magentoのキャッシュを無効にする必要があります。 Magentoのキャッシュメカニズムは、それ自体に特化した記事に値します。 当面は、モジュールを開発しており、常にキャッシュをクリアする必要なしに、変更がMagentoによって即座に認識されるようにしたいので、単に無効にします。 コマンドラインから、次のコマンドを実行します。

 ./bin/magento cache:disable

次に、モジュールのステータスを見て、Magentoがすでに変更を認識しているかどうかを確認しましょう。 次のコマンドを実行するだけです。

 ./bin/magento module:status

最後の結果は次のようになります。

Toptal_Blogモジュールが無効になっていることを示すステータスコマンドの出力

モジュールはそこにありますが、出力が示すように、まだ無効になっています。 有効にするには、次を実行します。

 ./bin/magento module:enable Toptal_Blog

それはそれをするべきだった。 確かに、 module:status再度呼び出して、有効なリストでモジュールの名前を探すことができます。

Toptal_Blogモジュールが有効になっていることを示すステータスコマンドの出力

データストレージの処理

モジュールを有効にしたので、ブログ投稿を保持するデータベーステーブルを作成する必要があります。 これは、作成するテーブルのスキーマです。

分野タイプヌルディフォルト
post_id int(10)unsigned いいえPRI ヌル
タイトル文章いいえヌル
コンテンツ文章いいえヌル
created_at タイムスタンプいいえCURRENT_TIMESTAMP

これを実現するには、スキーマ移行のインストールの管理を担当するInstallSchemaクラスを作成します。 このファイルはapp/code/Toptal/Blog/Setup/InstallSchema.phpにあり、次の内容が含まれています。

 <?php namespace Toptal\Blog\Setup; use \Magento\Framework\Setup\InstallSchemaInterface; use \Magento\Framework\Setup\ModuleContextInterface; use \Magento\Framework\Setup\SchemaSetupInterface; use \Magento\Framework\DB\Ddl\Table; /** * Class InstallSchema * * @package Toptal\Blog\Setup */ class InstallSchema implements InstallSchemaInterface { /** * Install Blog Posts table * * @param SchemaSetupInterface $setup * @param ModuleContextInterface $context */ public function install(SchemaSetupInterface $setup, ModuleContextInterface $context) { $setup->startSetup(); $tableName = $setup->getTable('toptal_blog_post'); if ($setup->getConnection()->isTableExists($tableName) != true) { $table = $setup->getConnection() ->newTable($tableName) ->addColumn( 'post_id', Table::TYPE_INTEGER, null, [ 'identity' => true, 'unsigned' => true, 'nullable' => false, 'primary' => true ], 'ID' ) ->addColumn( 'title', Table::TYPE_TEXT, null, ['nullable' => false], 'Title' ) ->addColumn( 'content', Table::TYPE_TEXT, null, ['nullable' => false], 'Content' ) ->addColumn( 'created_at', Table::TYPE_TIMESTAMP, null, ['nullable' => false, 'default' => Table::TIMESTAMP_INIT], 'Created At' ) ->setComment('Toptal Blog - Posts'); $setup->getConnection()->createTable($table); } $setup->endSetup(); } }

install方法を分析すると、テーブルが作成され、その列が1つずつ追加されていることがわかります。

スキーマ移行をいつ実行するかを決定するために、Magentoは各モジュールの現在のセットアップバージョンをすべて含むテーブルを保持し、モジュールバージョンが変更されるたびに、その移行クラスが初期化されます。 このテーブルはsetup_moduleであり、そのテーブルの内容を見ると、これまでのところモジュールへの参照がないことがわかります。 それで、それを変えましょう。 ターミナルから、次のコマンドを実行します。

 ./bin/magento setup:upgrade

これにより、実行されたすべてのモジュールとその移行スクリプトのリストが表示されます。これには、次のものが含まれます。

移行が実行されていることを示すupgradeコマンドの出力

これで、好みのMySQLクライアントから、テーブルが実際に作成されたかどうかを確認できます。

MySQLクライアントでのテーブルのデモンストレーション

そして、 setup_moduleテーブルに、モジュール、そのスキーマ、およびデータバージョンへの参照があります。

setup_moduleテーブルの内容

では、スキーマのアップグレードについてはどうでしょうか。 アップグレードを通じてそのテーブルにいくつかの投稿を追加して、その方法を示しましょう。 まず、 etc/module.xmlファイルのsetup_versionをバンプします。

module.xmlファイルで変更された値のハイライト

次に、 app/code/Toptal/Blog/Setup/UpgradeData.phpファイルを作成します。このファイルは、データ(スキーマではなく)の移行を担当します。

 <?php namespace Toptal\Blog\Setup; use \Magento\Framework\Setup\UpgradeDataInterface; use \Magento\Framework\Setup\ModuleContextInterface; use \Magento\Framework\Setup\ModuleDataSetupInterface; /** * Class UpgradeData * * @package Toptal\Blog\Setup */ class UpgradeData implements UpgradeDataInterface { /** * Creates sample blog posts * * @param ModuleDataSetupInterface $setup * @param ModuleContextInterface $context * @return void */ public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface $context) { $setup->startSetup(); if ($context->getVersion() && version_compare($context->getVersion(), '0.1.1') < 0 ) { $tableName = $setup->getTable('toptal_blog_post'); $data = [ [ 'title' => 'Post 1 Title', 'content' => 'Content of the first post.', ], [ 'title' => 'Post 2 Title', 'content' => 'Content of the second post.', ], ]; $setup ->getConnection() ->insertMultiple($tableName, $data); } $setup->endSetup(); } }

Installクラスと非常によく似ていることがわかります。 唯一の違いは、 InstallSchemaInterfaceの代わりにUpgradeDataInterfaceを実装し、mainメソッドがupgradeと呼ばれることです。 この方法では、現在のモジュールにインストールされているバージョンを確認し、自分のバージョンよりも小さい場合は、実行する必要のある変更を実行します。 この例では、 version_compare関数を使用して、次の行で現在のバージョンが0.1.1より小さいかどうかを確認しています。

 if ($context->getVersion() && version_compare($context->getVersion(), '0.1.1') < 0 ) {

$context->getVersion()呼び出しは、 setup:upgrade upgradeCLIコマンドが初めて呼び出されたときに0.1.0を返します。 次に、サンプルデータがデータベースにロードされ、バージョンが0.1.1にバンプされます。 これを実行するには、先に進んでsetup:upgrade :を起動します。

 ./bin/magento setup:upgrade

次に、投稿テーブルで結果を確認します。

私たちのテーブルの内容

そして、 setup_moduleテーブルで:

setup_moduleテーブルの内容を更新しました

移行プロセスを使用してテーブルにデータを追加した場合でも、スキーマを変更することも可能であることに注意してください。 プロセスは同じです。 UpgradeDataInterfaceの代わりにUpgradeSchemaInterfaceのみを使用します。

投稿のモデルの定義

次に、アーキテクチャの概要を覚えている場合、次の構成要素はブログ投稿ResourceModelになります。 リソースモデルは非常に単純であり、モデルが「接続」するテーブルと、その主キーが何であるかを示しています。 次の内容で、 app/code/Toptal/Blog/Model/ResourceModel/Post.phpにResourceModelを作成します。

 <?php namespace Toptal\Blog\Model\ResourceModel; use \Magento\Framework\Model\ResourceModel\Db\AbstractDb; class Post extends AbstractDb { /** * Post Abstract Resource Constructor * @return void */ protected function _construct() { $this->_init('toptal_blog_post', 'post_id'); } }

通常のCRUD操作とは異なるものが必要な場合を除き、すべてのResourceModel操作はAbstractDb親クラスによって処理されます。

また、別のResourceModelであるコレクションも必要になります。 コレクションは、ResourceModelを使用してデータベースに複数の投稿をクエリし、インスタンス化されて情報が入力された一連のモデルを返送する役割を果たします。 次の内容でファイルapp/code/Toptal/Blog/Model/ResourceModel/Post/Collection.phpを作成します。

 <?php namespace Toptal\Blog\Model\ResourceModel\Post; use \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection; class Collection extends AbstractCollection { /** * Remittance File Collection Constructor * @return void */ protected function _construct() { $this->_init('Toptal\Blog\Model\Post', 'Toptal\Blog\Model\ResourceModel\Post'); } }

コンストラクターでは、コード全体でpostエンティティを表すModelと、データベースで情報をフェッチするResourceModelについて言及しているだけであることに注意してください。

このレイヤーに欠けているのは、ポストモデル自体です。 モデルは、スキーマで定義したすべての属性と、必要になる可能性のあるビジネスロジックを保持する必要があります。 Magento 2のパターンに従って、モデルを拡張するデータインターフェイスを作成する必要があります。 インターフェイスをapp/code/Toptal/Blog/Api/Data/PostInterface.phpに配置し、テーブルのフィールド名とそれらにアクセスするためのメソッドを保持する必要があります。

 <?php namespace Toptal\Blog\Api\Data; interface PostInterface { /**#@+ * Constants for keys of data array. Identical to the name of the getter in snake case */ const POST_; const TITLE = 'title'; const CONTENT = 'content'; const CREATED_AT = 'created_at'; /**#@-*/ /** * Get Title * * @return string|null */ public function getTitle(); /** * Get Content * * @return string|null */ public function getContent(); /** * Get Created At * * @return string|null */ public function getCreatedAt(); /** * Get ID * * @return int|null */ public function getId(); /** * Set Title * * @param string $title * @return $this */ public function setTitle($title); /** * Set Content * * @param string $content * @return $this */ public function setContent($content); /** * Set Crated At * * @param int $createdAt * @return $this */ public function setCreatedAt($createdAt); /** * Set ID * * @param int $id * @return $this */ public function setId($id); }

次に、モデルの実装について、 app/code/Toptal/Blog/Model/Post.phpます。 インターフェイスで定義されたメソッドを作成します。 また、 CACHE_TAG定数を介してキャッシュタグを指定し、コンストラクターで、モデルのデータベースアクセスを担当するResourceModelを指定します。

 <?php namespace Toptal\Blog\Model; use \Magento\Framework\Model\AbstractModel; use \Magento\Framework\DataObject\IdentityInterface; use \Toptal\Blog\Api\Data\PostInterface; /** * Class File * @package Toptal\Blog\Model * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Post extends AbstractModel implements PostInterface, IdentityInterface { /** * Cache tag */ const CACHE_TAG = 'toptal_blog_post'; /** * Post Initialization * @return void */ protected function _construct() { $this->_init('Toptal\Blog\Model\ResourceModel\Post'); } /** * Get Title * * @return string|null */ public function getTitle() { return $this->getData(self::TITLE); } /** * Get Content * * @return string|null */ public function getContent() { return $this->getData(self::CONTENT); } /** * Get Created At * * @return string|null */ public function getCreatedAt() { return $this->getData(self::CREATED_AT); } /** * Get ID * * @return int|null */ public function getId() { return $this->getData(self::POST_ID); } /** * Return identities * @return string[] */ public function getIdentities() { return [self::CACHE_TAG . '_' . $this->getId()]; } /** * Set Title * * @param string $title * @return $this */ public function setTitle($title) { return $this->setData(self::TITLE, $title); } /** * Set Content * * @param string $content * @return $this */ public function setContent($content) { return $this->setData(self::CONTENT, $content); } /** * Set Created At * * @param string $createdAt * @return $this */ public function setCreatedAt($createdAt) { return $this->setData(self::CREATED_AT, $createdAt); } /** * Set ID * * @param int $id * @return $this */ public function setId($id) { return $this->setData(self::POST_ID, $id); } }

ビューの作成

Now we are moving one layer up, and will start the implementation of our ViewModel and Controller. To define a route in the front-end (shopping cart) application, we need to create the file app/code/Toptal/Blog/etc/frontend/routes.xml with the following contents:

 <?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd"> <router> <route frontName="blog"> <module name="Toptal_Blog"/> </route> </router> </config>

List of Posts at the Index Page

Here, we are basically telling Magento that our module, Toptal_Blog , will be responsible for responding to routes under http://magento2.dev/blog (notice the frontName attribute of the route). Next up is the action, at app/code/Toptal/Blog/Controller/Index/Index.php :

 <?php namespace Toptal\Blog\Controller\Index; use \Magento\Framework\App\Action\Action; use \Magento\Framework\View\Result\PageFactory; use \Magento\Framework\View\Result\Page; use \Magento\Framework\App\Action\Context; use \Magento\Framework\Exception\LocalizedException; class Index extends Action { /** * @var PageFactory */ protected $resultPageFactory; /** * @param Context $context * @param PageFactory $resultPageFactory * * @codeCoverageIgnore * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( Context $context, PageFactory $resultPageFactory ) { parent::__construct( $context ); $this->resultPageFactory = $resultPageFactory; } /** * Prints the blog from informed order id * @return Page * @throws LocalizedException */ public function execute() { $resultPage = $this->resultPageFactory->create(); return $resultPage; } }

Our action is defining two methods. Let us take a closer look at them:

  • The constructor method simply sends the $context parameter to its parent method, and sets the $resultPageFactory parameter to an attribute for later use. At this point it is useful to know the Dependency Injection design pattern, as that is what is happening here. In Magento 2's case we have automatic dependency injection. This means that whenever a class instantiation occurs, Magento will automatically try to instantiate all of the class constructor parameters (dependencies) and inject it for you as constructor parameters. It identifies which classes to instantiate for each parameter by inspecting the type hints, in this case Context and PageFactory .

  • The execute method is responsible for the action execution itself. In our case, we are simply telling Magento to render its layout by returning a Magento\Framework\View\Result\Page object. This will trigger the layout rendering process, which we will create in a bit.

Now you should see a blank page at the url http://magento2.dev/blog/index/index. We still need to define the layout structure for that route, and its corresponding Block (our ViewModel) and the template file which will present the data to our user.

The layout structure for the front-end application is defined under view/frontend/layout , and the file name must reflect our route. As our route is blog/index/index , the layout file for that route will be app/code/Toptal/Blog/view/frontend/layout/blog_index_index.xml :

 <?xml version="1.0"?> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="2columns-left" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <referenceContainer name="content"> <block class="Toptal\Blog\Block\Posts" name="posts.list" template="Toptal_Blog::post/list.phtml" /> </referenceContainer> </body> </page>

Here, we must define three very important structures in the Magento layout structure: Blocks, Containers, and Templates.

  • Blocks are the ViewModel part of our MVVM architecture, which was explained in earlier sections. They are the building blocks of our template structure.

  • Containers contain and output Blocks. They hold blocks together in nice hierarchical structures, and help in making things make sense when the layout for a page is being processed.

  • Templates are PHMTL (mixed HTML and PHP) files used by a special type of block in Magento. You can make calls to methods of a $block variable from within a template. The variable is always defined in the template context. You will be invoking your Block's methods by doing so, and thus allowing you to pull information from the ViewModel layer to the actual presentation.

With that extra information at hand, we can analyze the XML layout structure above. This layout structure is basically telling Magento that, when a request is made to the blog/index/index route, a Block of the type Toptal\Blog\Block\Posts is to be added to the content container, and the template which will be used to render it is Toptal_blog::post/list.phtml .

This leads us to the creation of our two remaining files. Our Block, located at app/code/Toptal/Blog/Block/Posts.php :

<?php namespace Toptal\Blog\Block; use \Magento\Framework\View\Element\Template; use \Magento\Framework\View\Element\Template\Context; use \Toptal\Blog\Model\ResourceModel\Post\Collection as PostCollection; use \Toptal\Blog\Model\ResourceModel\Post\CollectionFactory as PostCollectionFactory; use \Toptal\Blog\Model\Post; class Posts extends Template { /** * CollectionFactory * @var null|CollectionFactory */ protected $_postCollectionFactory = null; /** * Constructor * * @param Context $context * @param PostCollectionFactory $postCollectionFactory * @param array $data */ public function __construct( Context $context, PostCollectionFactory $postCollectionFactory, array $data = [] ) { $this->_postCollectionFactory = $postCollectionFactory; parent::__construct($context, $data); } /** * @return Post[] */ public function getPosts() { /** @var PostCollection $postCollection */ $postCollection = $this->_postCollectionFactory->create(); $postCollection->addFieldToSelect('*')->load(); return $postCollection->getItems(); } /** * For a given post, returns its url * @param Post $post * @return string */ public function getPostUrl( Post $post ) { return '/blog/post/view/id/' . $post->getId(); } }

このクラスはかなり単純であり、その目的は、表示される投稿をロードし、テンプレートにgetPostUrlメソッドを提供することだけです。 ただし、注意すべき点がいくつかあります。

覚えているかと思いますが、 Toptal\Blog\Model\ResourceModel\Post\CollectionFactoryクラスは定義されていません。 Toptal\Blog\Model\ResourceModel\Post\Collectionのみを定義しました。 では、これはどのように機能していますか? モジュールで定義するクラスごとに、Magento2は自動的にファクトリを作成します。 ファクトリには2つのメソッドがありcreateは呼び出しごとに新しいインスタンスを返し、 getは呼び出されるたびに常に同じインスタンスを返します。これはシングルトンパターンの実装に使用されます。

ブロックの3番目のパラメーターである$dataは、オプションの配列です。 これはオプションであり、タイプヒントがないため、自動注入システムによって注入されません。 オプションのコンストラクターパラメーターは、常にパラメーターの最後に配置する必要があることに注意してください。 たとえば、親クラスであるMagento\Framework\View\Element\Templateのコンストラクターには、次のパラメーターがあります。

 public function __construct( Template\Context $context, array $data = [] ) { ...

Templateクラスを拡張した後にCollectionFactoryをコンストラクターパラメーターに追加したかったので、オプションのパラメーターのに追加する必要がありました。そうしないと、インジェクションが機能しませんでした。

 public function __construct( Context $context, PostCollectionFactory $postCollectionFactory, array $data = [] ) { ...

後でテンプレートからアクセスされるgetPostsメソッドで、 PostCollectionFactoryからcreateメソッドを呼び出すだけで、新しいPostCollectionが返され、データベースから投稿をフェッチして応答に送信できるようになります。

このルートのレイアウトを完成させるために、PHTMLテンプレートapp/code/Toptal/Blog/view/frontend/templates/post/list.phtmlます。

 <?php /** @var Toptal\Blog\Block\Posts $block */ ?> <h1>Toptal Posts</h1> <?php foreach($block->getPosts() as $post): ?> <?php /** @var Toptal\Blog\Model\Post */ ?> <h2><a href="<?php echo $block->getPostUrl($post);?>"><?php echo $post->getTitle(); ?></a></h2> <p><?php echo $post->getContent(); ?></p> <?php endforeach; ?>

ここで、モデルビューにアクセスするビューレイヤー( $block->getPosts() )を確認できます。このレイヤーは、ResourceModel(コレクション)を使用して、データベースからモデル( Toptal\Blog\Model\Post )をフェッチします。 すべてのテンプレートで、そのブロックのメソッドにアクセスするときはいつでも、 $block変数が定義され、呼び出しを待機します。

これで、ルートをもう一度押すだけで投​​稿リストを表示できるようになります。

投稿のリストを表示するインデックスページ

個々の投稿の表示

ここで、投稿のタイトルをクリックすると404が表示されるので、修正しましょう。 すべての構造が整っているので、これは非常に簡単になります。 以下を作成するだけで済みます。

  • blog/post/viewルートへのリクエストの処理を担当する新しいアクション
  • 投稿をレンダリングするためのブロック
  • ビュー自体を担当するPHTMLテンプレート
  • これらの最後の部分をまとめた、ブログ/投稿/表示ルートのレイアウトファイル。

新しいアクションは非常に簡単です。 リクエストからパラメータidを受け取り、それをMagentoコアレジストリに登録します。これは、単一のリクエストサイクル全体で利用できる情報の中央リポジトリです。 これにより、後でブロックでIDを使用できるようになります。 ファイルはapp/code/Toptal/Blog/Controller/Post/View.phpにあり、その内容は次のとおりです。

 <?php namespace Toptal\Blog\Controller\Post; use \Magento\Framework\App\Action\Action; use \Magento\Framework\View\Result\PageFactory; use \Magento\Framework\View\Result\Page; use \Magento\Framework\App\Action\Context; use \Magento\Framework\Exception\LocalizedException; use \Magento\Framework\Registry; class View extends Action { const REGISTRY_KEY_POST_; /** * Core registry * @var Registry */ protected $_coreRegistry; /** * @var PageFactory */ protected $_resultPageFactory; /** * @param Context $context * @param Registry $coreRegistry * @param PageFactory $resultPageFactory * * @codeCoverageIgnore * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( Context $context, Registry $coreRegistry, PageFactory $resultPageFactory ) { parent::__construct( $context ); $this->_coreRegistry = $coreRegistry; $this->_resultPageFactory = $resultPageFactory; } /** * Saves the blog id to the register and renders the page * @return Page * @throws LocalizedException */ public function execute() { $this->_coreRegistry->register(self::REGISTRY_KEY_POST_ID, (int) $this->_request->getParam('id')); $resultPage = $this->_resultPageFactory->create(); return $resultPage; } }

$coreRegistryパラメーターを__constructに追加し、後で使用するために属性として保存したことに注意してください。 executeメソッドでは、リクエストからidパラメータを取得し、登録します。 また、レジスタのキーとしてクラス定数self::REGISTRY_KEY_POST_IDを使用し、この同じ定数をブロックで使用して、レジストリ内のIDを参照します。

app/code/Toptal/Blog/Block/View.phpに、次の内容でブロックを作成しましょう。

 <?php namespace Toptal\Blog\Block; use \Magento\Framework\Exception\LocalizedException; use \Magento\Framework\View\Element\Template; use \Magento\Framework\View\Element\Template\Context; use \Magento\Framework\Registry; use \Toptal\Blog\Model\Post; use \Toptal\Blog\Model\PostFactory; use \Toptal\Blog\Controller\Post\View as ViewAction; class View extends Template { /** * Core registry * @var Registry */ protected $_coreRegistry; /** * Post * @var null|Post */ protected $_post = null; /** * PostFactory * @var null|PostFactory */ protected $_postFactory = null; /** * Constructor * @param Context $context * @param Registry $coreRegistry * @param PostFactory $postCollectionFactory * @param array $data */ public function __construct( Context $context, Registry $coreRegistry, PostFactory $postFactory, array $data = [] ) { $this->_postFactory = $postFactory; $this->_coreRegistry = $coreRegistry; parent::__construct($context, $data); } /** * Lazy loads the requested post * @return Post * @throws LocalizedException */ public function getPost() { if ($this->_post === null) { /** @var Post $post */ $post = $this->_postFactory->create(); $post->load($this->_getPostId()); if (!$post->getId()) { throw new LocalizedException(__('Post not found')); } $this->_post = $post; } return $this->_post; } /** * Retrieves the post id from the registry * @return int */ protected function _getPostId() { return (int) $this->_coreRegistry->registry( ViewAction::REGISTRY_KEY_POST_ID ); } }

ビューブロックで、保護されたメソッド_getPostIdを定義します。これは、コアレジストリから投稿IDを取得するだけです。 パブリックgetPostメソッドは、投稿を遅延読み込みし、投稿が存在しない場合は例外をスローします。 ここで例外をスローすると、Magentoにデフォルトのエラー画面が表示されます。このような場合、これは最善の解決策ではない可能性がありますが、簡単にするためにこの方法を維持します。

PHTMLテンプレートに移ります。 次の内容でapp/code/Toptal/Blog/view/frontend/templates/post/view.phtmlを追加します。

 <?php /** @var Toptal\Blog\Block\View $block */ ?> <h1><?php echo $block->getPost()->getTitle(); ?></h1> <p><?php echo $block->getPost()->getContent(); ?></p>

素晴らしくシンプルで、前に作成したViewブロックのgetPostメソッドにアクセスするだけです。

そして、すべてをまとめるために、 app/code/Toptal/Blog/view/frontend/layout/blog_post_view.xmlに次のコンテンツを含む新しいルートのレイアウトファイルを作成します。

 <?xml version="1.0"?> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="2columns-left" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <referenceContainer name="content"> <block class="Toptal\Blog\Block\View" name="post.view" template="Toptal_Blog::post/view.phtml" /> </referenceContainer> </body> </page>

これは以前と同じことをします。 Toptal_Blog::post/view.phtmlを関連付けられたテンプレートとして、 Toptal\Blog\Block\Viewcontentコンテナに追加するだけです。

実際の動作を確認するには、ブラウザでhttp://magento2.dev/blog/post/view/id/1にアクセスして、投稿を正常に読み込んでください。 次のような画面が表示されます。

個々の投稿を表示するためのページ

ご覧のとおり、初期構造を作成した後、プラットフォームに機能を追加するのは非常に簡単で、初期コードのほとんどはプロセスで再利用されます。

モジュールをすばやくテストしたい場合のために、これが私たちの作業の全体的な結果です。

ここからどこへ行くか

ここまでフォローしてくださった方、おめでとうございます! 私はあなたがMagento2開発者になるのにかなり近いと確信しています。 私たちはかなり高度なMagento2カスタムモジュールを開発しました。その機能はシンプルですが、多くの分野がカバーされています。

簡単にするために、この記事からいくつかのことを省略しました。 いくつか例を挙げると:

  • 管理者がフォームとグリッドを編集して、ブログコンテンツを管理します
  • ブログのカテゴリ、タグ、コメント
  • リポジトリと、確立できた可能性のあるいくつかのサービス契約
  • モジュールをMagento2拡張機能としてパッケージ化

いずれにせよ、ここにあなたがあなたの知識をさらに深めることができるいくつかの有用なリンクがあります:

  • Magento2に関するAlanStormブログ— Alan Stormは、Magentoの学習に関しておそらく最も教訓的なコンテンツを持っています。
  • アランケントのブログ
  • Magentoのドキュメント:Magento 2 Dev Docs

Magento 2でモジュールを作成する方法のすべての関連する側面と、必要に応じていくつかの追加リソースを包括的に紹介しました。 さて、コーディングを取得するか、検討したい場合はコメントに進んでください。