shirane lab - personal notes on drupal & web application https://www.white-root.com/ ja Windows 版の MariaDB を ZIP パッケージからインストールしてみた https://www.white-root.com/blog/1610936934 <span>Windows 版の MariaDB を ZIP パッケージからインストールしてみた</span> <span><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">shirane</span></span> <span>2021/01/18(月) - 11:28</span> <div class="w3-section field field--name-body field--type-text-with-summary field--label-hidden w3-bar-item field__item"><p>Windows 版の MariaDB を、.msi ファイルのインストーラではなく .zip ファイルからインストールしてみたので、そのときの手順をメモしておく。</p> <h2>パッケージの入手</h2> <p><a href="https://downloads.mariadb.org/" target="_blank">MariaDB のダウンロードページ</a>から各 OS 向けのソース、バイナリ、パッケージがダウンロードできる。今回は 10.5.8 Stable の Windows 向け、ZIP ファイル、64-bit 版を、台湾のミラーサーバーから取得した。</p> <ul> <li><a href="https://downloads.mariadb.org/interstitial/mariadb-10.5.8/winx64-packages/mariadb-10.5.8-winx64-debugsymbols.zip/from/https%3A//ftp.ubuntu-tw.org/mirror/mariadb/">mariadb-10.5.8-winx64-debugsymbols.zip</a></li> </ul> <p>念のため、<a href="https://docs.microsoft.com/ja-jp/windows-server/administration/windows-commands/certutil#-hashfile" target="_blank">certutil コマンドの -hashfile オプションで</a>チェックサムが一致することを確認しておこう。</p> <p><img alt="ダウンロードするファイルのチェックサム" data-entity-type="file" data-entity-uuid="insert-image-f448fb81-a19d-4436-9a23-ea92f35bb324" data-insert-attach="{&quot;id&quot;:&quot;f448fb81-a19d-4436-9a23-ea92f35bb324&quot;,&quot;attributes&quot;:{&quot;alt&quot;:[&quot;alt&quot;,&quot;description&quot;],&quot;title&quot;:[&quot;title&quot;]}}" data-insert-class="" data-insert-type="image" height="376" src="/system/files/2021-01/mdcheck.png" width="476" /></p> <pre> <code class="language-bash">&gt;certutil -hashfile mariadb-10.5.8-winx64.zip md5 MD5 ハッシュ (対象 \mariadb-10.5.8-winx64.zip): b823284d4189299766c1e8a8fdeefeba CertUtil: -hashfile コマンドは正常に完了しました。 &gt;certutil -hashfile mariadb-10.5.8-winx64.zip sha1 SHA1 ハッシュ (対象 \mariadb-10.5.8-winx64.zip): 82d109b16d87488277f9160da7418848f777a87a CertUtil: -hashfile コマンドは正常に完了しました。 &gt;certutil -hashfile mariadb-10.5.8-winx64.zip sha256 SHA256 ハッシュ (対象 \mariadb-10.5.8-winx64.zip): 0cb42847637a321740b4776b630a0ab9fea226834db17923b3c8bef341c6b89d CertUtil: -hashfile コマンドは正常に完了しました。 &gt;certutil -hashfile mariadb-10.5.8-winx64.zip sha512 SHA512 ハッシュ (対象 \mariadb-10.5.8-winx64.zip): 86430b1bef8ad3d5b0c14eeaf47987787d2a0de231717d0634aa0e6e9bd9a0a4ebab986a77eb06839965afc9f2d3edc3975c2aac0a6363cb0291d8f0f665a8a5 CertUtil: -hashfile コマンドは正常に完了しました。 </code></pre> <p>問題なさそうだ。</p> <h2>展開とインストール</h2> <p>続いて、<a href="https://mariadb.com/kb/en/installing-mariadb-windows-zip-packages/" target="_blank">本家の資料</a>に従ってインストールを実行する。</p> <p>まず、取得した ZIP ファイルを展開し、フォルダを適切な場所に配置する。ここでは、<strong><code>C:\mariadb</code></strong> に展開したとしよう。</p> <p>次に、mysql_install_db.exe という付属ツールを実行して、データベースの構成と Windows サービスとしての登録を行う。この際、データの保管場所、登録するサービスの名前、作成する MariaDB インスタンスの管理者(root)のパスワードをオプションとして指定する。</p> <p>たとえば、データの保管場所を <strong><code>C:\mariadb\data</code></strong>、サービス名を <strong><code>MariaDB</code></strong>、root パスワードを <strong><code>root</code></strong> とするには、次のコマンドラインを使用する。</p> <pre> <code class="language-bash">&gt;C:\mariadb\bin\mysql_install_db.exe --datadir=C:\mariadb\data --service=MariaDB --password=root Running bootstrap 2021-01-18 12:21:20 0 [Note] C:\mariadb\bin\mysqld.exe (mysqld 10.5.8-MariaDB) starting as process 1488 ... Removing default user Setting root password Creating my.ini file Registering service 'MariaDB' Creation of the database was successful </code></pre> <p>なお、上記はサービスの登録を行うので「<a href="https://www.google.com/search?ei=gf0EYOztPIPg-Abh54nYDw&amp;q=%E7%AE%A1%E7%90%86%E8%80%85%E3%81%A8%E3%81%97%E3%81%A6%E5%AE%9F%E8%A1%8C+windows+10&amp;oq=%E7%AE%A1%E7%90%86%E8%80%85%E3%81%A8%E3%81%97%E3%81%A6%E5%AE%9F%E8%A1%8C+windows+10&amp;gs_lcp=CgZwc3ktYWIQAzIECAAQHjIGCAAQCBAeMgYIABAIEB4yBggAEAgQHjIGCAAQCBAeMgYIABAIEB4yBggAEAgQHjIGCAAQCBAeOgQIABBHOgIIADoICAAQCBAEEB46BggAEAUQHlCqf1jVjQFgr48BaABwAngAgAHCAYgB9wqSAQQwLjExmAEAoAEBqgEHZ3dzLXdpesgBCMABAQ&amp;sclient=psy-ab&amp;ved=0ahUKEwissuOpwqTuAhUDMN4KHeFzAvsQ4dUDCA0&amp;uact=5" target="_blank">管理者として実行</a>」する必要がある。</p> <p>正常に実行されると、指定したフォルダにデータベースの実体が作られ、同じ場所に最小限の my.ini ファイルも作成される。</p> <p>さらに、指定した名前で MairiaDB のサービスも登録される。</p> <p><a href="/system/files/2021-01/service1.png" target="_blank"><img alt="登録された MairaDB のサービス" data-entity-type="file" data-entity-uuid="insert-image-4f6def60-e0fc-47f7-a9ed-fede3d5b3226" data-insert-attach="{&quot;id&quot;:&quot;4f6def60-e0fc-47f7-a9ed-fede3d5b3226&quot;,&quot;attributes&quot;:{&quot;alt&quot;:[&quot;alt&quot;,&quot;description&quot;],&quot;title&quot;:[&quot;title&quot;]}}" data-insert-class="" data-insert-type="image" height="371" src="/system/files/2021-01/service1.png" width="1109" /></a></p> <p>スタートアップの種類は「自動」で設定される。</p> <p><a href="/system/files/2021-01/service2.png" target="_blank"><img alt="スタートアップで「自動」で登録される" data-entity-type="file" data-entity-uuid="insert-image-d8fafa6b-269c-4372-b6ec-ea129a06a455" data-insert-attach="{&quot;id&quot;:&quot;d8fafa6b-269c-4372-b6ec-ea129a06a455&quot;,&quot;attributes&quot;:{&quot;alt&quot;:[&quot;alt&quot;,&quot;description&quot;],&quot;title&quot;:[&quot;title&quot;]}}" data-insert-class="" data-insert-type="image" height="533" src="/system/files/2021-01/service2.png" width="472" /></a></p> <p>あとは、サービスを起動すれば使えるようになる。管理コンソールから起動しても良いし、次のコマンドラインを使用することもできる。</p> <pre> <code>&gt;sc start MariaDB SERVICE_NAME: MariaDB TYPE : 10 WIN32_OWN_PROCESS STATE : 2 START_PENDING (NOT_STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN) WIN32_EXIT_CODE : 0 (0x0) SERVICE_EXIT_CODE : 0 (0x0) CHECKPOINT : 0x3 WAIT_HINT : 0x3a98 PID : 892 FLAGS : </code></pre> <p>「自動」スタートアップのサービスとして登録されているので、次回のコンピュータ再起動以降は、すぐに MariaDB が使える状態になる。</p> <p>なお、実際に使うには、動かすアプリケーションの要件に合わせて my.ini を調整する必要があるだろう。たとえば、デフォルトの文字セットを utf8mb4 にするのであれば、次の設定(当該行のみ抜粋)を追加してサービスを再起動する。</p> <pre> <code class="language-ini">[mysqld] character-set-server = utf8mb4 collation_server=utf8mb4_unicode_ci [client] default_character_set = utf8mb4 </code></pre> <h2>アンインストール</h2> <p>登録したサービスとデータベースを削除するには、次のコマンドラインが使用できる。</p> <pre> <code>sc stop MariaDB sc delete MariaDB rmdir /s /q C:\mariadeb\data</code></pre> <h2>参考資料</h2> <ul> <li><a href="https://mariadb.com/kb/en/installing-mariadb-windows-zip-packages/" target="_blank">Installing MariaDB Windows ZIP Packages</a></li> <li><a href="https://mariadb.com/kb/en/mysql_install_dbexe/" target="_blank">mysql_install_db.exe</a></li> <li><a href="https://mariadb.com/kb/en/installing-system-tables-mysql_install_db/" target="_blank">Installing System Tables (mysql_install_db)</a></li> </ul> </div> <div class="w3-section field field--name-field-tag field--type-entity-reference field--label-inline"> <label class="field__label">Tag</label> <div class="field__items"> <div class="w3-bar-item field__item"><a href="/taxonomy/term/6" hreflang="ja">開発環境</a></div> <div class="w3-bar-item field__item"><a href="/taxonomy/term/7" hreflang="ja">Windows</a></div> <div class="w3-bar-item field__item"><a href="/taxonomy/term/58" hreflang="ja">MariaDB</a></div> </div> </div> Mon, 18 Jan 2021 02:28:54 +0000 shirane 160 at https://www.white-root.com Drush 出力の形式、フィールド、絞り込みについて https://www.white-root.com/blog/output-formats-filters <span>Drush 出力の形式、フィールド、絞り込みについて</span> <span><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">shirane</span></span> <span>2021/01/17(日) - 11:41</span> <div class="w3-section field field--name-body field--type-text-with-summary field--label-hidden w3-bar-item field__item"><p>この記事は<a href="https://www.drush.org/latest/output-formats-filters/" target="_blank">「Output Formats, Fields and Filters」</a>(drush.org)の拙訳です。</p> <hr /> <p>Drush は、書式設定とフィルタ処理の強力なシステムを利用している。このシステムにより、さまざまなコマンドからの出力を柔軟に制御することができる。</p> <ul> <li>出力形式のオプションを使用して、表示されるデータのタイプを選択できる。たとえば、多くのコマンドが、人間が読みやすいテーブル形式か、yaml や json など機械処理に適した形式かを選択できるようにしている。</li> <li>出力フィールドのオプションを使用して、出力されるデータの列やその順序を指定できる。</li> <li>出力フィルタのオプションを使用して、論理式に基づいて表示されるデータ行を絞り込むことができる。</li> </ul> <h2>出力形式</h2> <p><strong>--format</strong> オプションを使用すると、コマンドの出力を表示するデータ形式を選択できる。オブジェクトやシステムに関する情報の出力を生成する大半のコマンドは、それらのデータを別の形式に変換できる。たとえば、<a href="/drush-commands/version">version コマンド</a>の出力は、人間が見やすいテーブル形式(デフォルト)か、json 配列で表示することができる。</p> <pre> <code class="language-bash">$ drush version Drush version : 10.3.1 $ drush version --format=json { "drush-version": "10.3.1" }</code></pre> <p>利用可能な出力形式は、各コマンドの <a href="/drush-commands/help">help</a> で確認できる。</p> <pre> <code class="language-bash">$ drush help version Show drush version. Options: --format=&lt;json&gt; Select output format. Available: json, string, var_export, yaml. Default is key-value.</code></pre> <h2>出力フィールド</h2> <p>コマンドから出力される列を限定したい場合は、<strong>--fields</strong> オプションを使用する。表示するフィールドを、表示したい順序で列挙すればよい:</p> <pre> <code class="language-bash">$ drush views:list --fields=machine-name,status +-------------------+----------+ | Machine name | Status | +-------------------+----------+ | block_content | Enabled | | comment | Enabled | | comments_recent | Enabled | | content | Enabled | | content_recent | Enabled | | files | Enabled | | frontpage | Enabled | | taxonomy_term | Enabled | | user_admin_people | Enabled | | watchdog | Enabled | | who_s_new | Enabled | | who_s_online | Enabled | | archive | Disabled | | glossary | Disabled | +-------------------+----------+</code></pre> <p>指定できるフィールド名は、<a href="/drush-commands/help">help</a> 出力中に表示される。</p> <pre> <code class="language-bash">$ drush help views:list Get a list of all views in the system. Options: --fields=FIELDS Available fields: Machine name (machine-name), Name (label), Description (description), Status (status), Tag (tag) [default: "machine-name,label,description,status"] </code></pre> <p>フィールドは、英語の表示名でも(カッコ内に表示される)内部的なマシン名でも、どちらでも指定できる。</p> <p>利用可能なすべてのデータ列が、デフォルトでは表示されないコマンドもある。すべてのフィールドを表示するには、<strong>--fields=*</strong> を指定する。</p> <p>単一形式の <strong>--field</strong> オプションもある。この形式を使用すると、出力の形式は string(文字列)となる。</p> <pre> <code class="language-bash">$ drush views:list --field=machine-name block_content comment comments_recent content content_recent files frontpage taxonomy_term user_admin_people watchdog who_s_new who_s_online archive glossary</code></pre> <h2>出力フィルタ</h2> <p>テーブル形式でデータ出力する多くのコマンドが <strong>--filter</strong> オプションをサポートしている。これを利用すると、簡潔な論理式を使用して、出力される行を選択することができる。</p> <p>最もシンプルな形式では、<strong>--filter</strong> オプションは、コマンドが持つデフォルトのフィルタ フィールドで絞り込みを行う条件を表す。たとえば、<a href="/drush-commands/role_list">role:list</a> コマンドの場合、デフォルト フィルタが <strong>perms</strong> となっているため、<strong>--filter</strong> オプションで指定した権限を持つロールだけに出力を絞り込むことができる。</p> <pre> <code class="language-bash">$ drush role:list --filter='post comments' authenticated: label: 'Authenticated user' perms: - 'access comments' - 'access content' - 'access shortcuts' - 'access site-wide contact form' - 'access user contact forms' - 'post comments' - 'search content' - 'skip comment approval' - 'use text format basic_html'</code></pre> <p>なお、すべてのコマンドにデフォルトのフィルタ フィールドがあるわけではない。</p> <p><strong>--filter</strong> で簡単な式を使用することで、出力に含まれる別のフィールドを検索することもできる。たとえば、有効になっている拡張コンポーネント(モジュールやテーマ)だけを <a href="/drush-commands/pm_list">pm:list</a> コマンドで列挙するには、次のコマンドを使用する:</p> <pre> <code class="language-bash">$ drush pm:list --filter='status=enabled'</code></pre> <p>部分文字列で検索を行うには演算子 <strong>*=</strong> を使用する。また、正規表現にマッチするものを検索するには <strong>~=</strong> 演算子を使用する。たとえば、マシン名に "content" という語が含まれるビューを検索するには、次のコマンドを使用する:</p> <pre> <code class="language-bash">drush views:list --filter='machine-name*=content'</code></pre> <p>コアのステータス通知の中から、タイトルに "php" か "gd" のどちらかが含まれるものを正規表現で見つけ出すには、次のコマンドを使用する:</p> <pre> <code class="language-bash">drush core:requirements --filter='title~=#(php|gd)#i'</code></pre> <p>最後に、フィルタ式では、論理積(&amp;&amp;)や論理和(||)の各演算子を使用して、複数の条件を連結することもできる。カッコはサポートされない。たとえば、<strong>title</strong> と <strong>severity</strong> の両フィールドを <a href="/drush-commands/core_requirements">core:requirement</a> コマンドで検索するには、次のコマンドを使用する:</p> <pre> <code class="language-bash">drush core:requirements --filter='title~=#(php|gd)#i&amp;&amp;severity=warning'</code></pre> <p><strong>=</strong> と <strong>*=</strong> の各演算子は常に大文字と小文字を区別しない。<strong>~=</strong> 演算子は、上の例で示したように、<a href="https://www.php.net/manual/ja/reference.pcre.pattern.modifiers.php" target="_blank">PCRE パターン修飾子</a>の i を使用しない限り大文字と小文字を区別する。</p> <h2>フィルタと grep との比較</h2> <p><strong>--filter</strong> の機能は <strong>grep</strong> コマンドと似ている。主な違いは、フィルタ機能ではセマンティックな検索(明示的に特定フィールドのデータと比較を行う)ができることだ。対照的に、grep コマンドは行に基づく検索を行う。</p> <p>severity が <em>warning</em> である結果だけを表示するには、次のコマンドを使用する:</p> <pre> <code class="language-bash">drush core:requirements --filter='severity=warning'</code></pre> <p>文字列 <em>warning</em> が(severty フィールドでも行内のどこか別の場所でも)含まれる行だけを表示するのであれば、次のコマンドを使用する:</p> <pre> <code class="language-bash">drush core:requirements | grep -i warning</code></pre> <p>2つの検索方法の比較を下表にまとめる。</p> <table class="w3-table-all"> <tbody> <tr> <th>機能</th> <th>--filter</th> <th>grep</th> </tr> <tr> <td>正規表現</td> <td>~= で可</td> <td>可</td> </tr> <tr> <td>折り返しを含むフィールド データ</td> <td>正しく検索できる</td> <td>検索漏れの可能性あり</td> </tr> <tr> <td>単一フィールドの検索</td> <td>可</td> <td>不可(行単位になる)</td> </tr> <tr> <td>複数フィールドの検索</td> <td>|| や &amp;&amp; で可</td> <td>可(行単位の検索)</td> </tr> <tr> <td>検索のヘッダー非表示</td> <td>不可</td> <td>可(マッチしなければ)</td> </tr> </tbody> </table> </div> <div class="w3-section field field--name-field-tag field--type-entity-reference field--label-inline"> <label class="field__label">Tag</label> <div class="field__items"> <div class="w3-bar-item field__item"><a href="/taxonomy/term/30" hreflang="ja">Drush</a></div> </div> </div> Sun, 17 Jan 2021 02:41:32 +0000 shirane 159 at https://www.white-root.com 画像スタイルの itok トークンを無効にする設定 https://www.white-root.com/blog/1610501394 <span>画像スタイルの itok トークンを無効にする設定</span> <span><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">shirane</span></span> <span>2021/01/13(水) - 10:29</span> <div class="w3-section field field--name-body field--type-text-with-summary field--label-hidden w3-bar-item field__item"><p>Drupal ユーザーの間では常識かもしれないが、先日の移行作業で実際に使う機会があったのでメモ。</p> <p><a href="https://www.drupal.org/docs/user_guide/en/structure-image-style-create.html" target="_blank">画像スタイル</a>による加工画像の URL に付加されるクエリパラメータ(トークン)を解除するには、settings.php 等で次の設定を行う。</p> <pre> <code class="language-php">$config['image.settings']['allow_insecure_derivatives'] = TRUE; $config['image.settings']['suppress_itok_output'] = TRUE; </code></pre> <p><a href="https://www.drupal.org/project/insert" target="_blank">Insert モジュール</a>でコンテンツ中に埋め込んでいた加工画像が、移行後のサイトで軒並み表示できなくなり焦ったが、この設定で表示を復元した。</p> <h2>解説</h2> <p>Drupal の<a href="https://www.drupal.org/docs/user_guide/en/structure-image-style-create.html" target="_blank">画像スタイル</a>を使用すると、サイズ変更をはじめとする様々な加工処理をオンデマンドで実行してくれる。たとえば、アップロードした画像ファイル <a href="/system/files/2021-01/view.jpg">view.jpg</a> を元に、異なる複数のサイズの画像を生成して取得できる。</p> <p><img alt="ホテルからの眺望" class="image-thumbnail" data-entity-type="file" data-entity-uuid="insert-thumbnail-3400d123-7989-4f28-a4e3-014297f81caf" data-insert-attach="{&quot;id&quot;:&quot;3400d123-7989-4f28-a4e3-014297f81caf&quot;,&quot;attributes&quot;:{&quot;alt&quot;:[&quot;alt&quot;,&quot;description&quot;],&quot;title&quot;:[&quot;title&quot;]}}" data-insert-class="image-thumbnail" data-insert-type="image" height="75" src="/system/files/styles/thumbnail/private/2021-01/view.jpg?itok=7x_NMciE" width="100" /></p> <p><img alt="ホテルからの眺望" class="image-medium" data-entity-type="file" data-entity-uuid="insert-medium-3400d123-7989-4f28-a4e3-014297f81caf" data-insert-attach="{&quot;id&quot;:&quot;3400d123-7989-4f28-a4e3-014297f81caf&quot;,&quot;attributes&quot;:{&quot;alt&quot;:[&quot;alt&quot;,&quot;description&quot;],&quot;title&quot;:[&quot;title&quot;]}}" data-insert-class="image-medium" data-insert-type="image" height="165" src="/system/files/styles/medium/private/2021-01/view.jpg?itok=eWeETG3S" width="220" /></p> <p><img alt="ホテルからの眺望" class="image-large" data-entity-type="file" data-entity-uuid="insert-large-3400d123-7989-4f28-a4e3-014297f81caf" data-insert-attach="{&quot;id&quot;:&quot;3400d123-7989-4f28-a4e3-014297f81caf&quot;,&quot;attributes&quot;:{&quot;alt&quot;:[&quot;alt&quot;,&quot;description&quot;],&quot;title&quot;:[&quot;title&quot;]}}" data-insert-class="image-large" data-insert-type="image" height="360" src="/system/files/styles/large/private/2021-01/view.jpg?itok=jPRhbP_6" width="480" /></p> <p>事前に構成した画像スタイルの名前(thumbnail、medium、large)を含むパスの画像 URL をリクエストすることで、対応する加工処理を起動し、生成後のファイルがレスポンスとして返ってくる仕組み。</p> <p>ところで、上のサンプルの画像 URL を見てみると、クエリパラメータ(トークン)が付加されていることがわかる。</p> <ul> <li><a href="/system/files/styles/thumbnail/private/2021-01/view.jpg?itok=7x_NMciE">files/styles/thumbnail/private/2021-01/view.jpg<strong>?itok=7x_NMciE</strong></a></li> <li><a href="/system/files/styles/medium/private/2021-01/view.jpg?itok=eWeETG3S">files/styles/medium/private/2021-01/view.jpg<strong>?itok=eWeETG3S</strong></a></li> <li><a href="/system/files/styles/large/private/2021-01/view.jpg?itok=jPRhbP_6">files/styles/large/private/2021-01/view.jpg<strong>?itok=jPRhbP_6</strong></a></li> </ul> <p>このトークンは、<a href="https://www.drupal.org/forum/newsletters/security-advisories-for-drupal-core/2013-02-20/sa-core-2013-002-drupal-core-denial" target="_blank">画像の加工処理リクエストを浴びせかける DoS/DDoS 攻撃の脆弱性</a>を修正する目的で <a href="https://www.drupal.org/project/drupal/releases/7.20" target="_blank">Drupal 7.20</a> のときに導入された。これにより、Drupal API で生成したサイト固有のトークンを URL に指定しないと、加工処理の呼び出しも、また加工後の画像を取得することもできなくなった。試みに、このクエリパラメータを削除してみると、レスポンスが 404 になることがわかる。</p> <ul> <li><a href="/system/files/styles/thumbnail/private/2021-01/view.jpg">files/styles/thumbnail/private/2021-01/view.jpg</a></li> <li><a href="/system/files/styles/medium/private/2021-01/view.jpg">files/styles/medium/private/2021-01/view.jpg</a></li> <li><a href="/system/files/styles/large/private/2021-01/view.jpg">files/styles/large/private/2021-01/view.jpg</a></li> </ul> <p>その後、これが CDN との連携や加工後の画像パスを含むコンテンツ移行等で<a href="https://www.drupal.org/node/1934498" target="_blank">問題を引き起こすという指摘</a>から、必要に応じて無効化できるオプションが <a href="https://www.drupal.org/project/drupal/releases/7.21" target="_blank">Drupal 7.21</a> で導入された。これが冒頭記した設定で、今回 Drupal 9 でも使えることが確認できた。</p> <p>なお、itok トークンを無効にした場合に DoS/DDoS 攻撃が問題になる環境では、対策を別に考える必要がある。</p> <h2>参考資料</h2> <ul> <li><a href="https://drupal.stackexchange.com/questions/66552/why-are-tokens-added-to-image-urls" target="_blank">Why are tokens added to image URLs?</a>(Drupal Answers)</li> <li><a href="https://drupal.stackexchange.com/questions/215165/how-do-i-remove-the-itok-token-from-the-image-urls" target="_blank">How do I remove the itok token from the image URLs?</a>(Drupal Answers)</li> <li><a href="https://api.drupal.org/api/drupal/core!modules!image!src!Entity!ImageStyle.php/function/ImageStyle%3A%3AbuildUrl" target="_blank">public function ImageStyle::buildUrl</a>(api.drupal.org)</li> </ul> </div> <div class="w3-section field field--name-field-tag field--type-entity-reference field--label-inline"> <label class="field__label">Tag</label> <div class="field__items"> <div class="w3-bar-item field__item"><a href="/taxonomy/term/5" hreflang="ja">Drupal9</a></div> </div> </div> Wed, 13 Jan 2021 01:29:54 +0000 shirane 158 at https://www.white-root.com Search and Replace Scanner モジュールでリンク切れの URL を一括修正する https://www.white-root.com/blog/1609759495 <span>Search and Replace Scanner モジュールでリンク切れの URL を一括修正する</span> <span><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">shirane</span></span> <span>2021/01/04(月) - 20:24</span> <div class="w3-section field field--name-body field--type-text-with-summary field--label-hidden w3-bar-item field__item"><p>昨年秋から投稿してきた <a href="/drush-commands">Drush コマンド ヘルプの日本語訳</a>では、各ページにオリジナルの英語ページへのリンクを掲載していたが、先日その URL のパスが変更になったようで、すべてリンク切れになっていることに気づいた。</p> <ul> <li>変更前:https://www.drush.org/commands/10.x/&lt;コマンド名&gt;</li> <li>変更後:https://www.drush.org/latest/commands/&lt;コマンド名&gt;</li> </ul> <p>これらは記事コンテンツ中の a タグの href 属性として埋め込んでいたので、修正するには、すべての対象コンテンツの該当箇所を書き換える必要がある。さてどうするか。</p> <p>そんなときに超便利なのが <a href="https://www.drupal.org/project/scanner" target="_blank">Search and Replace Scanner モジュール</a>。サイト内の全ノードを対象にコンテンツ(タイトルと本文)中の文字列を検索して一括置換してくれる。</p> <p>大文字小文字の区別、単語単位か部分文字列かの指定のほか、正規表現で検索文字列のパターンを指定するオプションもある。</p> <p class="w3-container w3-center"><img alt="環境設定" class="w3-border" data-entity-type="file" data-entity-uuid="insert-image-1da7c0ea-9432-4f1e-a84d-1cb186cb26d0" data-insert-attach="{&quot;id&quot;:&quot;1da7c0ea-9432-4f1e-a84d-1cb186cb26d0&quot;,&quot;attributes&quot;:{&quot;alt&quot;:[&quot;alt&quot;,&quot;description&quot;],&quot;title&quot;:[&quot;title&quot;]}}" data-insert-class="" data-insert-type="image" height="625" src="/system/files/2021-01/searchconf1.png" /></p> <p>実際の処理は、検索文字列と置換文字列を指定して実行する。今回はそれぞれ、冒頭書いた変更前と変更後の URL ディレクトリ文字列を指定し、(単語ではなく)部分文字列を対象として検索と置換を行った。</p> <p class="w3-container w3-center"><img alt="検索と置換の条件設定" class="w3-border" data-entity-type="file" data-entity-uuid="insert-image-37b98dd4-138e-4408-b45c-6ffc75e33055" data-insert-attach="{&quot;id&quot;:&quot;37b98dd4-138e-4408-b45c-6ffc75e33055&quot;,&quot;attributes&quot;:{&quot;alt&quot;:[&quot;alt&quot;,&quot;description&quot;],&quot;title&quot;:[&quot;title&quot;]}}" data-insert-class="" data-insert-type="image" height="873" src="/system/files/2021-01/searchconf2.png" /></p> <p>MS Word などのワープロソフトの文字列置換と同様の感覚で使えるので便利だ。おかげさまで、Drush コマンド ヘルプのリンク切れを無事すべて解消することができた。</p> <p>なお、現状のリリース(<a href="https://www.drupal.org/project/scanner/releases/8.x-1.0-rc3" target="_blank">8.x-1.0-rc3</a>)では、Drupal 9 環境で実行するには<a href="https://www.drupal.org/project/scanner/issues/3173506" target="_blank">パッチを適用する必要があった</a>。実際に試してみる場合は要注意。</p> </div> <div class="w3-section field field--name-field-tag field--type-entity-reference field--label-inline"> <label class="field__label">Tag</label> <div class="field__items"> <div class="w3-bar-item field__item"><a href="/taxonomy/term/14" hreflang="ja">モジュール</a></div> <div class="w3-bar-item field__item"><a href="/taxonomy/term/5" hreflang="ja">Drupal9</a></div> </div> </div> Mon, 04 Jan 2021 11:24:55 +0000 shirane 157 at https://www.white-root.com Migrate API で変換の前処理フックを利用して複数画像のサブフィールドを設定する例 https://www.white-root.com/blog/1609652574 <span>Migrate API で変換の前処理フックを利用して複数画像のサブフィールドを設定する例</span> <span><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">shirane</span></span> <span>2021/01/03(日) - 14:42</span> <div class="w3-section field field--name-body field--type-text-with-summary field--label-hidden w3-bar-item field__item"><p><a href="/blog/1609389337">Migrate API のパイプラインを利用してマルチフィールドに複数画像をマイグレーションする例</a>の続き。</p> <h2>サブフィールドの例</h2> <p>これまでの例では、画像フィールドの代替テキスト(alt 属性)を設定していなかった。こうした、フィールドに含まれるデータ項目はサブフィールドと呼ばれ、フィールド名の後にスラッシュで区切って名前を指定することができる。画像(画像ファイル エンティティの ID)と代替テキスト(alt 属性)を個別に指定する例を示す。</p> <pre> <code class="language-yaml">id: mgdemo_article source: plugin: embedded_data data_rows: - myid: 1 mytitle: 吾輩は猫である mytext: 吾輩は猫である。名前はまだない。 myimg: /tmp/img/cat1.jpg myalt: 猫 - myid: 2 mytitle: 羅生門 mytext: ある日の暮方の事である。一人の下人が、羅生門の下で雨やみを待っていた。 myimg: /tmp/img/gate.jpg myalt: 門の外観 - myid: 3 mytitle: 雪国 mytext: 好きよあなた。いまでも。いまでも。 myimg: /tmp/img/snow.jpg myalt: 雪景色 ids: myid: type: integer process: nid: myid title: mytitle uid: plugin: default_value default_value: 1 body: mytext field_image/target_id: plugin: migration_lookup migration: mgdemo_image source: myimg field_image/alt: myalt destination: plugin: 'entity:node' default_bundle: article</code></pre> <p>field_image/target_id が画像ファイル エンティティの ID、field_image/alt が代替テキストをそれぞれ表すサブフィールドである。</p> <p>このマイグレーションを実行すると、次のように画像と代替テキストがインポートされる。</p> <p class="w3-padding-16 w3-center"><img alt="画像フィールドの代替テキストに値をセットした例" data-entity-type="file" data-entity-uuid="insert-image-fae5e29e-0b7b-46a7-9878-cba483fd6ce8" data-insert-attach="{&quot;id&quot;:&quot;fae5e29e-0b7b-46a7-9878-cba483fd6ce8&quot;,&quot;attributes&quot;:{&quot;alt&quot;:[&quot;alt&quot;,&quot;description&quot;],&quot;title&quot;:[&quot;title&quot;]}}" data-insert-class="" data-insert-type="image" height="318" src="/system/files/2021-01/snowalt.png" width="714" /></p> <p>フィールドタイプの多くはデフォルトのサブフィールドを定義している。その場合、サブフィールドを明示しなければデフォルトのサブフィールドに値がセットされる。たとえば、画像フィールドのデフォルトのサブフィールドは target_id なので、画像ファイル エンティティの ID を設定するだけならサブフィールドの指定は不要だ。しかし、代替テキストのようなデフォルト以外のサブフィールドに値をセットするには、サブフィールド名(alt)を明示的に指定する必要がある。</p> <p>各種フィールドタイプにどんなサブフィールドがあるのかは、対象となるフィールドタイプの定義を調べる必要があるが、ありがたいことに<a href="https://understanddrupal.com/articles/drupal-migrations-reference-list-subfields-field-type" target="_blank">フィールドのタイプ別にサブフィールドの一覧を整理した資料</a>が公開されている。たとえば、画像フィールド(Entity reference image field)の項を見ると、次の5つのサブフィールドがあることがわかる。</p> <ol> <li>target_id(デフォルト)</li> <li>alt</li> <li>title</li> <li>width</li> <li>height</li> </ol> <h2>マルチのサブフィールドと subprocess プラグイン</h2> <p><a href="/blog/1609389337">Migrate API のパイプラインを利用してマルチフィールドに複数画像をマイグレーションする例</a>では、参照先の画像ファイル エンティティの ID の配列を field_image にセットすることで、デフォルトのサブフィールドである target_id に各配列要素の値を反映させていた。では、マルチフィールドの各画像に、代替テキストのような非デフォルトのサブフィールドをセットするにはどうすれば良いか。</p> <p>image_field に値をセットする処理の中で、ソースのある項目を target_id に、別の項目を alt に振り分ける、いわば子供の変換処理を image_field の配下に定義する必要がある。</p> <p>こうした要求に応えるため、コアの Migrate API に <a href="https://api.drupal.org/api/drupal/core%21modules%21migrate%21src%21Plugin%21migrate%21process%21SubProcess.php/class/SubProcess" target="_blank">subprocess プラグイン</a>が用意されている。これを利用すると(連想)配列の配列をソースとして、要素である配列のキーの値をサブフィールドにマッピングすることができる。</p> <p>たとえば、次のような、画像のパスと代替テキストからなる配列を要素とする配列をソース imginfo として用意できれば、</p> <pre> <code class="language-php">source: Array ( [imginfo] =&gt; Array ( [0] =&gt; Array ( [0] =&gt; '/tmp/img/cat1.jpg' [1] =&gt; '猫' ) [1] =&gt; Array ( [0] =&gt; '/tmp/img/cat2.jpg' [1] =&gt; 'ぬこ' ) [2] =&gt; Array ( [0] =&gt; '/tmp/img/cat3.jpg' [1] =&gt; 'ねんネコ' ) ) ) </code></pre> <p>次のようなマイグレーションを記述することで、</p> <pre> <code class="language-yaml"> field_image: plugin: sub_process source: imginfo process: target_id: plugin: migration_lookup migration: mgdemo_image source: '0' alt: '1' </code></pre> <p>各配列要素の先頭要素(インデックス = 0)を参照先の画像ファイル エンティティ ID に、2番目の要素(インデックス = 1)を代替テキストにそれぞれセットする処理を、マルチフィールドの各画像について実行させることができる。</p> <p>process サブキーが subprocess プラグインによって実行される子供の変換処理の定義。field_image の各サブフィールドに、ソース imginfo の配列要素の対応する位置の値をマッピングしている。</p> <h2>フック関数の利用</h2> <p>問題はソース配列 imginfo をどのようにして作るかだ。種々のプラグインを駆使すれば実現できるのかもしれないが、このくらいになるともう直接 PHP のコードを書いた方が早い気もする。</p> <p><a href="https://api.drupal.org/api/drupal/core!modules!migrate!migrate.api.php/group/migration" target="_blank">Migrate API</a> には、ソースデータを追加/加工する目的で利用できるフック関数が用意されている。</p> <ul> <li><a href="https://api.drupal.org/api/drupal/core%21modules%21migrate%21migrate.api.php/function/hook_migrate_prepare_row" target="_blank">function hook_migrate_prepare_row</a></li> <li><a href="https://api.drupal.org/api/drupal/core%21modules%21migrate%21migrate.api.php/function/hook_migrate_MIGRATION_ID_prepare_row" target="_blank">function hook_migrate_MIGRATION_ID_prepare_row</a></li> </ul> <p>前者はすべてのマイグレーション共通、後者は特定のマイグレーション ID 限定で、変換処理が実行される前に呼び出される。ここでは後者を使ってみよう。</p> <p>まず、マルチ画像フィールドに画像と代替テキストをインポートするためのソースデータとマイグレーションを次のように定義した。</p> <pre> <code class="language-yaml">id: mgdemo_article source: plugin: embedded_data data_rows: - myid: 1 mytitle: 吾輩は猫である mytext: 吾輩は猫である。名前はまだない。 myimg: /tmp/img/cat1.jpg, /tmp/img/cat2.jpg, /tmp/img/cat3.jpg myalt: 猫, ぬこ, ねんネコ - myid: 2 mytitle: 羅生門 mytext: ある日の暮方の事である。一人の下人が、羅生門の下で雨やみを待っていた。 myimg: /tmp/img/gate.jpg myalt: 門の写真 - myid: 3 mytitle: 雪国 mytext: 好きよあなた。いまでも。いまでも。 myimg: /tmp/img/snow.jpg, /tmp/img/ski.jpg myalt: 雪景色, スキーヤー ids: myid: type: integer process: nid: myid title: mytitle uid: plugin: default_value default_value: 1 body: mytext field_image: plugin: sub_process source: imginfo process: target_id: plugin: migration_lookup migration: mgdemo_image source: '0' alt: '1' destination: plugin: 'entity:node' default_bundle: article</code></pre> <p>myimg 列が画像のフルパス、myalt 列が各画像に対応する代替テキストである。</p> <p>次に、mgdemo というマシン名で簡単なモジュールを作り、マイグレーション mgdemo_article 用のフック関数を定義した。</p> <p>mgdemo.module</p> <pre> <code class="language-php">&lt;?php use Drupal\migrate\Row; use Drupal\migrate\Plugin\MigrateSourceInterface; use Drupal\migrate\Plugin\MigrationInterface; /** * Implements hook_migrate_MIGRATION_ID_prepare_row(). */ function mgdemo_migrate_mgdemo_article_prepare_row(Row $row, MigrateSourceInterface $source, MigrationInterface $migration) { // myimage列の値をカンマ位置で分割して配列にする $myimg = $row-&gt;getSourceProperty('myimg'); $images = explode(',', $myimg); array_walk($images, '_trim_value'); // myalt列の値をカンマ位置で分割して配列にする $myalt = $row-&gt;getSourceProperty('myalt'); $alts = explode(',', $myalt); array_walk($alts, '_trim_value'); // 各配列の同位置の要素を束ねた配列の配列を作る $imginfo = array_map(null, $images, $alts); // imginfoという名前のソース列として追加 $row-&gt;setSourceProperty('imginfo', $imginfo); } /* * array_walk用のコールバック */ function _trim_value(&amp;$value) { $value = trim($value); } </code></pre> <p>myimg 列、myalt 列それぞれの値を <a href="https://www.php.net/manual/ja/function.explode.php" target="_blank">explode 関数</a>で分割して配列にし、各要素を <a href="https://www.php.net/manual/ja/function.trim.php" target="_blank">trim 関数</a>で処理して前後の空白を除去している。_trim_value() 関数は <a href="https://www.php.net/manual/ja/function.array-walk.php" target="_blank">array_walk 関数</a> で trim 関数を反復適用するためのコールバックである。</p> <p>最後に、<a href="https://www.php.net/manual/ja/function.array-map.php" target="_blank">array_map 関数</a>の第1引数に null を指定することで、両配列の各要素を合体した配列の配列を生成し、imginfo という名前でソース列($row)に追加している。こうすることで、マイグレーション側では、imginfo というソース列の値として、変数 $imginfo の配列を利用できるようになる。</p> <p>モジュールを有効化し、キャッシュをクリアしてから、mgdemo_article のマイグレーションを実行すると、画像フィールドに画像と代替テキストが反映する形で記事ノードがインポートされる。</p> <p class="w3-padding-16 w3-center"><img alt="代替テキストが反映されたマルチ画像フィールド" data-entity-type="file" data-entity-uuid="insert-image-69e37991-6e9c-414a-8d1e-b791fd332f50" data-insert-attach="{&quot;id&quot;:&quot;69e37991-6e9c-414a-8d1e-b791fd332f50&quot;,&quot;attributes&quot;:{&quot;alt&quot;:[&quot;alt&quot;,&quot;description&quot;],&quot;title&quot;:[&quot;title&quot;]}}" data-insert-class="" data-insert-type="image" height="750" src="/system/files/2021-01/nekoalts.png" width="661" /></p> <h2>まとめ</h2> <p>subprocess プラグインを使用して、マルチフィールドのサブフィールドに値を設定するサンプルを示した。Drupal 開発者にはおなじみのフック関数を利用すれば、マイグレーションの過程で必要となる込み入ったソースデータの構築を PHP コードで記述することも簡単にできる。</p> </div> <div class="w3-section field field--name-field-tag field--type-entity-reference field--label-inline"> <label class="field__label">Tag</label> <div class="field__items"> <div class="w3-bar-item field__item"><a href="/taxonomy/term/27" hreflang="ja">Migrate</a></div> <div class="w3-bar-item field__item"><a href="/taxonomy/term/5" hreflang="ja">Drupal9</a></div> </div> </div> Sun, 03 Jan 2021 05:42:54 +0000 shirane 156 at https://www.white-root.com Migrate API のパイプラインを利用してマルチフィールドに複数画像をマイグレーションする例 https://www.white-root.com/blog/1609389337 <span>Migrate API のパイプラインを利用してマルチフィールドに複数画像をマイグレーションする例</span> <span><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">shirane</span></span> <span>2020/12/31(木) - 13:35</span> <div class="w3-section field field--name-body field--type-text-with-summary field--label-hidden w3-bar-item field__item"><p>この記事は、<a href="https://qiita.com/advent-calendar/2020/drupal" target="_blank">Drupal アドベントカレンダー 2020</a> の 10 日目です。<br /> (延長戦での参加なので記事の日付が大幅に遅れてます…すみません)</p> <hr /> <p><a href="/blog/1608609031">Migrate API で画像ファイルを取り込むサンプル</a>の続き。</p> <p>前の例では、画像フィールド(field_image)の多重度「許容する値の数」は1だった(standard インストール プロファイルの初期設定)。ここでは、複数画像を設定できるマルチフィールドのサンプルを紹介する。</p> <p>まず、インポート済みのエンティティ(記事ノード、画像ファイル)をすべてロールバックしてから、「記事」コンテンツタイプの画像フィールドの「フィールドの設定」で、「許容する値の数」を「無制限」に変更しておく。</p> <p class="w3-padding-16"><img alt="画像フィールドの多重度設定" class="w3-border" data-entity-type="file" data-entity-uuid="insert-image-958d6e75-7988-458e-a15b-35f6b2988c0a" data-insert-attach="{&quot;id&quot;:&quot;958d6e75-7988-458e-a15b-35f6b2988c0a&quot;,&quot;attributes&quot;:{&quot;alt&quot;:[&quot;alt&quot;,&quot;description&quot;],&quot;title&quot;:[&quot;title&quot;]}}" data-insert-class="" data-insert-type="image" height="130" src="/system/files/2020-12/cardinality_confg.png" width="348" /></p> <p class="w3-small">(パス:/admin/structure/types/manage/article/fields/node.article.field_image/storage)</p> <p>これでマルチの画像フィールドになった。このフィールドに複数の画像をインポートするには、対応する画像エンティティの ID の配列をセットすればよい。</p> <p>例として、タイトル別に異なる複数画像をインポートするマイグレーションを考える:</p> <ul> <li>吾輩は猫である <ul> <li>/tmp/img/<a href="/system/files/2020-12/cat1.jpg" target="_blank">cat1.jpg</a></li> <li>/tmp/img/<a href="/system/files/2020-12/cat2.jpg" target="_blank">cat2.jpg</a></li> <li>/tmp/img/<a href="/system/files/2020-12/cat3.jpg" target="_blank">cat3.jpg</a></li> </ul> </li> <li>羅生門 <ul> <li>/tmp/img/<a href="/system/files/2020-12/gate.jpg" target="_blank">gate.jpg</a></li> </ul> </li> <li>雪国 <ul> <li>/tmp/img/<a href="/system/files/2020-12/snow.jpg" target="_blank">snow.jpg</a></li> <li>/tmp/img/<a href="/system/files/2020-12/ski.jpg" target="_blank">ski.jpg</a></li> </ul> </li> </ul> <p>まず、画像ファイルを画像エンティティとしてインポートするマイグレーションは次のとおり:</p> <pre> <code class="language-yaml">id: mgdemo_image source: constants: FILE_DIRECTORY: 'public://' plugin: embedded_data data_rows: - myimg: /tmp/img/cat1.jpg - myimg: /tmp/img/cat2.jpg - myimg: /tmp/img/cat3.jpg - myimg: /tmp/img/gate.jpg - myimg: /tmp/img/snow.jpg - myimg: /tmp/img/ski.jpg ids: myimg: type: string process: tmp_srcpath: myimg tmp_filename: plugin: callback callable: basename source: myimg tmp_destpath: plugin: concat source: - constants/FILE_DIRECTORY - '@tmp_filename' uri: plugin: file_copy source: - '@tmp_srcpath' - '@tmp_destpath' file_exists: replace move: false destination: plugin: 'entity:file'</code></pre> <p>source: 配下の埋め込みレコードとして、ノードから参照させる画像ファイルを列挙している。ファイルのフルパスをレコードの一意識別子として使用している。</p> <p>次に、上でインポートした画像エンティティを参照する記事ノードのマイグレーション例を示す:</p> <pre> <code class="language-yaml">id: mgdemo_article source: plugin: embedded_data data_rows: - myid: 1 mytitle: 吾輩は猫である mytext: 吾輩は猫である。名前はまだない。 myimg: '/tmp/img/cat1.jpg, /tmp/img/cat2.jpg, /tmp/img/cat3.jpg' - myid: 2 mytitle: 羅生門 mytext: ある日の暮方の事である。一人の下人が、羅生門の下で雨やみを待っていた。 myimg: '/tmp/img/gate.jpg' - myid: 3 mytitle: 雪国 mytext: 好きよあなた。いまでも。いまでも。 myimg: '/tmp/img/snow.jpg, /tmp/img/ski.jpg' ids: myid: type: integer process: nid: myid title: mytitle uid: plugin: default_value default_value: 1 body: mytext field_image: - plugin: explode delimiter: ',' source: myimg - plugin: callback callable: trim - plugin: migration_lookup migration: mgdemo_image destination: plugin: 'entity:node' default_bundle: article</code></pre> <p>ソースデータでは、myimg 列に1つ以上の画像パス(画像の一意識別子)をカンマ区切りで指定している。</p> <p>これを元に目的を果たすには、次のような複数ステップの処理が必要になる:</p> <ol> <li>myimg 列のカンマ区切り部分文字列を切り出して配列にする。</li> <li>カンマ前後の空白を除去する。</li> <li>画像パス(画像の一意識別子)を基に対応するインポート済みエンティティを照会する。</li> </ol> <p>Migrate API では、連続する複数の変換処理を実行する仕組みとして<a href="https://www.drupal.org/docs/drupal-apis/migrate-api/process-pipelines" target="_blank">パイプライン</a>が利用できる。具体的には、各処理(プラグインとパラメータの指定)を配列要素(-)として列挙すれば、それらを連結して実行してくれる。この例では、次の3つのプラグインによる処理を列挙している。</p> <ol> <li><a href="https://api.drupal.org/api/drupal/core%21modules%21migrate%21src%21Plugin%21migrate%21process%21Explode.php/class/Explode" target="_blank">explode プラグイン</a><br /> PHP の <a href="https://www.php.net/manual/ja/function.explode.php" target="_blank">explode 関数</a>と同様の働きをする。入力(source キーの値)に指定した文字列を区切り文字(delimiter キーの値)で分割した結果を配列として出力する。この例では、myimg 列の値をカンマ位置で分割した部分文字列の配列が出力される。</li> <li><a href="https://api.drupal.org/api/drupal/core%21modules%21migrate%21src%21Plugin%21migrate%21process%21Callback.php/class/Callback" target="_blank">callback プラグイン</a><br /> callable に指定した名前の PHP 関数を呼び出し、入力値を引数として渡して、その戻り値を出力する。この例では、<a href="https://www.php.net/manual/ja/function.trim.php" target="_blank">trim 関数</a>を呼び出して、前処理から渡ってきた文字列の前後にある空白文字列を除去している。</li> <li><a href="https://api.drupal.org/api/drupal/core%21modules%21migrate%21src%21Plugin%21migrate%21process%21MigrationLookup.php/class/MigrationLookup" target="_blank">migration_lookup プラグイン</a><br /> 入力値をキーとして、migration キーの値として指定した名前の既存マイグレーションからインポート済みエンティティの ID を照会して返却する。この例では、前処理から渡ってきた画像ファイルのフルパスをキーに、対応するインポート済み画像エンティティの ID を返却する。</li> </ol> <p>パイプラインにおいては、前の処理の出力(返却値)が次の処理の入力(source)として認識される。したがって、明示的に source キーの値を指定するのは先頭の要素(上の例では explode)のみで、それ以外は前段の出力が source キーに指定されたものとして処理を実行する。</p> <p>また、Migrate API の process プラグインには、<a href="https://api.drupal.org/api/drupal/core%21modules%21migrate%21src%21ProcessPluginBase.php/function/ProcessPluginBase%3A%3Amultiple" target="_blank">出力値が単独の値なのか複数の値のリスト(配列)なのかを識別する仕組み</a>があり、<a href="https://www.drupal.org/docs/8/api/migrate-api/migrate-process-plugins/migrate-process-overview#handling_multiple" target="_blank">配列を渡されたプラグインはその各要素に処理を適用すること</a>ができる。上の例では、<a href="https://api.drupal.org/api/drupal/core%21modules%21migrate%21src%21Plugin%21migrate%21process%21Explode.php/function/Explode%3A%3Amultiple" target="_blank">explode プラグインの multiple()</a> は TRUE を返却するので、次ステップの callback プラグインの処理は、explode プラグインの出力値そのものではなく、その各配列要素に適用される。</p> <p>他方、callback プラグインの multiple() は基底クラスをオーバーライドしておらず、既定の FALSE を返却する。しかし、migration_lookup プラグインは単独のソースキー値でも、その配列でも受け取れるようになっており、上の例では配列を引き渡している。migration_lookup の出力は、入力が単独であれ複数であれ配列となる。これは、実は Drupal 側のフィールドの内部構造も同じである。</p> <p>以上の処理を実行すると、複数の画像を設定した記事をインポートすることができる。</p> <p class="w3-padding-16"><img alt="複数画像を含む記事ノードをインポートした結果" data-entity-type="file" data-entity-uuid="insert-image-d0e7b608-a2fb-40f5-9ad6-1080997afb21" data-insert-attach="{&quot;id&quot;:&quot;d0e7b608-a2fb-40f5-9ad6-1080997afb21&quot;,&quot;attributes&quot;:{&quot;alt&quot;:[&quot;alt&quot;,&quot;description&quot;],&quot;title&quot;:[&quot;title&quot;]}}" data-insert-class="" data-insert-type="image" height="1280" src="/system/files/2020-12/result_multi.png" width="1040" /></p> <p>このように、複数ステップの処理が必要な変換処理では Migrate API のパイプラインが利用できる。パイプラインの過程で配列を受け渡す場合は、その各要素に処理を適用する仕組みがある点にも注目しておきたい。</p> </div> <div class="w3-section field field--name-field-tag field--type-entity-reference field--label-inline"> <label class="field__label">Tag</label> <div class="field__items"> <div class="w3-bar-item field__item"><a href="/taxonomy/term/27" hreflang="ja">Migrate</a></div> <div class="w3-bar-item field__item"><a href="/taxonomy/term/5" hreflang="ja">Drupal9</a></div> </div> </div> Thu, 31 Dec 2020 04:35:37 +0000 shirane 155 at https://www.white-root.com Google Analytics から Google Tag Manager へのモジュール移行 https://www.white-root.com/blog/1609101170 <span>Google Analytics から Google Tag Manager へのモジュール移行</span> <span><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">shirane</span></span> <span>2020/12/28(月) - 05:32</span> <div class="w3-section field field--name-body field--type-text-with-summary field--label-hidden w3-bar-item field__item"><p>Google アナリティクスで新しいプロパティを作ろうとしたら、Google Analytics 4(GA4)にバージョンアップしたらしく、取得する ID が「UA-」で始まるトラッキング ID から「G-」で始まる<strong>測定 ID</strong> というものに変わっていた。</p> <p>これまで <a href="https://www.drupal.org/project/google_analytics" target="_blank">Google Analytics モジュール</a>を使っていたが、GA4 の測定 ID を使うには、現状ではパッチを適用する必要があるらしい:<br /> <a href="https://www.drupal.org/project/google_analytics/issues/3178179" target="_blank">https://www.drupal.org/project/google_analytics/issues/3178179</a></p> <p>パッチ運用は避けたいのと、ちょうど良い機会でもあるので、<a href="https://marketingplatform.google.com/intl/ja/about/tag-manager/" target="_blank">Google Tag Manager</a>(GTM)を使ってトラッキングコードを管理する方法に移行することにした。テーマでテンプレートを編集して GTM のスクリプトを埋め込んでも良いが、Drupal には <a href="https://www.drupal.org/project/google_tag" target="_blank">GoogleTagManager モジュール</a>というものがある。Drupal 9 にも対応しているので、これを使うことにする。</p> <p>インストールは、いつものように Composer と Drush でサクッと終了。</p> <pre> <code class="language-bash">$ composer require 'drupal/google_tag:^1.4' $ drush en google_tag</code></pre> <p>自分の Google アカウントで GTM の操作パネルにログインし、GTM の「アカウント」を作成して、配下に「コンテナ」を作成するとコンテナ ID(形式:GTM-XXXXXXX)が発行される。作成したコンテナに「Google Analytics: GA4 設定」タグを追加し、事前に Google アナリティクスで作っておいた GA4 のプロパティの測定 ID を設定する。トリガーは「All Pages」を選択して保存。最後にコンテナを公開して準備完了。</p> <p>Drupal 側では、Google Tag Manager モジュールの管理画面でコンテナを追加し、GTM の操作パネルで作成したコンテナの Container ID を設定する。</p> <p class="w3-center"><img alt="GTM モジュールの設定画面" data-entity-type="file" data-entity-uuid="insert-image-7b63327a-6ef1-4d88-9e10-70c0853e77eb" data-insert-attach="{&quot;id&quot;:&quot;7b63327a-6ef1-4d88-9e10-70c0853e77eb&quot;,&quot;attributes&quot;:{&quot;alt&quot;:[&quot;alt&quot;,&quot;description&quot;],&quot;title&quot;:[&quot;title&quot;]}}" data-insert-class="" data-insert-type="image" height="448" src="/system/files/2020-12/gtmconf.png" width="670" /></p> <p>正常に動作すれば GTM の2つのスクリプトがページ内に埋め込まれるはず。ブラウザで任意のページを開いて HTML ソースを見てみよう。</p> <p>まず、head 要素内に次のようなコードが出力されていることを確認:</p> <pre> <code>&lt;script src="/sites/default/files/google_tag/default/google_tag.script.js?qlzstx" defer&gt;&lt;/script&gt; </code></pre> <p>src 属性の参照先を見てみると、上で設定した コンテナ ID を埋め込んだ GTM 用のスクリプトコードが返ってくることがわかる。</p> <p>もう1箇所は body 要素内の冒頭部分に下記コードを確認:</p> <pre> <code>&lt;noscript aria-hidden="true"&gt;&lt;iframe src="https://www.googletagmanager.com/ns.html?id=GTM-XXXXXXX" height="0" width="0" style="display:none;visibility:hidden"&gt;&lt;/iframe&gt;&lt;/noscript&gt;</code></pre> <p>(GTM-XXXXXXX のところには、実際には上で設定したコンテナ ID が入る)</p> <p>よしよし、正しく動いているようだ。</p> <p>Google Analytics の操作パネルの「リアルタイム」セクションを見てみると、ブラウザでページを開いた回数が「表示回数」や「イベント数」に反映されることが確認できた。</p> <p>GA4 の知識はゼロなので(笑)これから少しずつ勉強して使いこなしていこう。</p> </div> <div class="w3-section field field--name-field-tag field--type-entity-reference field--label-inline"> <label class="field__label">Tag</label> <div class="field__items"> <div class="w3-bar-item field__item"><a href="/taxonomy/term/5" hreflang="ja">Drupal9</a></div> <div class="w3-bar-item field__item"><a href="/taxonomy/term/57" hreflang="ja">Analytics</a></div> </div> </div> Sun, 27 Dec 2020 20:32:50 +0000 shirane 154 at https://www.white-root.com Drush のコマンド ヘルプについて https://www.white-root.com/blog/1608735600 <span>Drush のコマンド ヘルプについて</span> <span><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">shirane</span></span> <span>2020/12/24(木) - 00:00</span> <div class="w3-section field field--name-body field--type-text-with-summary field--label-hidden w3-bar-item field__item"><p>この記事は、<a href="https://qiita.com/advent-calendar/2020/drupal" target="_blank">Drupal アドベントカレンダー 2020</a> の 24 日目です。</p> <hr /> <p>Drupal の開発/運用を効率的に行う上で、Composer、Drush、Drupal Console といった CLI(コマンドライン インターフェイス)ツールは重要です。中でも、Drush は古くから使われてきた Drupal 固有の CLI ツールで、Drupal の実務に欠かせません。</p> <p>Drush には数多くの機能が標準で用意されており、引数なしで drush コマンドを実行すると、すべての機能(サブコマンド)が短い説明とともに表示されます。また、特定のサブコマンドの後に --help オプションを指定して実行することで、そのサブコマンドのヘルプを閲覧できます。</p> <pre> <code class="language-ini">$ drush cr --help Rebuild a Drupal 8 site. This is a copy of core/rebuild.php. Additionally it also clears Drush cache and Drupal's render cache. Options: --cache-clear[=CACHE-CLEAR] Set to 0 to suppress normal cache clearing; the caller should then clear if needed. [default: true] --no-cache-clear Negate --cache-clear option. Aliases: cr, rebuild, cache-rebuild </code></pre> <p>また「drush topic」と実行すると、さらに踏み込んだ内容のドキュメントを参照できます。</p> <pre> <code class="language-ini">$ drush topic Choose a topic: [0 ] Cancel [1 ] All global options. (core:global-options) [2 ] An example Drush script. (docs:script) [3 ] Bashrc customization examples for Drush. (docs:bashrc) [4 ] Bootstrap explanation: how Drush starts up and prepares the Drupal environment. (docs:bootstrap) [5 ] Creating site aliases for running Drush on remote sites. (docs:aliases) [6 ] Crontab instructions for running your Drupal cron tasks via `drush cron`. (docs:cron) [7 ] Deploy command for Drupal. (docs:deploy) [8 ] Drupal config export instructions, including customizing config by environment. (docs:config:exporting) [9 ] Drush's PHP Shell. (docs:repl) [10] Drush's support for Git Bisect. (docs:bisect) [11] Drush configuration example. (docs:configuration) [12] Drush hooks. (docs:hooks) [13] Example Drush command file. (docs:examplecommand) [14] Example policy file. (docs:policy) [15] Extend sql-sync to allow transfer of the sql dump file via http. (docs:example-sync-via-http) [16] Instructions on creating your own Drush commands. (docs:commands) [17] Instructions on creating your own Drush Generators. (docs:generators) [18] Output formatters and filters: control the command output (docs:output-formats-filters) [19] README.md (docs:readme) &gt; </code></pre> <p>これらの資料は、<a href="https://www.drush.org/" target="_blank">drush.org</a> で Web ドキュメントとして読むこともできます。使い慣れたツールでも、一度こうした資料を網羅的に見ておくと新しい発見があるかもしれません。</p> <p>すべて英語というところが難点なので、自分も勉強のため Drush のコマンドヘルプを1件ずつ読んで日本語化し、ブログ記事としてアップしてきました。この年末までに一通り網羅できたので、<a href="/drush-commands" target="_blank">コマンドの分類で記事を絞り込めるようにしてみました</a>。今後、Drush コマンドの日本語リファレンスとして活用していければと思います。間違いやコメントなどあれば、<a href="https://twitter.com/bkenro" target="_blank">@bkenro</a> までお知らせいただけると嬉しいです。</p> <hr /> <p>ところで、Drush の使い方については、コミュニティ有志による電子書籍<a href="https://techbookfest.org/product/5141784608899072" target="_blank"><strong>『Drupal 9 Web 開発をはじめるための薄い本』</strong></a>(Drupal Meetup 豊田支部=編)でも独立した章をあてて解説されています。</p> <p class="w3-center"><a href="https://techbookfest.org/product/5141784608899072" target="_blank"><img alt="Drupal 9 Web 開発をはじめるための薄い本" class="image-large" data-entity-type="file" data-entity-uuid="insert-large-ad172b7f-c6cf-46ef-95f9-9e13c1676b7f" data-insert-attach="{&quot;id&quot;:&quot;ad172b7f-c6cf-46ef-95f9-9e13c1676b7f&quot;,&quot;attributes&quot;:{&quot;alt&quot;:[&quot;alt&quot;,&quot;description&quot;],&quot;title&quot;:[&quot;title&quot;]}}" data-insert-class="image-large" data-insert-type="image" height="480" src="/system/files/styles/large/private/2020-12/131440743_440653580268386_3342372955023513485_n.jpg?itok=VrrpQSQ2" width="342" /></a></p> <p>"薄い本" と銘打ちながらも、最新 Drupal 9 に関する内容満載!ボリューム満点の一冊です。是非ぜひチェックしてみてください。</p> </div> <div class="w3-section field field--name-field-tag field--type-entity-reference field--label-inline"> <label class="field__label">Tag</label> <div class="field__items"> <div class="w3-bar-item field__item"><a href="/taxonomy/term/30" hreflang="ja">Drush</a></div> <div class="w3-bar-item field__item"><a href="/taxonomy/term/1" hreflang="ja">コミュニティ</a></div> </div> </div> Wed, 23 Dec 2020 15:00:00 +0000 shirane 153 at https://www.white-root.com Migrate API で画像ファイルを取り込むサンプル https://www.white-root.com/blog/1608609031 <span>Migrate API で画像ファイルを取り込むサンプル</span> <span><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">shirane</span></span> <span>2020/12/22(火) - 12:50</span> <div class="w3-section field field--name-body field--type-text-with-summary field--label-hidden w3-bar-item field__item"><p>この記事は、<a href="https://qiita.com/advent-calendar/2020/drupal" target="_blank">Drupal アドベントカレンダー 2020</a> の 7 日目です。<br /> (後から参加したので記事の日付は遅れています…すみません)</p> <hr /> <p>みなさん、Migrate 使っていますか?<br /> Migrate モジュールは Drupal 8 からコアに取り込まれた標準データ移行 API です。この記事では、Migrate を初めて使う人にはちょっとわかりづらい(かもしれない)エンティティ参照を扱う例として、画像ファイルをフィールドに取り込むサンプルを示します。</p> <p>Migrate の基本的な使い方には触れませんが、コミュニティ有志による電子書籍<strong><a href="https://techbookfest.org/product/5141784608899072" target="_blank">『Drupal 9 Web 開発をはじめるための薄い本』</a></strong>(Drupal Meetup 豊田支部=編)で執筆を担当した章に詳しい手順を書かせていただきました。</p> <p class="w3-center"><a href="https://techbookfest.org/product/5141784608899072" target="_blank"><img alt="Drupal 9 Web 開発をはじめるための薄い本" class="image-large" data-entity-type="file" data-entity-uuid="insert-large-ad172b7f-c6cf-46ef-95f9-9e13c1676b7f" data-insert-attach="{&quot;id&quot;:&quot;ad172b7f-c6cf-46ef-95f9-9e13c1676b7f&quot;,&quot;attributes&quot;:{&quot;alt&quot;:[&quot;alt&quot;,&quot;description&quot;],&quot;title&quot;:[&quot;title&quot;]}}" data-insert-class="image-large" data-insert-type="image" height="480" src="/system/files/styles/large/private/2020-12/131440743_440653580268386_3342372955023513485_n.jpg?itok=VrrpQSQ2" width="342" /></a></p> <p>こちら "薄い本" と銘打ちつつ、最新 Drupal 9 に関する内容満載!ボリューム満点の一冊です。是非ぜひチェックしてみてください。</p> <h2>やりたいこと</h2> <p>まず、やりたいことをまとめます。環境は、標準(standard)プロファイルでインストールした Drupal 9.1 サイトを前提とします。</p> <ul> <li>記事(article)ノードに外部データをインポートする。</li> <li>対象フィールドは以下とする: <ul> <li>タイトル(title)</li> <li>本文(body)</li> <li>画像(field_image)</li> </ul> </li> </ul> <p>Drupal では、画像をファイル エンティティとして管理します。記事ノードの画像フィールドはファイル エンティティへの参照で、内部的には参照先エンティティの ID 値を保持します。このため、画像フィールドにデータを取り込むには、次の2つのステップが必要になります:</p> <ol> <li>画像ファイルをファイルエンティティとしてインポートする。</li> <li>記事ノードをインポートし、画像フィールドには 1. のファイルエンティティの ID 値をセットする。</li> </ol> <h2>画像ファイルのマイグレーション</h2> <p>次の3ファイルをインポートするとします:</p> <ul> <li>/tmp/img/<a href="/system/files/2020-12/cat.jpg" target="_blank">cat.jpg</a></li> <li>/tmp/img/<a href="/system/files/2020-12/gate.jpg" target="_blank">gate.jpg</a></li> <li>/tmp/img/<a href="/system/files/2020-12/snow.jpg" target="_blank">snow.jpg</a></li> </ul> <p>このファイルを Drupal サイトのパブリック領域(public://)直下のファイルとしてインポートするマイグレーションの例を示します。簡単のため、ソースデータは <a href="https://api.drupal.org/api/drupal/core%21modules%21migrate%21src%21Plugin%21migrate%21source%21EmbeddedDataSource.php/class/EmbeddedDataSource" target="_blank">embedded_data ソースプラグイン</a>を使用して YAML 内に埋め込んでいます。</p> <pre> <code class="language-yaml">id: mgdemo_image source: constants: FILE_DIRECTORY: 'public://' plugin: embedded_data data_rows: - myid: 301 myimg: /tmp/img/cat.jpg - myid: 302 myimg: /tmp/img/gate.jpg - myid: 303 myimg: /tmp/img/snow.jpg ids: myid: type: integer process: tmp_srcpath: myimg tmp_filename: plugin: callback callable: basename source: myimg tmp_destpath: plugin: concat source: - constants/FILE_DIRECTORY - '@tmp_filename' uri: plugin: file_copy source: - '@tmp_srcpath' - '@tmp_destpath' file_exists: replace move: false destination: plugin: 'entity:file'</code></pre> <p>記述内容を簡単に説明します。</p> <h3>メタ情報</h3> <p>id キーは、このマイグレーションの識別名。実行する時や他のマイグレーションから実行結果を参照する時などに使用する名前。</p> <h3>source 配下(データソースの定義)</h3> <p>サブキー constant は定数値を定義する。ここでは、FILE_DIRECTORY という名前でパブリック領域のストリーム名(public://)を指定している。これは後続の process 配下の記述で使用する。</p> <p>各ファイルのフルパス(myimg)とそれぞれを一意に識別する番号(myid)をセットにして 1 つのレコードとし、これを 3 件分埋め込んである。</p> <p>ids はレコードを一意に識別する列(データベース テーブルの主キーに相当)を指定する。この例では、myid を整数型の一意識別子として指定している。</p> <h3>process 配下(変換処理の定義)</h3> <p>疑似フィールドを使用している。これは、実際には存在しない名前のフィールドを処理中の一時変数として利用できるもので、名前の先頭に @ を付けることで値を参照できる。たとえば、疑似フィールド tmp_srcpath にはソースの myimg 列の値がセットされ、後続の処理で '@tmp_scrpath' と書いてその値を参照している。</p> <p>サンプルでは次の 3 つの疑似フィールドを使用している:</p> <ul> <li><strong>tmp_srcpath</strong><br /> コピー元のファイルのフルパス(myimg の値そのまま)</li> <li><strong>tmp_filename</strong><br /> パスを除去したファイル名</li> <li><strong>tmp_destpath</strong><br /> コピー先のディレクトリパス</li> </ul> <p>tmp_filename では、<a href="https://api.drupal.org/api/drupal/core%21modules%21migrate%21src%21Plugin%21migrate%21process%21Callback.php/class/Callback" target="_blank">callback プラグイン</a>で PHP の <a href="https://www.php.net/manual/ja/function.basename.php" target="_blank">basename 関数</a>を呼び出して、フルパスのファイル名からディレクトリパスを除いたベース名を設定している。tmp_destpath では、<a href="https://api.drupal.org/api/drupal/core%21modules%21migrate%21src%21Plugin%21migrate%21process%21Concat.php/class/Concat" target="_blank">concat プラグイン</a>を使用して、先述の FILE_DIRECTORY 定数と tmp_filename の値を連結して、コピー先ファイルのパスを生成している。</p> <p>uri はファイルエンティティの URI を表すフィールドで、<a href="https://api.drupal.org/api/drupal/core%21modules%21migrate%21src%21Plugin%21migrate%21process%21FileCopy.php" target="_blank">file_copy プラグイン</a>でファイルをコピーした結果を設定している。コピー元としてファイルのフルパス、コピー先として tmp_destpath 疑似フィールドの値をそれぞれ渡している。file_exists は同名ファイルが存在した場合の挙動を指定するオプションで、値 replace は置換を意味する。move はコピーか移動かを指定するオプションで、値 false は移動ではないこと、つまりコピー元のファイルは残すよう指示している。</p> <h3>destination 配下(データの格納先の定義)</h3> <p>entity:file プラグインを使用して、結果をファイルエンティティとして格納する。</p> <p>以上です。</p> <p>このマイグレーションを実行すると、/tmp/img ディレクトリの 3 ファイルがサイト内のパブリック ファイルとしてコピーされ、3 つのファイル エンティティが生成されます。</p> <p><img alt="インポートした画像エンティティ" data-entity-type="file" data-entity-uuid="insert-image-008741fe-eea2-4c20-af33-e3c2f3f3e86c" data-insert-attach="{&quot;id&quot;:&quot;008741fe-eea2-4c20-af33-e3c2f3f3e86c&quot;,&quot;attributes&quot;:{&quot;alt&quot;:[&quot;alt&quot;,&quot;description&quot;],&quot;title&quot;:[&quot;title&quot;]}}" data-insert-class="" data-insert-type="image" height="444" src="/system/files/2020-12/image_entities1.png" width="939" /></p> <p>各ファイルの名前のリンクをクリックしてみると、パブリック領域 public://(既定では sites/default/files/)の下に、画像ファイルが JPEG 画像エンティティとしてインポートできていることがわかります。ここで注目したいのは、状態が「一時的」、利用場所が「0 places」になっている点。これは、各画像エンティティがまだどこからも参照されていないことを示しています。</p> <h3>Migrate モジュールによるエンティティの追跡</h3> <p>Migrate ではインポートしたエンティティを内部的に追跡しています。名前に migrate が含まれるテーブルを確認してみます。</p> <pre> <code class="language-sql">$ drush sqlc MariaDB [std]&gt; show tables like 'migrate%'; +------------------------------+ | Tables_in_std (migrate%) | +------------------------------+ | migrate_map_mgdemo_image | | migrate_message_mgdemo_image | +------------------------------+ 2 rows in set (0.001 sec) MariaDB [std]&gt; desc migrate_map_mgdemo_image; +-------------------+---------------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------------------+---------------------+------+-----+---------+-------+ | source_ids_hash | varchar(64) | NO | PRI | NULL | | | sourceid1 | int(11) | NO | MUL | NULL | | | destid1 | int(10) unsigned | YES | | NULL | | | source_row_status | tinyint(3) unsigned | NO | | 0 | | | rollback_action | tinyint(3) unsigned | NO | | 0 | | | last_imported | int(10) unsigned | NO | | 0 | | | hash | varchar(64) | YES | | NULL | | +-------------------+---------------------+------+-----+---------+-------+ 7 rows in set (0.001 sec) </code></pre> <p>migrate_map_mgdemo_image という名前のテーブルが、このマイグレーションでインポートされたエンティティを追跡するテーブルです。sourceid1 列がデータソース側の ID 値、destid1 列がインポート先エンティティの ID 値で、このテーブルによってデータソースのレコードと Drupal 側のエンティティとの対応関係が記録されています。</p> <p>実際の値を確認すると次のようになります。</p> <pre> <code class="language-sql">MariaDB [std]&gt; select sourceid1,destid1 from migrate_map_mgdemo_image; +-----------+---------+ | sourceid1 | destid1 | +-----------+---------+ | 303 | 306 | | 302 | 305 | | 301 | 304 | +-----------+---------+ 3 rows in set (0.000 sec)</code></pre> <p>このマイグレーションでは、ファイルエンティティの ID に値を設定していないので、エンティティ作成時に自動採番され(304、305、306)、それぞれ対応するデータソース ID 値(myid 列の値)のレコードに記録されています。この対照表により、データソースの ID 値に対応するインポート済み Drupal エンティティを、後でいつでも調べることができます。</p> <p>対照表のテーブルは「migrate_map_&lt;マイグレーションID&gt;」という名前になります。マイグレーション ID がわかれば、照会すべきテーブル名を特定できます。</p> <h2>記事のマイグレーション</h2> <p>画像ファイルのエンティティを確認したので、今度はそのエンティティを画像フィールドから参照するノードをインポートしてみましょう。マイグレーションの例を示します。</p> <pre> <code class="language-yaml">id: mgdemo_article source: plugin: embedded_data data_rows: - myid: 1 mytitle: 吾輩は猫である mytext: 吾輩は猫である。名前はまだない。 myimg: 301 - myid: 2 mytitle: 羅生門 mytext: ある日の暮方の事である。一人の下人が、羅生門の下で雨やみを待っていた。 myimg: 302 - myid: 3 mytitle: 雪国 mytext: 好きよあなた。いまでも。いまでも。 myimg: 303 ids: myid: type: integer process: nid: myid title: mytitle uid: plugin: default_value default_value: 1 body: mytext field_image: plugin: migration_lookup migration: mgdemo_image source: myimg destination: plugin: 'entity:node' default_bundle: article</code></pre> <p>データソースの myimg 列の値は、先にインポートした画像のソース ID です。<br /> タイトル(mytitle 列)と画像ファイルとの対応関係は次のとおり:</p> <ul> <li>吾輩は猫である→ 301(/tmp/img/cat.jpg)</li> <li>羅生門 → 302(/tmp/img/gate.jpg)</li> <li>雪国 → 303(/tmp/img/snow.jpg)</li> </ul> <p>マイグレーションの処理においては、画像ファイルのソース ID(301、302、303)から、インポート済み画像エンティティの ID 値(304、305、306)を照会して、画像フィールド(field_image)にセットする必要があります。</p> <p>この仕事を担うのが <a href="https://api.drupal.org/api/drupal/core%21modules%21migrate%21src%21Plugin%21migrate%21process%21MigrationLookup.php/class/MigrationLookup" target="_blank">migration_lookup プラグイン</a>です。<br /> field_image: 配下の定義を抜粋すると、</p> <pre> <code class="language-yaml"> field_image: plugin: migration_lookup migration: mgdemo_image source: myimg</code></pre> <p>migration キーの値として、照会先の既存マイグレーションの名前(mgdemo_image)を指定しています。Migrate API は、この名前から照会先のテーブル(migrate_map_mgdemo_image)を特定し、source キーに指定したソース列(myimg)の値(301、302、303)から、それぞれに対応するインポート済み画像エンティティのID(304、305、306)を取得して、その値が field_image フィールドに設定されることになります。</p> <p>画像、添付ファイル、タクソノミー、コメントなど、エンティティ参照として実装されるフィールドへのマイグレーションでは、この migration_lookup を用いたエンティティ間の紐づけが重要な役割を果たします。</p> <p>上記マイグレーションを実行すると 3 件の記事ノードが作成され、各ノードの画像フィールドに、対応する画像エンティティが設定されます。</p> <p><img alt="インポートしたノードの一覧" data-entity-type="file" data-entity-uuid="insert-image-8b13b5f0-2757-4247-9046-e38c948b69ba" data-insert-attach="{&quot;id&quot;:&quot;8b13b5f0-2757-4247-9046-e38c948b69ba&quot;,&quot;attributes&quot;:{&quot;alt&quot;:[&quot;alt&quot;,&quot;description&quot;],&quot;title&quot;:[&quot;title&quot;]}}" data-insert-class="" data-insert-type="image" height="926" src="/system/files/2020-12/nodes.png" width="1018" /></p> <p>また、参照元の記事ノードがインポートされたことで、各ファイルエンティティの状態が「恒久的」、利用場所が「1 place」にそれぞれ変わります。</p> <p><img alt="参照元をインポートした後の画像エンティティ" data-entity-type="file" data-entity-uuid="insert-image-7807ef76-0577-494f-aac7-43722951412d" data-insert-attach="{&quot;id&quot;:&quot;7807ef76-0577-494f-aac7-43722951412d&quot;,&quot;attributes&quot;:{&quot;alt&quot;:[&quot;alt&quot;,&quot;description&quot;],&quot;title&quot;:[&quot;title&quot;]}}" data-insert-class="" data-insert-type="image" height="437" src="/system/files/2020-12/image_entities2.png" width="949" /></p> <h2>画像ファイルのパスを ID にする</h2> <p>上のサンプルでは、各画像ファイルの一意識別子として番号(301、302、303)を割り当てていました。しかし、よく考えると、画像ファイルのフルパスそれ自体が一意の識別名として使用できます。わざわざ番号を振らなくても、画像ファイルのパスを ID にして簡潔な形で書き換えることができますね。</p> <h3>画像ファイルのマイグレーション(改訂版)</h3> <pre> <code class="language-yaml">id: mgdemo_image source: constants: FILE_DIRECTORY: 'public://' plugin: embedded_data data_rows: - myimg: /tmp/img/cat.jpg - myimg: /tmp/img/gate.jpg - myimg: /tmp/img/snow.jpg ids: myimg: type: string process: tmp_srcpath: myimg tmp_filename: plugin: callback callable: basename source: myimg tmp_destpath: plugin: concat source: - constants/FILE_DIRECTORY - '@tmp_filename' uri: plugin: file_copy source: - '@tmp_srcpath' - '@tmp_destpath' file_exists: replace move: false destination: plugin: 'entity:file'</code></pre> <p>ソースデータの myid 列を削除し、ids で myimg を文字列(string)型の ID 列として指定しています。</p> <p>スキーマが変更されるので、migrate_map_mgdemo_image と migrate_message_mgdemo_image の各テーブルをいったん削除してから、新しいマイグレーションでインポートを実行すると、テーブルは次のようになりました。</p> <pre> <code class="language-sql">MariaDB [std]&gt; desc migrate_map_mgdemo_image; +-------------------+---------------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------------------+---------------------+------+-----+---------+-------+ | source_ids_hash | varchar(64) | NO | PRI | NULL | | | sourceid1 | varchar(255) | NO | MUL | NULL | | | destid1 | int(10) unsigned | YES | | NULL | | | source_row_status | tinyint(3) unsigned | NO | | 0 | | | rollback_action | tinyint(3) unsigned | NO | | 0 | | | last_imported | int(10) unsigned | NO | | 0 | | | hash | varchar(64) | YES | | NULL | | +-------------------+---------------------+------+-----+---------+-------+ 7 rows in set (0.002 sec) MariaDB [std]&gt; select sourceid1,destid1 from migrate_map_mgdemo_image; +-------------------+---------+ | sourceid1 | destid1 | +-------------------+---------+ | /tmp/img/cat.jpg | 307 | | /tmp/img/gate.jpg | 308 | | /tmp/img/snow.jpg | 309 | +-------------------+---------+ 3 rows in set (0.001 sec)</code></pre> <p>ソースの ID がファイルのフルパス文字列に変わっていることがわかります。</p> <h3>記事ノードのマイグレーション(改訂版)</h3> <p>これに合わせて、記事ノードのソースデータは次のように書き換えることができます。</p> <pre> <code class="language-yaml">id: mgdemo_article source: plugin: embedded_data data_rows: - myid: 1 mytitle: 吾輩は猫である mytext: 吾輩は猫である。名前はまだない。 myimg: /tmp/img/cat.jpg - myid: 2 mytitle: 羅生門 mytext: ある日の暮方の事である。一人の下人が、羅生門の下で雨やみを待っていた。 myimg: /tmp/img/gate.jpg - myid: 3 mytitle: 雪国 mytext: 好きよあなた。いまでも。いまでも。 myimg: /tmp/img/snow.jpg ・・・以下変更なし</code></pre> <p>myimg 列の値を元の番号からファイルのパスに変更しています。migraton_lookup で照会するときのキーが番号から文字列に変わったことになりますが、対応関係に変更はないので、実行して得られる結果は同じです。</p> <p>このように、画像ファイルのマイグレーションでは、ファイルパスを ID として使った方が簡単で直感的にわかりやすいかもしれません。</p> <h2>まとめ</h2> <p>画像フィールドを含むノードに、Migrate API を使用して文字列や画像ファイルをインポートする例を示しました。Migrate API はインポートしたエンティティを内部的に追跡しており、それを利用して 、migration_lookup プラグインでエンティティ間の参照関係を解決することができます。この仕組みは、タクソノミーやコメントなど、エンティティ参照に基づく他のフィールドでも同様に利用できます。</p> <h2>参考資料</h2> <ul> <li><a href="https://www.drupal.org/docs/drupal-apis/migrate-api/migrate-api-overview" target="_blank">Migrate API overview</a>(drupal.org)</li> <li><a href="https://www.drupal.org/docs/drupal-apis/migrate-api" target="_blank">Migrate API</a>(drupal.org)</li> <li><a href="https://understanddrupal.com/31-days-of-migrations" target="_blank">31 days of Drupal migrations</a>(understanddrupal.com)</li> </ul> </div> <div class="w3-section field field--name-field-tag field--type-entity-reference field--label-inline"> <label class="field__label">Tag</label> <div class="field__items"> <div class="w3-bar-item field__item"><a href="/taxonomy/term/27" hreflang="ja">Migrate</a></div> <div class="w3-bar-item field__item"><a href="/taxonomy/term/5" hreflang="ja">Drupal9</a></div> <div class="w3-bar-item field__item"><a href="/taxonomy/term/1" hreflang="ja">コミュニティ</a></div> </div> </div> Tue, 22 Dec 2020 03:50:31 +0000 shirane 152 at https://www.white-root.com Drupal 9.0.9 から 9.1.0 へのアップデート https://www.white-root.com/blog/1606974738 <span>Drupal 9.0.9 から 9.1.0 へのアップデート</span> <span><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">shirane</span></span> <span>2020/12/03(木) - 14:52</span> <div class="w3-section field field--name-body field--type-text-with-summary field--label-hidden w3-bar-item field__item"><p>Drupal 9.1.0 がリリースされた。<br /> <a href="https://www.drupal.org/project/drupal/releases/9.1.0" target="_blank">https://www.drupal.org/project/drupal/releases/9.1.0</a></p> <p>このサイトは 9.0.9 で動いていたが、さっそく 9.1.0 に更新した。<br /> 以下そのときの手順をメモする。</p> <p>リリースノートを見ると、drush は最新の dev バージョンに更新する必要があるようだ。このサイトでも drush を使っていたので、更新前にいったん削除しておく:</p> <pre> <code class="language-bash">$ composer remove drush/drush ./composer.json has been updated Running composer update drush/drush Loading composer repositories with package information Updating dependencies …</code></pre> <p>次に Drupal の更新を行う。このサイトは drupal/core-recommended でインストールしたので、次のコマンドを実行する:</p> <pre> <code class="language-bash">$ composer update drupal/core 'drupal/core-*' --with-all-dependencies Loading composer repositories with package information Updating dependencies … </code></pre> <p>drush を再度インストールする。最新の dev バージョン(10.x-dev)を指定してインストールを実行した後、drush のバージョンを確認する。</p> <pre> <code class="language-bash">$ composer require drush/drush:10.x-dev ./composer.json has been updated Running composer update drush/drush Loading composer repositories with package information Updating dependencies … Generating autoload files 42 packages you are using are looking for funding. Use the `composer fund` command to find out more! $ drush version Drush version : 10.3.7-dev </code></pre> <p>以上でファイルの更新は終了。</p> <p>次にデータベースの更新が必要かどうかを確認する:</p> <pre> <code class="language-bash">$ drush updbst -------- ------------------ --------------- ------------------------------------------ Module Update ID Type Description -------- ------------------ --------------- ------------------------------------------ locale 9101 hook_update_n Delete an unnecessary index on the locales_location table. system claro_dropbutton post-update Clear caches due to trustedCallbacks _variants changing in ClaroPreRender. system schema_version_i post-update Update schema version to integers. nt @see https:www.drupal.orgprojectdrupalissues3 143713 views configuration_en post-update Clear errors caused by relationships to tity_relationshi configuration entities. ps -------- ------------------ --------------- ------------------------------------------ </code></pre> <p>必要とわかったので、更新を実行する:</p> <pre> <code class="language-bash">$ drush updb -y -------- --------------- --------------- ------------------------------------- Module Update ID Type Description -------- --------------- --------------- ------------------------------------- locale 9101 hook_update_n Delete an unnecessary index on the locales_location table. system claro_dropbut post-update Clear caches due to ton_variants trustedCallbacks changing in ClaroPreRender. system schema_versio post-update Update schema version to integers. n_int @see https:www.drupal.orgprojectdrupalis sues3143713 views configuration post-update Clear errors caused by _entity_relat relationships to configuration ionships entities. -------- --------------- --------------- ------------------------------------- // Do you wish to run the specified pending updates?: yes. &gt; [notice] Update started: locale_update_9101 &gt; [notice] Update completed: locale_update_9101 &gt; [notice] Update started: system_post_update_claro_dropbutton_variants &gt; [notice] Update completed: system_post_update_claro_dropbutton_variants &gt; [notice] Update started: system_post_update_schema_version_int &gt; [notice] Update completed: system_post_update_schema_version_int &gt; [notice] Update started: views_post_update_configuration_entity_relationships &gt; [notice] Update completed: views_post_update_configuration_entity_relationships [success] Finished performing updates. </code></pre> <p>最後にキャッシュをクリアする:</p> <pre> <code class="language-bash">$ drush cr [success] Cache rebuild complete. </code></pre> <p>以上で完了。</p> <p>ブラウザで管理画面を開いてサイトの状態を確認:</p> <p><img alt="更新後のサイトの状態" data-entity-type="file" data-entity-uuid="insert-image-9c204ef3-d414-45c1-92e7-05d2fc3e180d" data-insert-attach="{&quot;id&quot;:&quot;9c204ef3-d414-45c1-92e7-05d2fc3e180d&quot;,&quot;attributes&quot;:{&quot;alt&quot;:[&quot;alt&quot;,&quot;description&quot;],&quot;title&quot;:[&quot;title&quot;]}}" data-insert-class="" data-insert-type="image" height="325" src="/system/files/2020-12/status910.png" width="593" /></p> <p>大丈夫そう。サイトのログにも特にエラーは記録されていないので、これでしばらく運用してみることにする。</p> <p>なお、今回の更新では .htaccess と robots.txt に変更があるが、このサイトは両ファイルともデフォルトのままなので調整作業は特に必要なし。</p> <p>これから新機能を確認していこう。</p> <h2>参考資料</h2> <ul> <li><a href="https://www.drupal.org/project/drupal/releases/9.1.0" target="_blank">drupal 9.1.0 リリースノート</a></li> <li><a href="https://www.drupal.org/docs/updating-drupal/updating-drupal-core-via-composer" target="_blank">Updating Drupal core via Composer</a></li> </ul> </div> <div class="w3-section field field--name-field-tag field--type-entity-reference field--label-inline"> <label class="field__label">Tag</label> <div class="field__items"> <div class="w3-bar-item field__item"><a href="/taxonomy/term/5" hreflang="ja">Drupal9</a></div> <div class="w3-bar-item field__item"><a href="/taxonomy/term/31" hreflang="ja">Composer</a></div> <div class="w3-bar-item field__item"><a href="/taxonomy/term/30" hreflang="ja">Drush</a></div> </div> </div> Thu, 03 Dec 2020 05:52:18 +0000 shirane 117 at https://www.white-root.com