Drupal Backend Specialist
試験対策として順番にやっていく
1.1 Demonstrate knowledge of HTML, CSS and Javascript
飛ばす
1.2 Demonstrate knowlwdge of OO PHP programing concepts
ここも飛ばそう
1.3 Demonstrate knowledge of managing dependencies using Composer
- Drupal.org または Composer パッケージにないプロジェクトをダウンロード
- チルダとキャレット
- https://getcomposer.org/doc/articles/versions.md#next-significant-release-operators
-
~
-
~1.2
は>=1.2 <2.0.0
と同じ -
~1.2.3
は>=1.2.3 <1.3.0
- チルダは細かい設定
-
-
^
-
^1.2.3
は>=1.2.3 <2.0.0
と同じ - キャレットはどんぶり勘定
-
1.4 Demonstrate the use of Git for version control¶
一旦飛ばそう
雨がすごい
一旦終わり。次は↓から
1.5 Demonstrate knowledge of Automated Testing concepts
Test がよく分かってないまま PHPUnit のマニュアル読んだら意味不明だったので、
動画を見る。
PHPUnit ブラウザテストチュートリアル
- BrowserTestBase は Webベースの動作と相互作用をテストする方法を提供する
- 例えばアクセスチェック
- Drupal のブラウザテスト
- 仮想 Web ブラウザを使用して Drupal をインストール
- テスト用に0からのためユーザー等も一切存在しない
- コアモジュール以外も有効になっていない
実行するための設定が必要になる
実行するための設定
以下動画の内容が続く
- Drupal では PHPUnit というテストフレームワークをサポート
- テストスイートが5種類用意されている
ソフトウェアテストの目的や対象ごとに複数のテストケースをまとめたもの
- Unit: DB, 構成データを使わない。モックで対応可能。クラスや関数単位で行う。
- Kernel: DB や 構成データを使うが Webブラウザは不要。
- Functional: Webブラウザを使用したテスト。
- FunctionalJavaScript: WebブラウザとJS を使用したテスト。
- Build: ビルドプロセスとその結果に対してテストを行う。
導入方法
composer require drupal/core-dev --dev --update-with-all-dependencies
ローカルで composer 使えない
治す方法があった。
時間がかかるので動画みておく
- インストール
composer require drupal/core-dev --dev --update-with-all-dependencies
- version 確認
./vendor/bin/phpunit --version
- PHPUnit設定ファイルを用意
-
cp app/core/phpunit.xml.dist phpunit.xml
- app/core 下にコピーすると楽
-
../../vendor/bin/phpunit -c phpunit.xml --testsuite unit
等で実行
書いてみよう
- ルール
- {モジュール名}/tests/src/{testsuit}/xxxTest.php
- 名前空間
- Drupal\Tests{モジュール名}{testsuit}\xxxTest
- 例) Drupal\Tests\marucha\Unit\Controller\MaruchaControllerTest
- テストクラスは使用するテストスイートが用意する基底クラスを拡張する
- テストメソッドは test から始まる public メソッド
- docblock でテストに関する情報を記載
カテゴリ | 基底クラス |
---|---|
Unit | \Drupal\Tests\UnitTestCase |
Kernel | \Drupal\KernelTests\KernelTestBase |
Functional | \Drupal\Tests\BrowserTestBase |
FunctionalJavaScript | \Drupal\FunctionalJavaScriptsTests\WebDriverTestBase |
Build | \Drupal\BuildTests\Framework\BuildTestBase |
ディレクトリは module/tests/src/{testunit}
- 読む
- https://www.drupal.org/node/2116263
- ほとんど動画で網羅されていた
テストはローカルでは成功するが、drupal.org の testbot で実行すると失敗する
- testbot 依存関係を認識していない
- info.yml へ test_dependencies プロパティを設定する
- 最大24hかかる
- 開発バージョンを使用している
-
__DIR__
UnitTestCase::$root
のルートが間違っている- UnitTestCase::__construct でオーバーライドできる
- info.yml へ test_dependencies プロパティを設定する
寄り道したけどなんとなく理解できてきた。
PHPUnit ブラウザテストチュートリアルに戻る
- setUp() でテスト対象クラスのインスタンス化ができる
- テストの前処理を行う
- オプション
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
// Create an article content type that we will use for testing.
$type = $this->container->get('entity_type.manager')->getStorage('node_type')
->create([
'type' => 'article',
'name' => 'Article',
]);
$type->save();
$this->container->get('router.builder')->rebuild();
}
次は
から
PHPUnit ファイル構造、名前空間、および必要なメタデータ
動画で網羅されていた
以降読んだけど、実際使ってないとよくわからない印象。読むだけ読んだ。
2.1 Demonstrate an ability to register paths to define how URL requests are handled in Drupal using Routing system and Menu API¶
- デザインパターンとアーキテクチャ
Factory
オブジェクトの生成を担う
- 2つの利点
- Automoble クラスを変更、置換がある場合ファクトリ内のコードを変更するだけで良い
- オブジェクトの作成が複雑の場合、新しいインスタンスを作成するたびに繰り返すのではなく、すべての作業をファクトリで実行できる
interface Vehicle {
public function drive();
}
// Car クラス
class Car implements Vehicle {
public function drive() {
echo "Driving a car...\n";
}
}
// Truck クラス
class Truck implements Vehicle {
public function drive() {
echo "Driving a truck...\n";
}
}
// VehicleFactory クラス
class VehicleFactory {
public static function createVehicle(string $type): Vehicle {
switch ($type) {
case 'car':
return new Car();
case 'truck':
return new Truck();
default:
throw new Exception("Invalid vehicle type specified.");
}
}
}
$car = VehicleFactory::createVehicle('car');
$car->drive(); // Driving a car...
$truck = VehicleFactory::createVehicle('truck');
$truck->drive(); // Driving a truck...
Singleton
- 特定のクラスの1つのインスタンスのみにアクセスできるようにする
- インスタンスが1つであることを保証する
- 依存性注入を使用する方が多い(?)
class Singleton {
private static $singleton;
// private にする. new できない.
private function __construct() {
echo ;
}
public static function getInstance() {
if (!isset(self::$singleton)) {
self::$singleton = new Singleton();
}
return self::$singleton;
}
}
Strategy
-
柔軟性と拡張性の高さ: ストラテジーパターンを使用することで、アルゴリズムを定義し、それらをカプセル化し、お互いに置換可能にすることができます。これにより、クライアントコードは、アルゴリズムを変更する必要がある場合に、そのコードを変更することなく、異なるアルゴリズムを使用できるようになる。
-
再利用性の高さ: ストラテジーパターンでは、複数のクラスが同じインターフェースを実装するため、コードの再利用性が高まりまる。
-
テスト容易性の向上: ストラテジーパターンでは、アルゴリズムを単一のクラスにカプセル化するため、個々のアルゴリズムのテストが容易になる。
-
アルゴリズムの切り替えの容易さ: ストラテジーパターンでは、クライアントコードは、実行時に異なるアルゴリズムを切り替えることができる。これにより、アルゴリズムの変更によるシステムへの影響を最小限に抑えることができる。
-
アルゴリズムの比較の容易さ: ストラテジーパターンでは、複数のアルゴリズムが同じインターフェースを実装するため、アルゴリズム間の比較が容易。
interface PaymentMethod {
public function pay($amount);
}
class CreditCardPayment implements PaymentMethod {
public function pay($amount) {
// クレジットカードで支払いを行う
}
}
class BankTransferPayment implements PaymentMethod {
public function pay($amount) {
// 銀行振込で支払いを行う
}
}
class PaypalPayment implements PaymentMethod {
public function pay($amount) {
// Paypalで支払いを行う
}
}
class Payment {
private $paymentMethod;
public function setPaymentMethod(PaymentMethod $paymentMethod) {
$this->paymentMethod = $paymentMethod;
}
public function pay($amount) {
$this->paymentMethod->pay($amount);
}
}
// 使用例
$payment = new Payment();
// クレジットカードで支払い
$creditCardPayment = new CreditCardPayment();
$payment->setPaymentMethod($creditCardPayment);
$payment->pay(1000);
// 銀行振込で支払い
$bankTransferPayment = new BankTransferPayment();
$payment->setPaymentMethod($bankTransferPayment);
$payment->pay(2000);
// Paypalで支払い
$paypalPayment = new PaypalPayment();
$payment->setPaymentMethod($paypalPayment);
$payment->pay(3000);
Front Controller
index.php のように単一の入り口を持つ
Dependency Injection
- 可読性の工場
- テストの用意下
- 再利用性の向上
class Database {
protected $adapter;
public function __construct(MySqlAdapter $adapter) {
$this->adapter = $adapter;
}
}
class MysqlAdapter {}
S.O.L.I.D.
- S: 単一責任の原則 (Single Responsibility Principle)
- O: オープン・クローズドの原則 (Open/Closed Principle)
- 拡張用にオープンにする必要はあるが、変更用にはクローズ
- 新しいクラスが必要な場合に、既存のコードを変更するのではなく、既存のコードで使用される新しいコードを作成するように設計する
- L: リスコフの置換原則 (Liskov Substitution Principle)
- 子クラスは親クラスの型定義を壊してはならない
- I: インターフェース分離の原則 (Interface Segregation Principle)
- D: 依存性逆転の原則 (Dependency Inversion Principle)
info.yml に必須
- name
- type
- core_version_requirement
モジュールの命名
- 文字で始まる
- 小文字、数字、アンダースコア
- スペースはだめ
- 50文字以内
コントローラーとルーティング
やるのみ。
example.my_page:
path: '/mypage/page'
defaults:
_controller: '\Drupal\example\Controller\ExampleController::myPage'
_title: 'My first page in D8'
requirements:
_permission: 'access content'
<?php
namespace Drupal\example\Controller;
use Drupal\Core\Controller\ControllerBase;
/**
* Provides route responses for the Example module.
*/
class ExampleController extends ControllerBase {
/**
* Returns a simple page.
*
* @return array
* A simple renderable array.
*/
public function myPage() {
return [
'#markup' => 'Hello, world',
];
}
}
カスタムブロック作るのみ
- blockForm() でフォーム作成
- blockSubmit() で保存
public function blockSubmit($form, FormStateInterface $form_state) {
$this->configuration['hello_block_name'] = $form_state->getValue('hello_block_name');
}
// フィールドセットラッパーがあったら配列で
$this->configuration['hello_block_name'] = $form_state->getValue(['myfieldset', 'hello_block_name']);
構成として保存された値を使用
$config = $this->getConfiguration();
if (!empty($config['hello_block_name'])) {
$name = $config['hello_block_name'];
}
喉が痛い
カスタムフィールドの作成
先にプラグイン API を読む
プラグインAPI
プラグインの3つの要素
- プラグインタイプ
- プラグインタイプはこのタイプのプラグインがどのように発見され、インスタンス化されるかを定義する中央制御クラス
- キャッシュバックエンド, イメージアクション, ブロックなどのすべてのプラグインの中心的な目的を記述
- プラグインディスカバリー
- 利用可能なコードベースの中から、特定のプラグインタイプのユースケースで使用するのに適したプラグインを見つけるプロセス
- プラグインファクトリー
- インスタンス化するところ
さらに色々役立つコンポーネントもある。
- プラグインデリバティブ
- 1つのプラグインが多数のプラグインの代わりに機能
- 管理者の負担を減らすために部分的に構成されたファーストクラスのプラグインとして提供
- 複数のプラグインを1つのプラグインの変わりに表示することで、UIをサポートしてヘルプテキストもレンダリングできる
- ディスカバリーデコレーター
- 既存のディスカバリーメソッドをラップする方法
- CoreではcacheDecoratorを提供して、プラグインタイプのディスカバリープロセスをキャッシュ
- プラグインマッパー
- 文字列を特定のプラグインインスタンスにマップ
- 開発者がプラグインインスタンスを手動でインスタンス化と構成する必要がない
返信機能確認
独自のプラグインタイプを作成
- プラグインマネージャクラスを用意
- クラス内で検出方法を定義
- services.yml に登録
- アノテーションクラス
- プラグインで利用したいメタデータを定義
- インターフェースを用意
- プラグインで実装して欲しいメソッドを定義
プラグインマネージャ
- 新しいプラグインタイプの定義
- プラグインの検出方法の定義
- 検出とインタンス化
サービスとして定義されている
プラグインを選ぶ理由
なぜプラグイン
- インターフェイスの検出、メタデータの処理、プラグインクラスのファクトリを提供
プラグインかタグ付きサービスか
- ユーザーが動作を選択、構成する必要があるときはプラグイン
- ユーザーの操作が不要な場合はタグ付きサービス
アノテーションベースのプラグイン
コアによって提供されるプラグインタイプの例
- ブロック
- フィールドフォーマッタ、フィールドウィジェット
- ビュー
- コンディション
なぜアノテーション
- 複雑なデータの構造化が可能になった
- プラグイン検出に以前まではメモリがめっちゃ使われていた
- 各クラスに getInfo() メソッドがあり、各クラスをロードする必要があった。
- リクエストが終了するまでメモリが開放されない
- アノテーションを解析する実装はPHPファイルとして含めずトークン化するためメモリを抑えられる
例
/**
* Checks if a user name is unique on the site.
*
* @Constraint(
* id = "UserNameUnique",
* label = @Translation("User name unique", context = "Validation"),
* )
*/
class UserNameUnique extends Constraint {
}
アノテーションの構文
- プラグインIDから始める必要がある
- 二重引用符
- 1重引用符は例外をスロー
- 定数
- 使用可能な型
- 文字列: 二重引用符を文字列として扱うとき
"The ""On"" value"
- 数値: 引用符を使用しない
- 真偽値 引用符を使用しない
- リスト
- 文字列: 二重引用符を文字列として扱うとき
base = {
"node",
"foo",
}
- マップ
edit = {
"editor" = "direct",
}
独自のプラグインタイプでアノテーションを使用する
- AnnotatedClassDiscovery を使用する DefaultPluginManager を拡張するだけ
- DefaultPluginManager コンストラクタの第一引数は 名前空間
独自のプラグインマネージャの作成
特定のタイプのプラグインを検出してインスタンス化する方法を定義する中央制御クラス。
プラグインマネージャーの定義
- 検出方法を定義する
- ファクトリを定義する
DefaultPluginManager を基本クラスとして使用する
ディスカバリーデコレーター
- ディスカバリクラスをラップ
Drupal プラグインディスカバリー
Drupal が特定のプラグインタイプを見つけるプロセス
4つの異なるコアディスカバリータイプがある
- StaticDiscovery
- プラグインを直接登録できる
- \Drupal::service()のように呼び出せる
- フックディスカバリー
- AnnotatedClassDiscovery
- アノテーションを使用して検出できる
- Yaml ディスカバリー
- プラグインを yml ファイルで定義
次は
からプラグインの定義
- UIをコンポーネント化する役割を持つ
- それ以外にも様々な状況でコンポーネントが使用されるため、メタ情報または定義を使用して容易に関連付けができるようになっている
プラグインコンテキスト
- 例えばプラグインがブロックとして表示される場合、そのブロックが表示されるページの URL、言語、ユーザーのロールなどがコンテキストといえる
- 各コンテキストに対して名前、説明、タイプ、オプション等を定義するために使用
- 別のオブジェクトを注入する手段
プラグインデリバティブ
公式分からない。
を確認
1 つのプラグインクラスから複数のプラグインを生成するための仕組み
- 通常1:1 であるが、1:nを実現する仕組みのこと
あるタイプのエンティティが増えるとそれに伴ってプラグインが動的に生成される
- view 等が例
カスタムフィールドに戻る
作成する
-
必須
- ID
- ラベル
- デフォルトフォーマット
-
FieldItemInterface を実装
- それを実装しているFieldItemBaseを拡張
class BazItem extends FieldItemBase {}
-
FieldItemInterface::schema をオーバーライド
- フィールドの値を格納
-
propertyDefinitions
- バリデーション、プロパティを設定
-
isEmpty()
- 空かどうか
フィールドの設定
-
defaultFieldSettings
- デフォルトの設定
-
スキーマを作成
- defaultFieldSettings で設定したスキーマ
[MODULE ROOT]/config/schema/[MODULE_NAME].schema.yml
- defaultFieldSettings で設定したスキーマ
field.field_settings.[FIELD ID]:
type: mapping
label: 'FIELDNAME settings'
mapping:
size:
type: string
label: 'Size'
- fieldSettingsForm
- ユーザーが設定を変更できるフォームを提供
カスタムウィジェット
フォーム内のフィールドをレンダリングするために使用される
formElement()
が必須
-
defaultSettings()
- デフォルトの設定
-
スキーマを作成
/[MODULE_NAME]/config/schema/[MODULE_NAME].schema.yml
field.widget.settings.[WIDGET ID]:
type: mapping
label: 'WIDGET NAME widget settings'
mapping:
size:
type: integer
label: 'Size'
- settingsForm()
- ユーザーが設定できるフォーム作成
フォーマッター
エンドユーザー用にフォーマットする
-
defaultSettings()
- デフォルト設定
-
スキーマ作成
[MODULE ROOT]/config/schema/[MODULE_NAME].schema.yml
field.formatter.settings.[FORMATTER ID]:
type: mapping
label: 'FORMATTER NAME text length'
mapping:
text_length:
type: string
label: 'Text Length'
- settingsForm()
- ユーザーが設定を変更できるようにする
フォーマッタでDI
- ContainerFactoryPluginInterface() を実装
- create() を実装 or オーバーライド
- コンストラクタを実装 or オーバーライド
イベントサブスクライバ
読んで、やる
ビューズのスタイルプラグイン
次はここから
Form API
再確認のため見る
フォームの種類
- FormBase
- 普通のフォーム
- ConfigFormBase
- 構成を保存するフォーム
- ConfirmFormBase
- ユーザーへの確認画面
実装する
states 確認
否定も使える
$form['colour_select'] = [
'#type' => 'radios',
'#title' => $this->t('Pick a colour'),
'#options' => [
'black' => $this->t('Black'),
'other' => $this->t('Other'),
],
'#attributes' => [
'id' => 'field_colour_select',
],
];
$form['custom_color'] = [
'#type' => 'textfield',
'#size' => '60',
'#placeholder' => 'Enter favourite colour',
'#attributes' => [
'id' => 'custom-colour',
],
'#states' => [
'visible' => [
':input[id="field_colour_select"]' => ['!value' => 'other'],
],
],
];
Entity API
まず動画確認
以下動画の内容
Drupal で扱われるデータ
- Content: サイト訪問者に表示するテキスト、画像など
- Configuration: サイトの動作、表示方法を定義する情報
- State: cron が最後に実行された時刻等サイトの現在の状態に関する一時的な性質
- Session: ログインしているかどうか, Cookie等個々のサイト訪問者のサイトとのやり取り
Content ち Configuration が エンティティ
エンティティとは
- コンテンツ, 構成の永続的なストレージに使用されるオブジェクト
- データを扱う方法の1つ, データやコンテンツのモデリングを行う
- グルーピングはプラグインシステムによって定義される
エンティティの用語
- エンティティ: 構造化された単一のアイテム(インスタンス)
- 1つの記事、1つのユーザ
- エンティティタイプ: グルーピングしたもの。エンティティタイプはEntityTypeManager によって定義されるプラグインタイプのプラグイン
- コンテンツ、タクソノミー
- バンドル: サブタイプ。任意。
- 記事コンテンツタイプ、タグ
構成エンティティとコンテンツエンティティ
- エンティティタイプを定義するアノテーションを見て判別できる
- ContentEntityType または ConfigEntityType
イントロ
読む
タイプ
API の操作
バンドル
次は
からとりあえず全部読んだ
api reference が2つもある。
大事なことだからか..
Section 3: Debug code and troubleshooting
次は
からとりあえず一周した。理解は半分ぐらい。
とりあえず落ちた。ぼちぼち復習やっていく。