tag:crieit.net,2005:https://crieit.net/users/arm-band/feed
arm-bandの投稿 - Crieit
Crieitでユーザーarm-bandによる最近の投稿
2022-07-14T23:39:50+09:00
https://crieit.net/users/arm-band/feed
tag:crieit.net,2005:PublicArticle/18239
2022-07-14T23:39:50+09:00
2022-07-14T23:39:50+09:00
https://crieit.net/posts/extract-array-of-diff-exclude-duplicated-between-two-arraies-by-javascript-20220715
JavaScript で互いにオブジェクトを要素に持つ配列2つを比較して、重複していない要素のみを取り出す
<h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2>
<p>JavaScript で、互いにオブジェクトを要素に持つ配列2つを比較して、重複していない要素のみを取り出す処理を試みたのでメモしておきます。</p>
<h2 id="前提"><a href="#%E5%89%8D%E6%8F%90">前提</a></h2>
<p>前提として、次のような2つの配列があったとします。</p>
<ul>
<li>互いにオブジェクトを要素として持つ配列</li>
<li>配列2は配列1の子集合(サブセット)</li>
</ul>
<p>この2つの配列を比較して、配列1の中から<strong>配列2に含まれない要素のみ</strong>の配列を作りたい、と考えました。</p>
<p>想定する最終結果も併せて付記しておきます。</p>
<h3 id="配列1"><a href="#%E9%85%8D%E5%88%971">配列1</a></h3>
<pre><code class="json">[
{
"value": "value-0",
"label": "Château d'If"
},
{
"value": "value-1",
"label": "Marseille"
},
{
"value": "value-2",
"label": "France"
},
{
"value": "value-3",
"label": "Le Comte de Monte-Cristo"
},
{
"value": "value-4",
"label": "Alexandre Dumas"
},
{
"value": "value-5",
"label": "Rhino"
},
{
"value": "value-6",
"label": "prison"
},
{
"value": "value-7",
"label": "François I"
},
{
"value": "value-8",
"label": "Jean-Baptiste Kléber"
}
]
</code></pre>
<h3 id="配列2"><a href="#%E9%85%8D%E5%88%972">配列2</a></h3>
<pre><code class="json">[
{
"value": "value-0",
"label": "Château d'If"
},
{
"value": "value-1",
"label": "Le Comte de Monte-Cristo"
},
{
"value": "value-2",
"label": "Alexandre Dumas"
}
]
</code></pre>
<h3 id="得たい配列"><a href="#%E5%BE%97%E3%81%9F%E3%81%84%E9%85%8D%E5%88%97">得たい配列</a></h3>
<pre><code class="json">[
{
"value": "value-1",
"label": "Marseille"
},
{
"value": "value-2",
"label": "France"
},
{
"value": "value-5",
"label": "Rhino"
},
{
"value": "value-6",
"label": "prison"
},
{
"value": "value-7",
"label": "François I"
},
{
"value": "value-8",
"label": "Jean-Baptiste Kléber"
}
]
</code></pre>
<p>「2つの配列で重複した要素を除去した配列を生成する」というのはいくつか記事を見かけました。</p>
<p>しかし、それらは1次元配列のサンプルばかりだった上に、今回はさらに「要素の中の <code>label</code>キー の値で比較したい」という内容だったので、さらにハードルが上がりました。</p>
<h2 id="コード"><a href="#%E3%82%B3%E3%83%BC%E3%83%89">コード</a></h2>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://www.nxworld.net/js-array-filter-snippets.html">JavaScript:filter()を使って配列内の重複要素を削除・取得したり、2つの配列から共通要素を取得する方法 - NxWorld</a></li>
</ul>
<p>最終的にはこちらのコードをベースにして作りました。</p>
<p>サンプルは以下。</p>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://arm-band.github.io/test-l-enfer-de-chateau-d-if/">Home - L'enfer de Chateau d'If</a></li>
</ul>
<pre><code class="javascript">// 配列
const Ar1 = [
// 略
];
const Ar2 = [
// 略
];
// 重複要素を除去した配列を返す関数
const getArraysDiff = (array1, array2) => {
// 引数の各々の配列から label のみの配列を生成
const array1LabelArray = array1.map((itm) => {
return itm.label;
});
const array2LabelArray = array2.map((itm) => {
return itm.label;
});
// label のみの配列で比較
const arr1 = [...new Set(array1LabelArray)];
const arr2 = [...new Set(array2LabelArray)];
return [...arr1, ...arr2].filter((val) => {
return !arr1.includes(val) || !arr2.includes(val);
});
};
// 上述関数で重複除去した label の配列を得る
const ChateuDiff = getArraysDiff(Ar1, Ar2);
// filter メソッドで、元配列 から該当する label が存在する要素を除去する
const enferChateuDiff = Ar1.filter((item) => {
return ChateuDiff.includes(item.label);
});
</code></pre>
<p>ざっくりこのような処理で想定していた結果を得られました。</p>
<h2 id="余談"><a href="#%E4%BD%99%E8%AB%87">余談</a></h2>
<p>何故このようなことをしようかと思ったかというと、 <a target="_blank" rel="nofollow noopener" href="https://react-select.com/home">React Select</a> で <code>defaultCalue</code> で指定した選択済みの項目が選択候補にも上がってしまっていたので、除外しようとしたためでした。</p>
<p>が、そもそも React Select 側は選択済み項目を除外する機能を元々持っていたので上述の処理は不要ということが分かったため、今回のコードは未使用となりました。</p>
<p>ちなみに、この機能が働かなかった原因は API で取得した 値から 上述のような <code>label</code> と <code>value</code> のオブジェクトを生成するループ処理の際に、 <code>value</code> に ID の数値を振り方を間違えていたため、 React Select から「異なる値」として認識されてしまっていたためでした。</p>
<p>また、仮に今回の処理をかけたとしても、初期表示では上手く選択済み項目が除外されますが、選択済み項目を削除した場合は選択可能な項目として再度選択肢に復活させる必要があるため、かなり手間がかかることが想定されたためオミットしていたと思います。</p>
<p>……本当、 React Select 側に標準搭載されていて良かったです。</p>
<h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2>
<h3 id="本題"><a href="#%E6%9C%AC%E9%A1%8C">本題</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://www.nxworld.net/js-array-filter-snippets.html">JavaScript:filter()を使って配列内の重複要素を削除・取得したり、2つの配列から共通要素を取得する方法 - NxWorld</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://www.dkrk-blog.net/javascript/duplicate_an_array">配列同士で重複する値があるか確認する | grgr-dkrkのブログ</a></li>
</ul>
<h3 id="label のみの配列を作る"><a href="#label+%E3%81%AE%E3%81%BF%E3%81%AE%E9%85%8D%E5%88%97%E3%82%92%E4%BD%9C%E3%82%8B">label のみの配列を作る</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/tsu_eng/items/57f9d3919bf175188033">JavaScriptで連想配列から特定のキーだけ抽出 - Qiita</a></li>
</ul>
<h3 id="配列操作"><a href="#%E9%85%8D%E5%88%97%E6%93%8D%E4%BD%9C">配列操作</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/takeharu/items/d75f96f81ff83680013f">JavaScriptの配列の使い方まとめ。要素の追加,結合,取得,削除。 - Qiita</a></li>
</ul>
<h3 id="JSON のフォーマット"><a href="#JSON+%E3%81%AE%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%83%E3%83%88">JSON のフォーマット</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/unsoluble_sugar/items/7df08527215ea92831a6">JSON.stringifyの出力結果を整形して可読性を向上させる - Qiita</a></li>
</ul>
arm-band
tag:crieit.net,2005:PublicArticle/18238
2022-07-14T23:32:07+09:00
2022-07-14T23:32:07+09:00
https://crieit.net/posts/use-clipboard-api-by-react-20220714
React (create-react-app) で Clipboard API にアクセスするために localhost:3000 をhttpsにする
<h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/flu_bit/items/659a59260446117e9548">クリップボードにテキストをコピーするボタンの実装 - Qiita</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://mseeeen.msen.jp/copy-text-to-clipboard-with-javascript-in-2021/">JavaScript でクリップボードにテキストをコピーする (2021年版) | MSeeeeN | 大阪発 IT メディア by MSEN</a></li>
</ul>
<p>React アプリ(<code>create-react-app</code>(<code>react-scripts</code>))で Clipboard API を使おうとしたところ、次のエラーが発生してしまいました。</p>
<blockquote>
<p>Uncaught TypeError: Cannot read property ‘writeText’ of undefined ...</p>
</blockquote>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://owatata.com/2020/10/06/%E3%80%90javascript%E3%80%91navigator-clipboard-writetex%E3%81%A7-uncaught-typeerror-cannot-read-property-writetext-of-undefined/">【javascript】navigator.clipboard.writeTextで Uncaught TypeError: Cannot read property ‘writeText’ of undefined – オワタ太のブログ</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://propg.ee-mall.info/%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0/javascript/js-http%E7%92%B0%E5%A2%83%E3%81%A7%E3%81%AFnavigator-clipboard-writetext%E3%81%8C%E3%82%A8%E3%83%A9%E3%83%BC%E3%81%AB%E3%81%AA%E3%82%8B/">[JS] http環境ではnavigator.clipboard.writeTextがエラーになる - プロプログラマ -Flex,Air,C#,Oracle,HTML5+JS-</a></li>
</ul>
<p>調べてみたところ、 https でないと使用できないようです。</p>
<p>そこで通常 <code>npm start</code> で自動起動してくる <code>http://localhost:3000/</code> を <code>https://localhost:3000/</code> にする必要に迫られたので、その方法を書き留めておきます。</p>
<h2 id="対処"><a href="#%E5%AF%BE%E5%87%A6">対処</a></h2>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/sutara79/items/21a068494bc3a08a4803">XAMPP for WindowsでSSLを有効にする - Qiita</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://kdnakt.hatenablog.com/entry/2020/06/07/170000">Create React Appで作成したアプリをローカルのMacBook上でHTTPSで動作させる - kdnakt blog</a></li>
</ul>
<p>今回は XAMPP の入っているPCでそのオレオレSSL証明書が既にあったので、それを流用します。</p>
<p>証明書を <code>server.crt</code>、 秘密鍵を <code>server.key</code> という名前でコピーして、 <code>create-react-app</code> プロジェクト配下に設置します(今回は <code>ssl/</code> ディレクトリを切りました)。</p>
<p>次に、 <code>.env</code> に次の3行を足して再度 <code>npm start</code> します。</p>
<pre><code>HTTPS=true
SSL_CRT_FILE=./ssl/server.crt
SSL_KEY_FILE=./ssl/server.key
</code></pre>
<p>これで <code>https://localhost:3000/</code> でアプリにアクセスでき、無事 Clipboard API も動かすことができました。</p>
<h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2>
<h3 id="Clipboard API"><a href="#Clipboard+API">Clipboard API</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/flu_bit/items/659a59260446117e9548">クリップボードにテキストをコピーするボタンの実装 - Qiita</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://mseeeen.msen.jp/copy-text-to-clipboard-with-javascript-in-2021/">JavaScript でクリップボードにテキストをコピーする (2021年版) | MSeeeeN | 大阪発 IT メディア by MSEN</a></li>
</ul>
<h3 id="Uncaught TypeError: Cannot read property ‘writeText’ of undefined"><a href="#Uncaught+TypeError%3A+Cannot+read+property+%E2%80%98writeText%E2%80%99+of+undefined">Uncaught TypeError: Cannot read property ‘writeText’ of undefined</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://owatata.com/2020/10/06/%E3%80%90javascript%E3%80%91navigator-clipboard-writetex%E3%81%A7-uncaught-typeerror-cannot-read-property-writetext-of-undefined/">【javascript】navigator.clipboard.writeTextで Uncaught TypeError: Cannot read property ‘writeText’ of undefined – オワタ太のブログ</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://propg.ee-mall.info/%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0/javascript/js-http%E7%92%B0%E5%A2%83%E3%81%A7%E3%81%AFnavigator-clipboard-writetext%E3%81%8C%E3%82%A8%E3%83%A9%E3%83%BC%E3%81%AB%E3%81%AA%E3%82%8B/">[JS] http環境ではnavigator.clipboard.writeTextがエラーになる - プロプログラマ -Flex,Air,C#,Oracle,HTML5+JS-</a></li>
</ul>
<h3 id="https化"><a href="#https%E5%8C%96">https化</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/sutara79/items/21a068494bc3a08a4803">XAMPP for WindowsでSSLを有効にする - Qiita</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://kdnakt.hatenablog.com/entry/2020/06/07/170000">Create React Appで作成したアプリをローカルのMacBook上でHTTPSで動作させる - kdnakt blog</a></li>
</ul>
arm-band
tag:crieit.net,2005:PublicArticle/18235
2022-07-12T23:57:06+09:00
2022-07-12T23:57:06+09:00
https://crieit.net/posts/create-react-app-ignores-x-high-severity-vulnerabilities-20220713
create-react-app(react-scripts) の X high severity vulnerabilities の警告について
<h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2>
<p><code>create-react-app</code>(<code>react-scripts</code>) で作ったアプリに対して、パッケージ追加等をすると次のような脆弱性の警告が表示されます。</p>
<blockquote>
<p>up to date, audited 1485 packages in 5s</p>
<p>197 packages are looking for funding<br />
run <code>npm fund</code> for details</p>
<p>6 high severity vulnerabilities</p>
<p>To address all issues (including breaking changes), run:<br />
npm audit fix --force</p>
<p>Run <code>npm audit</code> for details.</p>
</blockquote>
<p>脆弱性がある、と言われるとさすがに放置はできないのでどうにかできないものかと調べました。</p>
<h2 id="対応"><a href="#%E5%AF%BE%E5%BF%9C">対応</a></h2>
<p>結論から言うと、今回のケースでは無視して良い、とのことでした。</p>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://zenn.dev/appare45/articles/7f667031aab94b">create-react-appでは脆弱性の警告が出るが無視して良い</a></li>
</ul>
<p>理由としては</p>
<ul>
<li>警告の発出元である <code>npm audit</code> 自体の警告が不正確</li>
<li>今回のケース(<code>create-react-app</code>(<code>react-scripts</code>))では依存パッケージはビルドツールとして用いられており、殆どの場合は成果物に含まれるわけではない (と思われる)</li>
<li>ビルドツールとして脆弱性があればアップデートがあるはず</li>
</ul>
<p>といったところのようです。</p>
<p>言われて見れば確かに、というところですね。</p>
<p>それでも気になる場合は</p>
<ul>
<li><code>dependencies</code> にある <code>react-scripts</code> を <code>devDependencies</code> に移動させる
<ul>
<li><code>npm audit --production</code> を使用する</li>
</ul></li>
<li>パッケージインストール時は <code>npm install --no-audit</code> で <code>audit</code> を使用しないように指定する</li>
</ul>
<p>とすることで警告を表示させなくすることができるとのこと。</p>
<h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://zenn.dev/appare45/articles/7f667031aab94b">create-react-appでは脆弱性の警告が出るが無視して良い</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/riversun/items/7f1679509f38b1ae8adb">npmパッケージのvulnerability対応フロー - Qiita</a></li>
</ul>
arm-band
tag:crieit.net,2005:PublicArticle/18234
2022-07-12T23:45:36+09:00
2022-07-12T23:45:36+09:00
https://crieit.net/posts/mysql80-with-recursive-in-standalone-eloquent-20220712
MySQL8.0 の再帰クエリを単独の Eloquent で使ってみる
<p>ちょうどやりたいことができそうだったので、 MySQL8.0 で新しく追加された再帰クエリを使ってみました。なお、単独インストールした Eloquent での使用を想定しているので、リレーションの機能をそのまま使うのは難しそうなケースでした。</p>
<h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2>
<p>内容としてはありがちな部署マスタのようなもので、親子関係まで見たいと考えたためでした。</p>
<div class="table-responsive"><table>
<thead>
<tr>
<th align="right">id</th>
<th align="right">parent</th>
<th align="left">name</th>
</tr>
</thead>
<tbody>
<tr>
<td align="right">1</td>
<td align="right">0</td>
<td align="left">営業部</td>
</tr>
<tr>
<td align="right">2</td>
<td align="right">1</td>
<td align="left">営業1課</td>
</tr>
<tr>
<td align="right">3</td>
<td align="right">1</td>
<td align="left">営業2課</td>
</tr>
<tr>
<td align="right">4</td>
<td align="right">0</td>
<td align="left">システム部</td>
</tr>
<tr>
<td align="right">5</td>
<td align="right">4</td>
<td align="left">開発課</td>
</tr>
<tr>
<td align="right">6</td>
<td align="right">4</td>
<td align="left">運用保守課</td>
</tr>
<tr>
<td align="right">7</td>
<td align="right">5</td>
<td align="left">フロントエンドグループ</td>
</tr>
<tr>
<td align="right">8</td>
<td align="right">5</td>
<td align="left">バックエンドグループ</td>
</tr>
</tbody>
</table></div>
<p>イメージとしてはこんなイメージのテーブルがあったとして、例えば「システム部」と指定した場合、「開発課」「運用保守課」「フロントエンドグループ」「バックエンドグループ」のIDを子部署として参照したい、というようなケースです。</p>
<h2 id="コード"><a href="#%E3%82%B3%E3%83%BC%E3%83%89">コード</a></h2>
<pre><code class="php">$hogeObjectsArray = $this->dbConnect->connection('hogera')
->select("
WITH recursive child(
depth,
id,
parent,
name
) AS (
SELECT 0,
id,
parent,
name
FROM hoge
WHERE id = {$foo}
UNION ALL
SELECT child.depth + 1,
hoge.id,
hoge.parent,
hoge.name
FROM hoge, child
WHERE hoge.parent = child.id
)
SELECT depth,
id,
parent,
name
FROM child
ORDER BY
depth
");
</code></pre>
<p>最終的には上述の形で取得できました。</p>
<p><code>->table($this->table)</code> 等でテーブルを指定せず、いきなり <code>->select()</code> しているのがミソです。</p>
<h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2>
<h3 id="with recursive"><a href="#with+recursive">with recursive</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://codezine.jp/article/detail/2679">MySQL 8.0の再帰With句のサンプル集 (1\/3):CodeZine(コードジン)</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://zenn.dev/rstliz/articles/65278841ac26f7">MySQL 再帰クエリで階層構造のデータを一度に取得</a></li>
</ul>
<h3 id="単独の Eloquent 内で with recursive を使用したい"><a href="#%E5%8D%98%E7%8B%AC%E3%81%AE+Eloquent+%E5%86%85%E3%81%A7+with+recursive+%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%97%E3%81%9F%E3%81%84">単独の Eloquent 内で with recursive を使用したい</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/okumurakengo/items/0670ce39d758ed65e8e7">100行ぐらいのSQLをLaravelのEloquent\/Query Builderで頑張る - Qiita</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://leben.mobi/blog/laravel_raw_sql/php/">Laravel5で生のSQLを実行する方法</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/Yinaura/items/562079d937021f1fc30a">MySQL — サブクエリに AS を付けないとエラーを起こす | Every derived table must have its own alias - Qiita</a></li>
</ul>
<h4 id="メソッド確認"><a href="#%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E7%A2%BA%E8%AA%8D">メソッド確認</a></h4>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://laravel.com/api/5.8/Illuminate/Database/Capsule/Manager.html#method_connection">Illuminate\Database\Capsule\Manager | Laravel API</a></li>
<li>query(): <a target="_blank" rel="nofollow noopener" href="https://laravel.com/api/5.8/Illuminate/Database/Connection.html#method_query">Illuminate\Database\Connection | Laravel API</a></li>
<li>Builder: <a target="_blank" rel="nofollow noopener" href="https://laravel.com/api/5.8/Illuminate/Database/Query/Builder.html">Illuminate\Database\Query\Builder | Laravel API</a></li>
<li>selectRaw(): <a target="_blank" rel="nofollow noopener" href="https://laravel.com/api/5.8/Illuminate/Database/Query/Builder.html#method_selectRaw">Illuminate\Database\Query\Builder | Laravel API</a></li>
<li>select(): <a target="_blank" rel="nofollow noopener" href="https://laravel.com/api/5.8/Illuminate/Database/Query/Builder.html#method_select">Illuminate\Database\Query\Builder | Laravel API</a></li>
<li>raw(): <a target="_blank" rel="nofollow noopener" href="https://laravel.com/api/5.8/Illuminate/Database/Connection.html#method_raw">Illuminate\Database\Connection | Laravel API</a></li>
</ul>
<h4 id="unionAll"><a href="#unionAll">unionAll</a></h4>
<ul>
<li>unionAll(): <a target="_blank" rel="nofollow noopener" href="https://laravel.com/api/5.8/Illuminate/Database/Query/Builder.html#method_unionAll">Illuminate\Database\Query\Builder | Laravel API</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://mysql.sql55.com/sql/mysql-union.php">MySQL の UNION \/ UNION ALL - MySQL の基礎 - MySQL 入門</a></li>
</ul>
<h4 id="(未使用) 外部パッケージ"><a href="#%28%E6%9C%AA%E4%BD%BF%E7%94%A8%29+%E5%A4%96%E9%83%A8%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8">(未使用) 外部パッケージ</a></h4>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://github.com/soda-framework/eloquent-closure">GitHub - soda-framework\/eloquent-closure</a></li>
</ul>
<h4 id="(未使用) もはや生PDOに回帰するか"><a href="#%28%E6%9C%AA%E4%BD%BF%E7%94%A8%29+%E3%82%82%E3%81%AF%E3%82%84%E7%94%9FPDO%E3%81%AB%E5%9B%9E%E5%B8%B0%E3%81%99%E3%82%8B%E3%81%8B">(未使用) もはや生PDOに回帰するか</a></h4>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/jjongs08/items/f53bc04d582e63e7ad10">Laravelでクエリ結果を配列で取得する方法 - Qiita</a></li>
</ul>
<h4 id="whereIn()"><a href="#whereIn%28%29">whereIn()</a></h4>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/miriwo/items/553dccbae72a25b5467b">laravel クエリビルダ whereInを使って賢く取得情報を絞ろう - Qiita</a></li>
</ul>
arm-band
tag:crieit.net,2005:PublicArticle/18211
2022-06-09T23:58:40+09:00
2022-06-09T23:58:40+09:00
https://crieit.net/posts/eloquent-use-db-raw-a-facade-root-has-not-been-set-error-20220610
Eloquent 単独で COUNT(*) を使用したいが DB:raw() を使用すると A facade root has not been set. エラーになる
<h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2>
<p>Eloquent 単独利用しているプログラムの中で COUNT(*) を使用したくなったのでその方法をメモ。</p>
<p>単純に件数をカウントしてその数値を使いたいだけであれば</p>
<pre><code class="php">$data_count = $dbConnect->table('hoge')
->count();
</code></pre>
<p>で済ませることができます。が、今回はサブクエリとして使用した方がコード的にすっきりしそうな状況でした。</p>
<p>先の方法ではコード上は関連性のない別のクエリをもう1つ記述しなければならないので如何なものかと思い……。</p>
<h2 id="調査"><a href="#%E8%AA%BF%E6%9F%BB">調査</a></h2>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://awesome-programmer.hateblo.jp/entry/2019/07/03/162835">LaravelのEloquentで集計関数Count()などを使うとき - バックエンドとフロントエンドを行き来するWEBプログラマ―のメモ帳</a></li>
</ul>
<p>検索したところ、 SQL で <code>COUNT(*)</code> を使用する場合は <code>DB:raw()</code> を使うとのこと。</p>
<p>ただし、 Laravel の中ならばいざ知らず今回は Eloquent 単独使用なので、 <code>DB</code> なるクラスはどこから来るのか……となってしまいました。しかも <code>::raw()</code> は static メソッドですし。</p>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://readouble.com/laravel/8.x/ja/queries.html">データベース:クエリビルダ 8.x Laravel</a></li>
</ul>
<p>引き続き検索すると、 <code>DB</code> はファサードでした。</p>
<p>しかし、 Laravel 公式のサンプルのように</p>
<pre><code class="php">use Illuminate\Support\Facades\DB;
</code></pre>
<p>を追加すると、</p>
<blockquote>
<p>A facade root has not been set.</p>
</blockquote>
<p>というエラーが発生して怒られてしまいます。</p>
<p>通常の Laravel 内で使用する場合はインスタンスとの紐付けが上手く行われますが、今回は Eloquent のみの単体使用なのでこのパターンではできそうにないように見受けられました (できたとしても素直に Laravel を使った方が早そう)。</p>
<p>最終的には</p>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/s-higuma/items/ab8fa110b13301d0c959">Eloquentを単体で導入している時にDBファサードを使用する方法 - Qiita</a></li>
</ul>
<p>この記事の方法で</p>
<pre><code class="php">use \Illuminate\Database\Capsule\Manager as DB;
</code></pre>
<p>を <code>DB::raw()</code> を使うクラスのファイルの中で再度読み込ませる (別のファイルでDB接続用のインスタンスを生成していて、それを DI していたので今回は「再度読み込ませる」という表現) ことで乗り越えることができました。</p>
<h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2>
<h3 id="count()"><a href="#count%28%29">count()</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/zaburo/items/d665804f8ea850502c64">Laravelでの基本的なリレーションシップもしくはJOIN - Qiita</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://www.84kure.com/blog/2017/07/12/laravel-eloquent%E3%81%A7%E3%82%AB%E3%82%A6%E3%83%B3%E3%83%88%E3%81%99%E3%82%8B%E3%81%A8%E3%81%8D%E3%81%AE%E6%B3%A8%E6%84%8F/">[Laravel] Eloquentでカウントするときの注意 – 端くれプログラマの備忘録</a></li>
</ul>
<h3 id="DB::raw()"><a href="#DB%3A%3Araw%28%29">DB::raw()</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://awesome-programmer.hateblo.jp/entry/2019/07/03/162835">LaravelのEloquentで集計関数Count()などを使うとき - バックエンドとフロントエンドを行き来するWEBプログラマ―のメモ帳</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://readouble.com/laravel/8.x/ja/queries.html">データベース:クエリビルダ 8.x Laravel</a></li>
</ul>
<h3 id="A facade root has not been set. エラー"><a href="#A+facade+root+has+not+been+set.+%E3%82%A8%E3%83%A9%E3%83%BC">A facade root has not been set. エラー</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://re-engines.com/2020/05/11/laravel-facade/">LaravelのFacade(ファサード)とは? 何気なく使用していた裏側の仕組みを解説! | RE:ENGINES</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://www.wakuwakubank.com/posts/453-laravel-sql-basic/">Laravel|DBデータ操作(DBファサード, クエリビルダ, Eloquent) - わくわくBank</a> (バージョン古い)</li>
<li><a target="_blank" rel="nofollow noopener" href="https://matchingood-engineer.hatenablog.com/">エンジニアブログ</a> (バージョン古い)</li>
<li><a target="_blank" rel="nofollow noopener" href="https://stackoverflow.com/questions/22925451/how-can-i-query-raw-via-eloquent">php - How can I query raw via Eloquent? - Stack Overflow</a> (バージョン古い)</li>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/s-higuma/items/ab8fa110b13301d0c959">Eloquentを単体で導入している時にDBファサードを使用する方法 - Qiita</a></li>
</ul>
arm-band
tag:crieit.net,2005:PublicArticle/18207
2022-05-31T23:48:56+09:00
2022-05-31T23:48:56+09:00
https://crieit.net/posts/stdclass-and-illuminate-support-collection-convert-array-20220601
Illuminate\Support\Collection を配列に変換する
<h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2>
<p>Eloquent で取得したデータはそのままだと Object の Collection になっているのですが、配列として扱いたいケースが出てきました。そこで先の Collection を配列に変換するメモを残しておきます。</p>
<h2 id="コード"><a href="#%E3%82%B3%E3%83%BC%E3%83%89">コード</a></h2>
<pre><code class="php">$employeesCollectionObject = $dbConnect->table($this->table)
->where(
[
['id', '=', $id],
]
)
->select(
'employee_id',
'depart',
)
->get();
$employeesCollectionArray = $employeesCollectionObject->map(function ($item, $key) {
return [
'tori_id' => $item->employee_id,
'domain' => $item->depart,
];
});
</code></pre>
<p>例えばこうすることで、</p>
<pre><code class="php">object(Illuminate\Support\Collection)[xxx]
protected 'items' =>
array (size=10)
0 =>
object(stdClass)[yyy]
public 'employee_id' => string '001' (length=3)
public 'depart' => string 'sales' (length=5)
1 =>
object(stdClass)[zzz]
public 'employee_id' => string '002' (length=3)
public 'depart' => string 'accounting' (length=10)
2 =>
...
</code></pre>
<p>このようなコレクション→オブジェクトの配列が</p>
<pre><code class="php">object(Illuminate\Support\Collection)[xxx]
protected 'items' =>
array (size=10)
0 =>
array (size=2)
'employee_id' => string '001' (length=3)
'depart' => string 'sales' (length=5)
1 =>
array (size=2)
'employee_id' => string '002' (length=3)
'depart' => string 'accounting' (length=10)
...
</code></pre>
<p>このようなコレクション→連想配列の配列になります。各プロパティへのアクセス等が犠牲になりますが、今回は織り込み済み。</p>
<p>さらに <code>->toArray()</code> を使って</p>
<pre><code class="php">$employeesCollectionArray = $employeesCollectionObject->map(function ($item, $key) {
return [
'tori_id' => $item->employee_id,
'domain' => $item->depart,
];
});
$customersArray = $employeesCollectionArray->toArray();
</code></pre>
<p>こうすることで</p>
<pre><code class="php">array (size=10)
0 =>
array (size=2)
'employee_id' => string '001' (length=3)
'depart' => string 'sales' (length=5)
1 =>
array (size=2)
'employee_id' => string '002' (length=3)
'depart' => string 'accounting' (length=10)
...
</code></pre>
<p>配列→連想配列にできました。</p>
<p>今回はこれで対応。</p>
<h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://techplay.jp/column/630">Laravel collectionの使い方【初心者向け】|TECH PLAY Magazine [テックプレイマガジン]</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/ucan-lab/items/47638a7b52090f59c2bf">Laravel Collection型の配列変換 toArray と all の違い - Qiita</a></li>
</ul>
arm-band
tag:crieit.net,2005:PublicArticle/18203
2022-05-31T00:00:59+09:00
2022-05-31T00:00:59+09:00
https://crieit.net/posts/docker-compose-conditional-branch-volume-mount-20220531
Docker Compose で条件に応じてマウントするボリュームを切り替える
<h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2>
<p>Docker Compose を使って複数の似たようなユースケースで検証を繰り返している中で、</p>
<ul>
<li>この検証では Apache のドキュメントルートと MySQL のデータを永続化して試験したい</li>
<li>その検証ではどちらも永続化しないで試験したい</li>
<li>あの検証ではファイル数が膨大な上にオペレーションでその膨大なファイルに変更ををかけるので Apache のファイル永続化をしているとディスクIOがボトルネックになって検証に時間がかかり過ぎる</li>
</ul>
<p>という具合に、同じ Docker Compose のベースを使いつつもユースケースに応じてボリュームをマウントするかどうかを条件によって切り分けたくなりました。</p>
<p>検証ごとに別リポジトリにフォークなりブランチなり切っていくと数が多くなってしまい管理しきれなくなる恐れがあるので、なるべくベースのコードは共通にしておきたいところです。</p>
<p>そこで、「同じ Docker Compose のコードで、条件に応じて処理を変化させる(今回はマウントするボリュームを切り替える)」ということが必要になりました。</p>
<h2 id="結果"><a href="#%E7%B5%90%E6%9E%9C">結果</a></h2>
<p>最初に結果を記すと、今回は <code>docker-compose</code> を <code>up</code> する際に <code>-f</code>オプション を付ける、という方法を選択しました。</p>
<h3 id="ボリュームをマウントしない場合"><a href="#%E3%83%9C%E3%83%AA%E3%83%A5%E3%83%BC%E3%83%A0%E3%82%92%E3%83%9E%E3%82%A6%E3%83%B3%E3%83%88%E3%81%97%E3%81%AA%E3%81%84%E5%A0%B4%E5%90%88">ボリュームをマウントしない場合</a></h3>
<pre><code class="bash">> docker-compose up -d
</code></pre>
<h3 id="ボリュームをマウントする場合"><a href="#%E3%83%9C%E3%83%AA%E3%83%A5%E3%83%BC%E3%83%A0%E3%82%92%E3%83%9E%E3%82%A6%E3%83%B3%E3%83%88%E3%81%99%E3%82%8B%E5%A0%B4%E5%90%88">ボリュームをマウントする場合</a></h3>
<pre><code class="bash">> docker-compose -f docker-compose.yml -f docker-compose.volumes.yml up -d
</code></pre>
<p>このように使い分けるイメージです。</p>
<h2 id="コード"><a href="#%E3%82%B3%E3%83%BC%E3%83%89">コード</a></h2>
<p>上述で指定した各 <code>yml</code> ファイルは次のようになっています。</p>
<h3 id="docker-compose.yml"><a href="#docker-compose.yml">docker-compose.yml</a></h3>
<pre><code class="yml">version: '3.8'
services:
web:
build:
context: ./apache/docker
dockerfile: Dockerfile
args:
WEB_ROOT_DIRECTORY: $WEB_ROOT_DIRECTORY
## 略
volumes:
# workspace
- ./workspace:/workspace
# docker settings template
- ./template:/template
## 略
db:
build:
context: ./mysql/docker
dockerfile: Dockerfile
## 略
volumes:
# workspace
- ./workspace:/workspace
# docker settings template
- ./template:/template
## 略
</code></pre>
<p>オプションなしで使用するデフォルトの <code>docker-compose.yml</code> はエントリポイントのシェルスクリプトや設定、ログの置き場所など最低限のディレクトリをマウントするに留めています。</p>
<h3 id="docker-compose.volumes.yml"><a href="#docker-compose.volumes.yml">docker-compose.volumes.yml</a></h3>
<pre><code class="yml">version: '3.8'
services:
web:
volumes:
# apache virtual host
- ./apache/www:/var/www/$WEB_ROOT_DIRECTORY/web
db:
volumes:
# mysql data
- ./mysql/data:/var/lib/mysql/data
</code></pre>
<p>一方、ボリュームマウント用の <code>yml</code> ファイルでは冒頭の通り Apache のドキュメントルート想定のディレクトリや MySQL のデータファイルのディレクトリをマウントするようにしています。</p>
<p>これにより、オプション指定のない場合は後者のファイルが読み込まれないことによりドキュメントルートやデータファイルをマウントせず、オプションを付けるとマウントする、というように挙動を切り替えられる、という寸法です。</p>
<p>ただし、このくらいで済むのでまだ良いですがこれが複雑化すると大量の <code>-f</code>オプション と Composeファイル による長大なコマンドになってしまう恐れがあるのでその点は要改善ですかね。現時点でも <code>readme.md</code> とかにコマンドを書いておかないと一々打ち込んでいられないレベルですし。</p>
<h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2>
<h3 id="-f オプション"><a href="#-f+%E3%82%AA%E3%83%97%E3%82%B7%E3%83%A7%E3%83%B3">-f オプション</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/reireias/items/ecb81e248314253eb156">docker-compose.ymlをDRYに書くテクニック2選 - Qiita</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://www.ykicchan.dev/posts/2020-07-13">docker-compose.yml を環境ごとに分割する - yKicchan's blog</a></li>
</ul>
<h3 id="-f オプションの先"><a href="#-f+%E3%82%AA%E3%83%97%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E5%85%88">-f オプションの先</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://suin.io/535">docker-compose.ymlが環境別に複数ある場合はCOMPOSE_FILEを定義しておくと幸せになれる</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://ikasamak503.hatenablog.com/entry/dry-docker-compose-yaml">docker-compose で複数環境を構築するときの設定をなるべく DRY に書く - ikasama over technology</a></li>
</ul>
<h3 id="Compose ファイルの指定"><a href="#Compose+%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E6%8C%87%E5%AE%9A">Compose ファイルの指定</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://docs.docker.jp/compose/reference/envvars.html#compose-file">Compose CLI 環境変数 — Docker-docs-ja 20.10 ドキュメント</a></li>
</ul>
<h3 id="(未使用) 環境変数"><a href="#%28%E6%9C%AA%E4%BD%BF%E7%94%A8%29+%E7%92%B0%E5%A2%83%E5%A4%89%E6%95%B0">(未使用) 環境変数</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://matsuand.github.io/docs.docker.jp.onthefly/compose/environment-variables/#the-env-file">Compose における環境変数 | Docker ドキュメント</a></li>
</ul>
<h3 id="ボリュームのマウント"><a href="#%E3%83%9C%E3%83%AA%E3%83%A5%E3%83%BC%E3%83%A0%E3%81%AE%E3%83%9E%E3%82%A6%E3%83%B3%E3%83%88">ボリュームのマウント</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://zenn.dev/randd/articles/84ac7de7f22800">Dockerのマウントについて</a></li>
</ul>
arm-band
tag:crieit.net,2005:PublicArticle/18202
2022-05-30T23:58:04+09:00
2022-05-30T23:58:04+09:00
https://crieit.net/posts/docker-container-refer-hostmachine-hosts-20220530
Dockerコンテナ 上の名前解決とホストマシンの Windows の hosts
<p>とある試験で Dockerコンテナ からのみ特別に名前解決したい、というケースが生じました。</p>
<p>そこでホストマシンの Windows の hosts が使えないか検証したのでメモ。</p>
<h2 id="検証"><a href="#%E6%A4%9C%E8%A8%BC">検証</a></h2>
<pre><code class="bash"># yum install -y bind-utils
</code></pre>
<p>まず今回検証したい Dockerコンテナ はデフォルトだと <code>nslookup</code> コマンドが入っていないのでインストール (<code>bind-utils</code> の中に含まれている)。</p>
<pre><code>#192.0.2.1 hoge.example1.jp
</code></pre>
<p>続いてホストマシンの Windows の <code>hosts</code> を上述のように記述。あえてコメントアウトしてあります。</p>
<p>なお、 <code>hoge.example1.jp</code> は公開 DNSサーバ にはレコードが存在しないドメインだとします。</p>
<pre><code class="bash"># nslookup
> hoge.example1.jp
Server: 127.0.0.11
Address: 127.0.0.11#53
** server can't find hoge.example1.jp: NXDOMAIN
</code></pre>
<p>この状態で Dockerコンテナ 上から <code>nslookup</code> 。上述の通り DNSサーバ にレコードが存在しない前提なので、名前解決できません。</p>
<pre><code># nslookup
> example1.jp
Server: 127.0.0.11
Address: 127.0.0.11#53
Non-authoritative answer:
Name: example1.jp
Address: 192.0.2.11
</code></pre>
<p>逆に、 DNSサーバ にレコードが存在する <code>example1.jp</code> は名前解決できます。想定通りですね。</p>
<pre><code>192.0.2.1 hoge.example1.jp
</code></pre>
<p>続いてホストマシンの Windows の <code>hosts</code> に記述をした場合です。</p>
<pre><code class="bash"># nslookup
> hoge.example1.jp
Server: 127.0.0.11
Address: 127.0.0.11#53
Non-authoritative answer:
Name: hoge.example1.jp
Address: 192.0.2.1
</code></pre>
<p>名前解決できました。このことから、 Dockerコンテナ はホストマシンの <code>hosts</code> を参照することが分かりました。</p>
<p>これで検証が進められます。</p>
<h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/nkojima/items/083eed7f835547b0b0f7">使えないコマンドがある時の対応方法 - Qiita</a></li>
</ul>
arm-band
tag:crieit.net,2005:PublicArticle/18196
2022-05-23T23:59:20+09:00
2022-05-23T23:59:20+09:00
https://crieit.net/posts/docker-compose-set-default-enviroment-variables-20220522
Docker Compose で使用する環境変数にデフォルト値を設定する
<p>Docker Compose で使用する <code>.env</code> の環境変数にデフォルト値を設定する方法についてメモ。</p>
<h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2>
<p>PHP Webアプリケーション用のコンテナからDB用のコンテナに接続する際、DB名やユーザ名・パスワードを任意の値に設定したい。</p>
<p>ただし <code>.env</code> は Git 管理にはしたくないので標準では <code>sample.env</code> 等に逃がしており、うっかりファイルをコピー・リネームし忘れていたり、あるいはキー名をタイポして値が消えてしまったりすると困る……ということでデフォルト値を設定できないか、と考えました。</p>
<h2 id="コード"><a href="#%E3%82%B3%E3%83%BC%E3%83%89">コード</a></h2>
<h3 id="docker-compose.yml"><a href="#docker-compose.yml">docker-compose.yml</a></h3>
<pre><code class="yml">version: '3.7'
volumes:
logs:
driver: local
services:
php:
build:
context: ./_docker/dockerfiles/
dockerfile: Dockerfile
working_dir: /var/www
command: php -S 0.0.0.0:8080 -t public
ports:
- 8080:8080
db:
image: mariadb
restart: always
ports:
- ${MYSQL_PORT:-3306}:${MYSQL_PORT:-3306}
environment:
- MYSQL_ROOT_PASSWORD=pwd
- MYSQL_DATABASE=${MYSQL_DBNAME:-test}
- MYSQL_USER=${MYSQL_USER:-user}
- MYSQL_PASSWORD=${MYSQL_PASSWORD:-pwd}
</code></pre>
<h3 id=".env (サンプル)"><a href="#.env+%28%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%29">.env (サンプル)</a></h3>
<pre><code class="env">MYSQL_DBNAME=foo_db
MYSQL_USER=foo_user
MYSQL_PASSWORD="Password-1234"
MYSQL_HOST=192.0.2.1
MYSQL_PORT=3306
</code></pre>
<p>早速ですが用意したコード。</p>
<p>まず基本的に <code>.env</code> を用意すれば、 <code>${HOGE}</code> の形式で環境変数をセットできます。</p>
<p>今回はそこに <code>${HOGE:-FUGA}</code> と、コロン(<code>:</code>)とハイフン(<code>-</code>)を付けた値を追加しました。これにより、「<code>.env</code> に <code>HOGE=PIYO</code> のように <code>HOGE</code> に値がセットされていれば <code>PIYO</code> を、そうでなければデフォルト値として <code>:-</code> の後の文字列 (この場合は <code>FUGA</code>) をセット」となります。</p>
<p>例えば上述の <code>- MYSQL_DATABASE=${MYSQL_DBNAME:-test}</code> の部分では、 MariaDB のコンテナイメージに対して <code>MYSQL_DATABASE</code> キーへ <code>.env</code> の <code>MYSQL_DBNAME</code> に値がセットされていればその値を、そうでなければ <code>test</code> をセット、となります。</p>
<p>なお <code>-</code> がない場合は empty も許容されるので、空文字列が環境変数にセットされるようです。今回はどれも空文字列では都合が宜しくないので全てハイフンありで統一。</p>
<h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2>
<h3 id="デフォルト値"><a href="#%E3%83%87%E3%83%95%E3%82%A9%E3%83%AB%E3%83%88%E5%80%A4">デフォルト値</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/fagai/items/b944950b26af19453c02">案外知られてないdocker-composeの環境変数定義の記法 - Qiita</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://docs.docker.com/compose/compose-file/#interpolation">Compose specification | Docker Documentation</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://matsuand.github.io/docs.docker.jp.onthefly/compose/environment-variables/">Compose における環境変数 | Docker ドキュメント</a></li>
</ul>
arm-band
tag:crieit.net,2005:PublicArticle/18195
2022-05-23T23:58:56+09:00
2022-05-23T23:58:56+09:00
https://crieit.net/posts/docker-compose-change-enviroment-variables-file-20220523
Docker Compose に渡す環境変数ファイルを変更する
<p>Docker Compose に渡す環境変数に .env 以外のファイルから渡したいと考えたのでメモしておきます。</p>
<h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2>
<p>アプリケーション開発環境等で開発環境用の <code>.development.env</code> と本番環境用の <code>.production.env</code> を用意したとして、この2つのどちらかから Docker Compose に環境変数を渡したい。</p>
<p>開発環境なのでアプリ側で使用する環境変数と Dockerコンテナ 内で使用する環境変数を統一したい、と。</p>
<p>ただし、デフォルトでは Docker Compose は <code>.env</code> からしか環境変数を読み取ってくれません。これをどうにかしたい。</p>
<h2 id="コマンド"><a href="#%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89">コマンド</a></h2>
<pre><code class="bash">> docker-compose --env-file .development.env up -d
</code></pre>
<p>ということでコマンドですが、 <code>docker-compose</code> コマンドに <code>--env-file</code> でパラメータを渡してあげると <code>.env</code> 以外のファイルも環境変数のソースとして渡すことができます。</p>
<h2 id="(余談) コマンド実行時の --env-file パラメータと .envファイル と docker-compose.yml の env_file キー"><a href="#%28%E4%BD%99%E8%AB%87%29+%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E5%AE%9F%E8%A1%8C%E6%99%82%E3%81%AE+--env-file+%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF%E3%81%A8+.env%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB+%E3%81%A8+docker-compose.yml+%E3%81%AE+env_file+%E3%82%AD%E3%83%BC">(余談) コマンド実行時の --env-file パラメータと .envファイル と docker-compose.yml の env_file キー</a></h2>
<p>さて、ここから少しややこしい話を。</p>
<p>Docker Compose 周りでは環境変数関連で見出しに挙げた3つの方法があるようです。</p>
<p><code>.env</code> については Docker Compose で使用できる環境変数ファイル。これは良いですね。</p>
<p>この <code>.env</code> をそれ以外のファイル名・ディレクトリのファイルから読み取るように変更できるのが <code>docker-compose</code>コマンド 実行時の <code>--env-file</code> パラメータ。</p>
<p>ここまでは <strong><code>docker-compose.yml</code> 内で使用できる</strong> 環境変数です。</p>
<p>それでは最後、 <code>docker-compose.yml</code> の <code>env_file</code>キー は何か。</p>
<pre><code>version: '3.7'
services:
db:
env_file:
- .dvelopment.env
</code></pre>
<p>最後の <code>env_file</code> は <code>docker-compose.yml</code> 内に記述できるパラメータで、これを使って環境変数のファイルを指定することができます。</p>
<p>ただし、このパラメータで指定した環境変数は、 <strong>起動したコンテナ内からは参照できますが、 <code>docker-compose.yml</code> 内からは参照できません</strong>。</p>
<p>ということで、今回やりたいこととはマッチしません。そもそもの目的が異なる、ということですね。</p>
<p>過去の先人達のように自分も嵌まったのでこのことも併せてメモしておきます。</p>
<h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://zenn.dev/rhene/scraps/781fdbecd340d3">docker-composeで環境変数が読み込まれない?</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/SolKul/items/989727aeeafcae28ecf7">docker-composeのenv_fileと.envファイルの違い - Qiita</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://docs.docker.jp/compose/environment-variables.html">Compose における環境変数 — Docker-docs-ja 19.03 ドキュメント</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://docs.docker.com/compose/environment-variables/">Environment variables in Compose | Docker Documentation</a></li>
</ul>
arm-band
tag:crieit.net,2005:PublicArticle/18194
2022-05-19T23:55:46+09:00
2022-05-19T23:55:46+09:00
https://crieit.net/posts/php-phoenix-migrate-db-depend-other-db-20220521
(PHP) Phoenix でデータ関係が依存しているDBをマイグレーションする
<p><a target="_blank" rel="nofollow noopener" href="https://github.com/lulco/phoenix">lulco/phoenix</a> でデータ関係が依存しているDBをマイグレーションする方法をメモ。</p>
<p>ドキュメントが全然ないので地道にやっていきます……。</p>
<h2 id="コード"><a href="#%E3%82%B3%E3%83%BC%E3%83%89">コード</a></h2>
<p>早速コードを。</p>
<h3 id="phoenix.php"><a href="#phoenix.php">phoenix.php</a></h3>
<pre><code class="php">return [
'migration_dirs' => [
'first' => __DIR__ . '/migrations/first',
'second' => __DIR__ . '/migrations/second',
],
'environments' => [
'local' => [
'adapter' => 'mysql',
'host' => $_ENV['MYSQL_HOST'],
'port' => (int)$_ENV['MYSQL_PORT'], // optional
'username' => $_ENV['MYSQL_USER'],
'password' => $_ENV['MYSQL_PASSWORD'],
'db_name' => $_ENV['MYSQL_DBNAME'],
'charset' => 'utf8mb4',
],
'production' => [
'adapter' => 'mysql',
'host' => $_ENV['MYSQL_HOST'],
'port' => (int)$_ENV['MYSQL_PORT'], // optional
'username' => $_ENV['MYSQL_USER'],
'password' => $_ENV['MYSQL_PASSWORD'],
'db_name' => $_ENV['MYSQL_DBNAME'],
'charset' => 'utf8mb4',
],
],
'default_environment' => 'local',
'log_table_name' => 'phoenix_log',
];
</code></pre>
<p>肝は <code>migration_dirs</code> で <code>first</code> と <code>second</code> でそれぞれ対応するディレクトリを指定しているところ。</p>
<h3 id="/migrations/first/hoge.php"><a href="#%2Fmigrations%2Ffirst%2Fhoge.php">/migrations/first/hoge.php</a></h3>
<pre><code class="php"><?php
namespace migrations;
use Phoenix\Database\Element\Index;
use Phoenix\Migration\AbstractMigration;
class HogeMigration extends AbstractMigration
{
protected function up(): void
{
$this->table('hoge')
->addColumn('create_date', 'datetime')
->addColumn('name', 'string')
->create();
// insert
$hogeData = [
[
'name' => 'foo'
],
[
'name' => 'bar'
],
[
'name' => 'buz'
],
// 略
];
$rows = [];
foreach ($hogeData as $key => $val) {
$rows[] = [
'create_date' => date('Y-m-d H:i:s'),
'name' => $val['name'],
];
}
$this->insert('hoge', $rows);
}
protected function down(): void
{
$this->table('hoge')
->drop();
}
}
</code></pre>
<p>まずは最初に <code>hoge</code> というDBを作成し、そこにデータを流し込みます。</p>
<h3 id="/migrations/second/fuga.php"><a href="#%2Fmigrations%2Fsecond%2Ffuga.php">/migrations/second/fuga.php</a></h3>
<pre><code class="php"><?php
namespace migrations;
use Phoenix\Database\Element\Index;
use Phoenix\Migration\AbstractMigration;
class FugaMigration extends AbstractMigration
{
protected function up(): void
{
$this->table('fuga')
->addColumn('create_date', 'datetime')
->addColumn('name', 'string')
->addColumn('hoge_id', 'integer')
->create();
// select hoge data
$hogeRows = $this->select('SELECT id, name FROM hoge');
// insert
$fugaData = [
[
'name' => 'un'
],
[
'name' => 'deux'
],
[
'name' => 'trois'
],
// 略
];
$rows = [];
foreach ($fugaData as $key => $val) {
$id = 0;
// hoge の name と fuga の name が一致する要素を array_filter() で抽出し、 array_values() で番号を詰める
$hogeArray = array_values(
array_filter(
$hogeRows,
function($hogeRow) use ($val) {
$needle = mb_strlen(mb_strtolower($val[0])) > 0 ? mb_strtolower($val[0]) : 'NOTHING';
return mb_strpos(mb_strtolower($hogeRow['name']), $needle) !== false;
}
)
);
// $hogeArray の要素が1つ (一意に定まる) 場合はその値を、そうでない場合はデフォルト値をセット
$id = count($hogeArray) === 1 ? (int)$hogeArray[0]['id'] : 0;
$rows[] = [
'create_date' => date('Y-m-d H:i:s'),
'name' => $val[0],
'hoge_id' => $id, // 上述でセットした id を使用
];
}
$this->insert('fuga', $rows);
}
protected function down(): void
{
$this->table('fuga')
->drop();
}
}
</code></pre>
<p>次に <code>fuga</code> を作成し、そこに徐に <code>$this->select()</code> で SQL文 を発行、先程流し込んだデータを抽出します。</p>
<p>その抽出したデータと <code>fuga</code> に流し込みたいデータを突き合わせて初期データを生成し、それを <code>fuga</code> に流し込む……という算段。</p>
<p>これで意図したデータをマイグレーションすることができました。</p>
<h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://github.com/lulco/phoenix">GitHub - lulco\/phoenix: Framework agnostic database migrations for PHP.</a>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://github.com/lulco/phoenix/blob/master/src/Database/Adapter/PdoAdapter.php">phoenix\/PdoAdapter.php at master · lulco\/phoenix · GitHub</a></li>
</ul></li>
</ul>
<p>ドキュメントがないのでコードを読んで普通に <code>select</code> とか使えそう、と思って試したりしていました。</p>
arm-band
tag:crieit.net,2005:PublicArticle/18193
2022-05-19T23:50:29+09:00
2022-05-19T23:50:29+09:00
https://crieit.net/posts/eloquent-connect-multiple-db-20220520
Eloquent で複数DBに接続する
<p>単独の Eloquent を利用して異なるデータソースにアクセスしてみたのでメモしておきます。</p>
<h2 id="コード"><a href="#%E3%82%B3%E3%83%BC%E3%83%89">コード</a></h2>
<pre><code class="php"><?php
declare(strict_types=1);
use Illuminate\Database\Capsule\Manager;
class DBConnection
{
private $db;
public function __construct()
{
$this->db = new Manager;
// default
$this->db->addConnection([
'driver' => 'mysql',
'host' => $_ENV['MYSQL1_HOST'],
'database' => $_ENV['MYSQL1_DBNAME'],
'username' => $_ENV['MYSQL1_USER'],
'password' => $_ENV['MYSQL1_PASSWORD'],
'charset' => 'utf8',
]);
// second
$this->db->addConnection([
'driver' => 'mysql',
'host' => $_ENV['MYSQL2_HOST'],
'database' => $_ENV['MYSQL2_DBNAME'],
'username' => $_ENV['MYSQL2_USER'],
'password' => $_ENV['MYSQL2_PASSWORD'],
'charset' => 'utf8',
],
'second'); // addConnection() メソッドの第二引数に接続名を指定する (1つ目はなしだと default になる)
$this->db->setAsGlobal();
$this->db->bootEloquent();
}
// 略
}
</code></pre>
<p>肝はコメントにも記した通り <code>Manager</code>クラス の <code>addConnection()</code>メソッド について、第二引数に接続名を指定することです (1つ目はなしだと <code>default</code> になり、後で使用する分にも指定は不要) 。</p>
<p>あ、しれっと書いていますが <code>dotenv</code> を使用した想定です。</p>
<pre><code class="php"><br />$db = new DBConnection();
$secondDBrows = $db->connection('second')->table('hoge_db')
->where( /* 略 */ )
->select( /* 略 */ )
->get();
</code></pre>
<p>こちらもしれっと書いていますがイメージとしては先程のDB接続用クラス <code>DBConnection</code> をインスタンス化して、その中から <code>connection()</code>メソッド で引数に先程指定した接続名を記述することで二つ目の接続を利用してDBにアクセスできる、ということです。</p>
<p>これで意図したことを実装することができました。</p>
<h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://blog.bagooon.com/?p=1492">Laravelを使わず、直接PHPからEloquentを使用し、かつ複数のDBに接続する方法。</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://technoledge.net/composer-eloquent-illuminate-database/">LaravelのORM「Eloquent」を単体で使ってスクラッチ開発する | テクナレジ</a></li>
</ul>
arm-band
tag:crieit.net,2005:PublicArticle/18192
2022-05-18T00:05:39+09:00
2022-05-18T00:05:39+09:00
https://crieit.net/posts/php-operating-array-closure-20220519
PHP の array_filter() で条件に一致した配列の要素を抽出する
<p>PHP で array.filter のようなことをしたくなったのでメモ。</p>
<h2 id="コード"><a href="#%E3%82%B3%E3%83%BC%E3%83%89">コード</a></h2>
<pre><code class="php"><?php
declare(strict_types=1);
function getEmployeeList(string $dept_name): array
{
return array_values(
array_filter(
$employees,
function($employee) use ($dept_name) {
return $employee->getDeptName() === $dept_name;
}
)
);
}
</code></pre>
<p>イメージとしてこのような感じで意図した挙動になることを確認しました。</p>
<p>やっていることととしては、「ある部署に所属する (部署名 <code>$dept_name</code> で抽出) 社員全員の一覧」を抽出しています。</p>
<p>なお、前提として社員データの配列 <code>$employees</code> (各要素が予め定義された <code>Employee</code>オブジェクト) があるものとしています。</p>
<p>ところで、この形 Eloquent の複合条件抽出でも見かけました……そう、 <code>array_filter()</code> の第二引数(コールバック関数)がクロージャで、スコープを越えて外部変数を使うために <code>use</code>句 を用いる、という形がそっくりそのままですね。</p>
<p>デジャヴも良いところですね。</p>
<h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2>
<h3 id="arra_values()"><a href="#arra_values%28%29">arra_values()</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://www.php.net/manual/ja/function.array-values.php">PHP: array_values - Manual</a></li>
</ul>
<h3 id="array_filter()"><a href="#array_filter%28%29">array_filter()</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://www.php.net/manual/ja/function.array-filter.php">PHP: array_filter - Manual</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://kimagureneet.hatenablog.com/entry/2017/03/07/003958">【php】配列を複数条件で検索、絞り込みする方法メモ - とりあえずphpとか</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://wani-pro.com/func-array_filter/">【php】 配列を条件で絞り込む(array_filter) - わにプログラミング</a></li>
</ul>
arm-band
tag:crieit.net,2005:PublicArticle/18191
2022-05-18T00:03:50+09:00
2022-05-18T00:03:50+09:00
https://crieit.net/posts/eloquent-multiple-where-condition-closure-20220518
Eloquent で複合条件の抽出をする
<p>Eloquent を実践的に使い始めたばかりなので初歩的なところですが、複合条件を組み立てるところについてメモ。</p>
<h2 id="コード"><a href="#%E3%82%B3%E3%83%BC%E3%83%89">コード</a></h2>
<p>やりたいこととしては、 <code>WHERE A AND B AND (C OR D)</code> という条件。</p>
<pre><code class="php">$departName = [
'総務部',
'営業部',
];
$dataRows = $dbConnect->table('hoge_db')
->where(
[
['user_id', '>', 23],
['age', '<', 42],
]
)
->where(function($query) use ($departName) {
$query->orWhere('depart_name', '=', $departName[0])
->orWhere('depart_name', '=', $departName[1]);
})
->select(
'fuga_db.name',
'fuga_db.attribute',
'fuga_db.parameter'
)
->join('fuga_db', 'hoge_db.user_id', '=', 'fuga_db.employee_id')
->get();
</code></pre>
<p><code>$dbConnect</code> にDB接続情報が入ったインスタンスがあるという前提ですが、イメージとしてこのようなコードです。</p>
<p>肝は以下。</p>
<ul>
<li><code>OR</code> 条件の括弧の中身を <code>where()</code>メソッド の中にクロージャとして無名関数で記述する。</li>
<li>関数の引数はクエリ。そこに <code>orWhere()</code>メソッド を足していく。</li>
<li>クロージャの外部の変数はスコープの関係で Undefined Variable になってしまうので <code>use</code>句 でクロージャ内に持ち込む</li>
</ul>
<p>これで意図した挙動になることを確認しました。</p>
<h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/Hwoa/items/542456b63e51895f9a55">Laravel5で「.. or ...) and (..」みたいな複雑な条件を書く - Qiita</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/mitashun/items/7b1a6c4bb44acd08472e">【Laravel】EloquentでOR検索する方法 - Qiita</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://umebius.com/laravel/querybuilder%E3%81%AB%E3%82%88%E3%82%8Band%E6%A4%9C%E7%B4%A2%E3%81%A8or%E6%A4%9C%E7%B4%A2%E3%81%AE%E8%A4%87%E5%90%88/">Eloquent\/QueryBuilderによるAnd検索とOr検索の複合 - Laravel5開発</a></li>
</ul>
arm-band
tag:crieit.net,2005:PublicArticle/18187
2022-05-10T22:43:05+09:00
2022-05-10T22:43:05+09:00
https://crieit.net/posts/slim-deploy-above-docroot-and-publish-below-subdirectory-20220510
Slim4 をサブディレクトリかつ本体はドキュメントルートより上にデプロイした状態で動作させる
<h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2>
<p><a target="_blank" rel="nofollow noopener" href="https://github.com/slimphp/Slim-Skeleton">slimphp/Slim-Skeleton</a> を サブディレクトリに配置しつつ、 app/ や src/, vendor/ 等のディレクトリはドキュメントルートより上の階層にデプロイした状態で動作させることができたのでメモしておきます。</p>
<h2 id="ディレクトリ構造"><a href="#%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E6%A7%8B%E9%80%A0">ディレクトリ構造</a></h2>
<pre><code>var/
└ www/
└ sampleapp/
├ slim-app/ // Slim4-Skeleton 本体 ( public/ ディレクトリ以外) デプロイ先
│ ├ app/
│ ├ src/
│ ├ vendor/
│ └ etc.
│
└ public_html/ // ドキュメントルート
└ subdirectory/ // サブディレクトリ
└ sub2/ // 2個目のサブディレクトリ
├ .htaccess // Slim4-Skeleton の public/ ディレクトリにある .htaccess
└ index.php // Slim4-Skeleton の public/ ディレクトリにある index.php
</code></pre>
<p>今回の最終的な構成はこのようになりました。</p>
<p>この階層構成は</p>
<ul>
<li>アプリケーション本体はドキュメントルート下には置きたくない</li>
<li>複数の Slim アプリケーションを配置する可能性がある</li>
<li>サブディレクトリも深い階層に設置する可能性がある</li>
</ul>
<p>ということで、本体のデプロイディレクトリやドキュメントルート下のサブディレクトリをそれぞれ1つずつ余計に掘っています。</p>
<h2 id="手順"><a href="#%E6%89%8B%E9%A0%86">手順</a></h2>
<p>すぐ削除するデモ環境なので割と雑に設定を。</p>
<pre><code class="bash"># cd /var/www/sampleapp/
# mkdir slim-app
# chown apache:apache slim-app/
# chmod 775 slim-app/
# ls -al
合計 16
drwxr-xr-x 4 ADMIN ADMIN 4096 MM月 dd yyyy hh:ii .
drwxr-xr-x 18 ADMIN ADMIN 4096 MM月 dd yyyy hh:ii ..
drwxrwxr-x 2 apache apache 4096 MM月 dd yyyy hh:ii slim-app
drwxrwxr-x 4 apache apache 4096 MM月 dd yyyy hh:ii public_html
#
# pwd
/var/www/sampleapp/
</code></pre>
<p>FTP 等で <code>subdirectory/</code>, <code>subdirectory/sub2/</code> を掘ります。</p>
<p>この状態になったら、</p>
<ul>
<li><code>/var/www/sampleapp/slim-app/</code> に <code>public/</code> 以外(他不要なファイル・ディレクトリは都度省く)をアップロード</li>
<li><code>/var/www/sampleapp/slim-app/logs/</code> の権限を 777 、 <code>/var/www/sampleapp/slim-app/logs/app.log</code> を 666 にする</li>
<li>ソースコードの <code>public/index.php</code> 内で <code>__DIR__ . '/../~~~.php';</code> となっているパスを <code>__DIR__ . '/../../../slim-app/~~~.php';</code> とデプロイ先の環境のパス階層に合わせる</li>
<li><code>/var/www/sampleapp/public_html/subdirectory/sub2</code> に <code>public/</code> 以下のファイルをアップロード</li>
</ul>
<p>として動作することを確認しました。なお、 <code>public/.htaccess</code> はデフォルトのままでOKでした。</p>
<p>なお、DBがある場合でDBサーバ上で必要な場合は接続情報を変更すること (接続元Webサーバがローカルの開発環境とサーバで異なるので別権限が必要)。</p>
<h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://www.asobou.co.jp/blog/web/slim-skeleton">PHPのマイクロフレームワーク「Slim4」の「slim-skeleton」を使用してハマったこと : ビジネスとIT活用に役立つ情報</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/asaokamei/items/b7a1af89db779317fe39">Slim4をサブディレクトリで走らせる方法 - Qiita</a></li>
</ul>
arm-band
tag:crieit.net,2005:PublicArticle/18186
2022-05-10T22:39:48+09:00
2022-05-10T22:39:48+09:00
https://crieit.net/posts/docker-php-7-alpine-use-composer-and-xdebug-20220510
Docker の php:7-alpine イメージで Composer と Xdebug を使えるようにする
<h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2>
<p><a target="_blank" rel="nofollow noopener" href="https://github.com/slimphp/Slim-Skeleton">slimphp/Slim-Skeleton</a> を利用したいと考えました。</p>
<p>手元の端末で開発しようとしたところ、 XAMPP に Xdebug を入れ忘れていたことに気付きました。しかも、 XAMPP の PHP のバージョンで Xdebug のインストールが止まってしまいました。</p>
<p>そこで、スケルトンプロジェクトに付随している Docker Compose での開発を試みましたが、この Docker Compose 内で pull してくるイメージは Composer も Xdebug もないので入れることにしました。</p>
<h2 id="コード"><a href="#%E3%82%B3%E3%83%BC%E3%83%89">コード</a></h2>
<h3 id="ディレクトリ構造"><a href="#%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E6%A7%8B%E9%80%A0">ディレクトリ構造</a></h3>
<pre><code>root/
├ _docker/ // Dockerに関する設定置き場
│ ├ dockerfiles/
│ │ ├ settings/
│ │ │ └ php.ini // PHPの設定 (Xdebug 有効化)
│ │ │
│ │ └ Dockerfile
│ │
│ ├ mysql/ // MariaDB 用ディレクトリ
│ │ └ .gitkeep
│ │
│ └ phpmyadmin/
│ ├ conf/
│ │ └ phpmyadmin-misc.ini // phpMyAdmin の設定 (メモリ上限など)
│ │
│ └ sessions/
│ └ .gitkeep
│
└ docker-compose.yml
</code></pre>
<p>Docker に関する部分のみ列挙。</p>
<h3 id="docker-compose.yml"><a href="#docker-compose.yml">docker-compose.yml</a></h3>
<pre><code class="yml">version: '3.7'
volumes:
logs:
driver: local
services:
slim:
build:
context: ./_docker/dockerfiles/
dockerfile: Dockerfile
working_dir: /var/www
command: php -S 0.0.0.0:8080 -t public
environment:
docker: "true"
ports:
- 8080:8080
volumes:
- .:/var/www
- ./logs:/var/www/logs
db:
image: mariadb
restart: always
ports:
- 3306:3306
volumes:
- ./_docker/mysql/mysql:/var/lib/mysql
- ./_docker/mysql/initdb.d:/docker-entrypoint-initdb.d
environment:
- MYSQL_ROOT_PASSWORD=pwd
- MYSQL_DATABASE=test
- MYSQL_USER=user
- MYSQL_PASSWORD=pwd
phpmyadmin:
image: phpmyadmin/phpmyadmin
volumes:
- ./_docker/phpmyadmin/sessions:/sessions
- ./_docker/phpmyadmin/conf/phpmyadmin-misc.ini:/usr/local/etc/php/conf.d/phpmyadmin-misc.ini
environment:
- PMA_ARBITRARY=1
- PMA_HOST=db
- PMA_USER=user
- PMA_PASSWORD=pwd
ports:
- 8081:80
</code></pre>
<p>変更点は以下。</p>
<ul>
<li>PHP 用環境: <code>Dockerfile</code> を使ったイメージにカスタマイズ</li>
<li>DB: MariaDB のイメージをそのまま利用
<ul>
<li><code>environment</code> のパラメータはそのまま使用。アプリケーションや phpMyAdmin 側と動機は取れていないですがひとまずはこれで……</li>
</ul></li>
<li>phpMyAdmin: これも phpMyAdmin 公式イメージを流用</li>
</ul>
<h3 id="_docker/dockerfiles/Dockerfile"><a href="#_docker%2Fdockerfiles%2FDockerfile">_docker/dockerfiles/Dockerfile</a></h3>
<pre><code class="Dockerfile">FROM php:7-alpine
RUN apk --update add curl
RUN set -ex \
&& apk --no-cache add \
autoconf build-base
RUN pecl install xdebug
RUN docker-php-ext-enable xdebug
RUN docker-php-ext-install pdo_mysql
COPY settings/php.ini /usr/local/etc/php/conf.d
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/bin --filename=composer
</code></pre>
<p><code>php:7-alpine</code> をベースに Xdebug と Composer を追加。</p>
<h3 id="_docker/dockerfiles/settings/php.ini"><a href="#_docker%2Fdockerfiles%2Fsettings%2Fphp.ini">_docker/dockerfiles/settings/php.ini</a></h3>
<pre><code class="ini">xdebug.mode=coverage
</code></pre>
<p>Xdebug でカバレッジを有効化するための設定を追加するため。なお、 <code>Dockerfile</code> で <code>COPY</code> する際のホストマシンでのパスは <strong><code>Dockerfile</code> の存在するディレクトリから下でないと参照できない</strong> という地味な制約があるので <code>dockerfiles</code> ディレクトリをわざわざ掘りました。</p>
<h3 id="_docker/phpmyadmin/conf/phpmyadmin-misc.ini"><a href="#_docker%2Fphpmyadmin%2Fconf%2Fphpmyadmin-misc.ini">_docker/phpmyadmin/conf/phpmyadmin-misc.ini</a></h3>
<pre><code class="ini">allow_url_fopen = Off
max_execution_time = 600
memory_limit = 64M
post_max_size = 64M
upload_max_filesize = 64M
</code></pre>
<p>メモリ上限等のカスタマイズ。</p>
<hr />
<p>これで今回はカバレッジ出力まで動作することを確認しました。</p>
<h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2>
<h3 id="Xdebug"><a href="#Xdebug">Xdebug</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/sachiko-kame/items/bf5480f4d7c751ab28aa">PHPUnitでHTMLコードカバレッジを出すまで(Docker使用) - Qiita</a></li>
</ul>
<blockquote>
<p>Warning: XDEBUG_MODE=coverage or xdebug.mode=coverage has to be set</p>
</blockquote>
<p>が出たので step3 について実施。</p>
<p>なお、今回のケースでは</p>
<pre><code>extension=xdebug.so
</code></pre>
<p>は不要だった。</p>
<h2 id="Dockerfile"><a href="#Dockerfile">Dockerfile</a></h2>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://crudzoo.com/blog/php-docker">Dockerで作るNginx + PHP7 + Xdebug環境 | Crudzoo</a></li>
</ul>
<p>ほぼこれでOK.</p>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/ucan-lab/items/fbf021bf69896538e515">php-alpineコンテナにxdebugをインストールする時にハマったメモ - Qiita</a></li>
</ul>
<p>こちらも参照。 <code>php -v</code> で <code>with Xdebug</code> と付いていればOK。</p>
<h4 id="Dockerfile の COPY"><a href="#Dockerfile+%E3%81%AE+COPY">Dockerfile の COPY</a></h4>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://scrapbox.io/taka521-tech-notes/%E3%80%90Docker%E3%80%91COPY%E3%81%A7%E6%8C%87%E5%AE%9A%E3%81%95%E3%82%8C%E3%81%9F%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AF%E3%80%81Dockerfile%E3%81%8C%E5%AD%98%E5%9C%A8%E3%81%99%E3%82%8B%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E3%81%8B%E3%82%89%E3%81%AE%E7%9B%B8%E5%AF%BE%E3%83%91%E3%82%B9%E3%81%A7%E3%80%81%E8%A6%AA%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E3%82%92%E8%A6%8B%E3%82%8C%E3%81%AA%E3%81%84">【Docker】COPYで指定されたファイルは、Dockerfileが存在するディレクトリからの相対パスで、親ディレクトリを見れない - タカの技術ノート</a></li>
</ul>
<h3 id="Mariadb"><a href="#Mariadb">Mariadb</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://mebee.info/2020/04/07/post-8227/">dockerを使用してmariadbを構築する | mebee</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/tarch710/items/1236a23f7ffde4c512f2">開発環境をDockerにしたら、PDOでcould not find driverが出た - Qiita</a></li>
</ul>
<blockquote>
<p>could not find driver</p>
</blockquote>
<p>普通に考えたら確かにドライバがないので追加して解決。</p>
<h3 id="シェル"><a href="#%E3%82%B7%E3%82%A7%E3%83%AB">シェル</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/yutachaos/items/56dd7ea09d7e2b0d9173">dockerでalpine linux ベースのcontainerに入って、shellを使いたいとき。 - Qiita</a></li>
</ul>
<p>bash ではなく ash 。 <code>bin/ash</code> 指定。</p>
<h3 id="sr -c"><a href="#sr+-c">sr -c</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://genzouw.com/entry/2020/01/28/120014/1910/">docker-compose.ymlのcommandプロパティに複数コマンドを設定する方法 | ゲンゾウ用ポストイット</a></li>
</ul>
<p>最終的には使わずに済みましたが念のためメモ。</p>
arm-band
tag:crieit.net,2005:PublicArticle/18181
2022-04-30T00:03:10+09:00
2022-04-30T00:03:10+09:00
https://crieit.net/posts/omit-jquery-note-20220502
脱jQuery メモ
<h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2>
<p>Bootstrap でブレークポイント未満(スマホ時)のナビゲーションリンクにアンカーリンクがある場合、アンカーリンクをタップしてもナビゲーションリンクのメニューが開いたままアンカーリンクへ遷移するので、それを制御するコードを自前で書いていました。その部分を脱 jQuery したのでメモしておきます。</p>
<h2 id="コード"><a href="#%E3%82%B3%E3%83%BC%E3%83%89">コード</a></h2>
<h3 id="jQuery"><a href="#jQuery">jQuery</a></h3>
<p>まずは Bootstrap 4 までの jQuery でのコード。</p>
<pre><code class="js">// ナビゲーションバー
const $navbar = $('#navbar');
// ブランド名とドロップダウンコンポーネント以外のナビゲーションリスト
$navbar.find('.navbar-brand, .nav-item:not(.dropdown) a, .dropdown-item').on('click', function (e) {
// 略
const navBarListID = 'navbarList';
if ($(e.currentTarget).closest('#' + navBarListID).length > 0) {
let breakpoint = 0;
if (/(^|\s)navbar-expand-(\S*)/g.test($navbar.children('.navbar').prop('class'))) {
switch (RegExp.$2) {
case 'sm':
breakpoint = 576;
break;
case 'md':
breakpoint = 768;
break;
case 'lg':
breakpoint = 992;
break;
case 'xl':
breakpoint = 1200;
break;
default:
breakpoint = 0;
break;
}
}
if ($(window).outerWidth() < breakpoint && !$navbar.find('.navbar-toggler[data-target="#' + navBarListID + '"]').hasClass('collapsed')) {
// 現在の表示がハンバーガーメニューの場合
$navbar.find('.navbar-toggler[data-target="#' + navBarListID + '"]').trigger('click'); // 移動したらハンバーガーを折りたたむ
} else if ($(e.currentTarget).hasClass('dropdown-item') && $(e.currentTarget).closest('.dropdown').hasClass('show')) {
// 現在の表示がハンバーガーメニューではなく、ドロップダウン内のメニューをクリックした場合
$(e.currentTarget).closest('.dropdown').trigger('click'); // 移動したらドロップダウンを折りたたむ
}
}
return false;
}
};
</code></pre>
<p>ざっくりこんなコードでした。やっていることとしては</p>
<ul>
<li>ブランド(<code>.navbar-brand</code>), ドロップダウンではないナビゲーションリンク(<code>.nav-item:not(.dropdown) a</code>, ドロップダウン内のリンク(<code>.dropdown-item</code>) がクリックされた場合
<ul>
<li>クリック(タップ)された要素の直近の祖先要素に <code>#navbarList</code> の id属性 がある要素が存在する場合
<ul>
<li>ナビゲーションバー要素のクラスにある <code>.navbar-expand-XX</code> のクラスのブレークポイントの文字列を取得してブレークポイント値をセット</li>
<li>その値と現在のウィンドウの幅を比較してウインドウ幅の方が小さい (=ハンバーガーメニューに表示が切り替わっている) 、かつ <code>.navbar-toggler</code> 要素が <code>.collapsed</code> のclass属性 を持っている (=メニュー展開) 場合
<ul>
<li>ハンバーガーメニューを折りたたむ (ハンバーガーアイコンを1度クリックする)</li>
</ul></li>
<li>またはクリックされた要素が <code>.dropdown-item</code> class属性 を持っている (=ドロップダウンメニュー) 、かつ直近の祖先要素で <code>.dropdown</code> class属性 を持つ要素が <code>.show</code> class属性 を持っている (=ドロップダウンが開かれている) 場合
<ul>
<li>ドロップダウン要素を折りたたむ</li>
</ul></li>
</ul></li>
</ul></li>
</ul>
<p>という挙動。</p>
<h3 id="プレーン JavaScript"><a href="#%E3%83%97%E3%83%AC%E3%83%BC%E3%83%B3+JavaScript">プレーン JavaScript</a></h3>
<p>これを Bootstrap 5 対応でプレーンな JavaScirpt に書き換え。</p>
<pre><code class="js">const navbar = document.querySelector('#navbar');
const navBarListID = 'navbarList';
if (
typeof e.currentTarget.closest(`#${navBarListID}`) !== 'undefined'
&& e.currentTarget.closest(`#${navBarListID}`) !== null
) {
let breakpoint = 0;
if (/(^|\s)navbar-expand-(\S*)/g.test(navbar.className)) {
switch (RegExp.$2) {
case 'sm':
breakpoint = 576;
break;
case 'md':
breakpoint = 768;
break;
case 'lg':
breakpoint = 992;
break;
case 'xl':
breakpoint = 1200;
break;
case 'xxl':
breakpoint = 1400;
break;
default:
breakpoint = 0;
break;
}
}
if (window.innerWidth < breakpoint) {
// ブレークポイント未満の幅のとき
const navbarTogglers = navbar.querySelectorAll(`.navbar-toggler[data-bs-target="#${navBarListID}"]`);
navbarTogglers.forEach((navbarToggler) => {
if(!navbarToggler.classList.contains('collapsed')) {
// 現在の表示がハンバーガーメニューの場合、
// 移動したらハンバーガーを折りたたむ
navbarToggler.dispatchEvent(new Event('click'));
}
else if(
e.currentTarget.classList.contains('dropdown-item')
&& e.currentTarget.closest('.dropdown').classList.contains('show')
) {
// 現在の表示がハンバーガーメニューではなく、ドロップダウン内のメニューをクリックした場合
// 移動したらドロップダウンを折りたたむ
e.currentTarget.closest('.dropdown').dispatchEvent(new Event('click'));
}
});
}
}
</code></pre>
<p>やっていることは大体同じです。ただし、いくつか置き換えが必要な部分があったので以下その点について触れていきます。</p>
<h2 id="置き換えた部分"><a href="#%E7%BD%AE%E3%81%8D%E6%8F%9B%E3%81%88%E3%81%9F%E9%83%A8%E5%88%86">置き換えた部分</a></h2>
<h3 id="複数のクラス指定で要素を取得"><a href="#%E8%A4%87%E6%95%B0%E3%81%AE%E3%82%AF%E3%83%A9%E3%82%B9%E6%8C%87%E5%AE%9A%E3%81%A7%E8%A6%81%E7%B4%A0%E3%82%92%E5%8F%96%E5%BE%97">複数のクラス指定で要素を取得</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://1-notes.com/javascript-multiple-with-getelementsbyclassname/">JavaScript | getElementsByClassNameで複数のclass名を指定して取得する方法 | ONE NOTES</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://q-az.net/none-jquery-find/">脱jQuery .find() | q-Az</a></li>
</ul>
<pre><code class="js">const elms = document.querySelectorAll('.hoge, .fuga');
</code></pre>
<p>jQuery のようにカンマ区切りで <code>document.querySelectorAll()</code> でOK。</p>
<h3 id=".on()"><a href="#.on%28%29">.on()</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://q-az.net/without-jquery-on-off/">脱jQuery .on() .off() | q-Az</a></li>
</ul>
<p>普通に <code>.addEventListener('eventName', function)</code> でOK。</p>
<h3 id="複数の要素に対するイベントハンドラ"><a href="#%E8%A4%87%E6%95%B0%E3%81%AE%E8%A6%81%E7%B4%A0%E3%81%AB%E5%AF%BE%E3%81%99%E3%82%8B%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%83%8F%E3%83%B3%E3%83%89%E3%83%A9">複数の要素に対するイベントハンドラ</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://www.mitsue.co.jp/knowledge/blog/frontend/201805/24_0917.html">どうして!?document.querySelectorAll(selector).addEventListener()が動かないわけ | フロントエンドBlog | ミツエーリンクス</a></li>
</ul>
<pre><code class="js">const elms = document.querySelectorAll('.hoge, .fuga');
elms.forEach(elm => {
elm.addEventListener('click', process);
},
false);
</code></pre>
<p><code>.querySelectorAll()</code> で取得した要素を <code>.forEach()</code> で反復処理させます。</p>
<h3 id="親要素・祖先要素"><a href="#%E8%A6%AA%E8%A6%81%E7%B4%A0%E3%83%BB%E7%A5%96%E5%85%88%E8%A6%81%E7%B4%A0">親要素・祖先要素</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://yuyauver98.me/js-parentnode-closest/">【Javascript】親要素や祖先要素を取得する方法 parentNode\/closest | ゆうやの雑記ブログ</a></li>
</ul>
<p>普通に <code>.closet('.parent')</code> 。</p>
<h3 id="子要素"><a href="#%E5%AD%90%E8%A6%81%E7%B4%A0">子要素</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://webty.jp/staffblog/production/post-1745/">脱jQuery!DOM要素取得コードの素のJavaScriptへの書き換え │ Webty Staff Blog</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://the-zombis.sakura.ne.jp/wp/blog/2019/09/16/post-4014/">【JavaScript】脱JQuery!?メソッドを比べてみた!要素取得編 - Web.fla</a></li>
</ul>
<p>jQuery の <code>.children('selectorName')</code> は一応プレーンな JavaScript にも <code>.children</code>プロパティ がある模様。</p>
<p>ただし、 jQuery のように <code>.children</code> へセレクタ指定はできなさそうなので、この部分は HTML のクラスを子要素の方に付けることで回避しました。</p>
<h3 id="クラス名全てを取得"><a href="#%E3%82%AF%E3%83%A9%E3%82%B9%E5%90%8D%E5%85%A8%E3%81%A6%E3%82%92%E5%8F%96%E5%BE%97">クラス名全てを取得</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://shanabrian.com/web/javascript/get-class.php">クラス名(class属性の値)をすべて取得 | JavaScript逆引き | Webサイト制作支援 | ShanaBrian Website</a></li>
</ul>
<p>jQuery では <code>.prop('class')</code> としていたところですが、 <code>.className</code> でOK。</p>
<h3 id="子孫要素の中から探す"><a href="#%E5%AD%90%E5%AD%AB%E8%A6%81%E7%B4%A0%E3%81%AE%E4%B8%AD%E3%81%8B%E3%82%89%E6%8E%A2%E3%81%99">子孫要素の中から探す</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://q-az.net/none-jquery-find/">脱jQuery .find() | q-Az</a></li>
</ul>
<p>jQueryでは <code>.find()</code> だったところを、 <code>elm.querySelector('selectorName')</code> と指定すればOK。</p>
<h3 id="ウインドウ幅"><a href="#%E3%82%A6%E3%82%A4%E3%83%B3%E3%83%89%E3%82%A6%E5%B9%85">ウインドウ幅</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://wemo.tech/470">スクリーン・ウインドウ・画面サイズをjavascriptで取得する方法まとめ | WEMO</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://q-az.net/without-jquery-innerheight-width-outerheight-width/">脱jQuery .innerHeight() .innerWidth() .outerHeight() .outerWidth() | q-Az</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://ja.javascript.info/size-and-scroll-window">ウィンドウサイズとスクローリング</a></li>
</ul>
<p>jQuery で <code>$(window).outerWidth()</code> としてたところを、今回の用途では <code>window.innerWidth</code> へ置き換えました。</p>
<h3 id="クラスの存在チェック"><a href="#%E3%82%AF%E3%83%A9%E3%82%B9%E3%81%AE%E5%AD%98%E5%9C%A8%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF">クラスの存在チェック</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://q-az.net/none-jquery-addclass-has-remove-toggle/">脱jQuery .addClass() .hasClass() .removeClass() .toggleClass() | q-Az</a></li>
</ul>
<p>jQuery では <code>.hasClass('className')</code> だったところを、 <code>.classList.contains('className')</code> へ置き換え。</p>
<h3 id="イベントハンドラの指定とイベント発火要素の取得"><a href="#%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%83%8F%E3%83%B3%E3%83%89%E3%83%A9%E3%81%AE%E6%8C%87%E5%AE%9A%E3%81%A8%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E7%99%BA%E7%81%AB%E8%A6%81%E7%B4%A0%E3%81%AE%E5%8F%96%E5%BE%97">イベントハンドラの指定とイベント発火要素の取得</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://note.com/yamanoborer/n/n2e4cc40328b7">【JavaScript】addEventListenerで関数に引数をわたす|北の南|note</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://www.mitsue.co.jp/knowledge/blog/frontend/201912/02_0000.html">脱jQueryに向けた第一歩 | フロントエンドBlog | ミツエーリンクス</a></li>
</ul>
<p>普通に関数指定で <code>elm.addEventListener('click', hoge(elm), false)</code> と書いてしまっていました。</p>
<p>しかし、参考記事に拠るとこれだと<strong>その関数の実行結果</strong>が渡されるとのこと。しかもイベント発火時ではなく該当コード読み込み時に実行されてしまうため、挙動がおかしくなってしまいます。</p>
<p>これについては第二引数はオブジェクト (または JavaScript の純粋な関数) なので <code>elm.addEventListener('click', hoge, false)</code> と関数名だけにしなければならず、その通りに書けばOK。</p>
<p>一方、 <code>const hoge = (e) => { /* 処理 */ };</code> で普通にイベントは渡ってくるので、「クリックされた要素」をイベントハンドラ内で利用したい場合は <code>e.currentTarget</code> とすれば問題ないですね。</p>
<p>最初これに気付かずしばらく嵌まっていました。参考記事に感謝。</p>
<h3 id="イベント発火"><a href="#%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E7%99%BA%E7%81%AB">イベント発火</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://mae.chab.in/archives/2821#post2821-5">jQueryを使わない書き方 ajax, each, trigger, on\/off, extend, deferred, animate, css編 | maesblog</a></li>
</ul>
<p>イベントがクラスで渡す必要がありますがほぼ同じで、 <code>elm.dispatchEvent(new Event('click'))</code> とすればOK。</p>
<hr />
<p>このような形でガシガシ書き換えていけば大きな問題はなさそうです。</p>
<p>とはいえ、この置き換えは地味にアンカーリンクへのスクロールアニメーションを <code>scroll-behavior: smooth;</code> に移行したおかげで上述以外の大半の JS を破棄しても問題ないと判断できたのが非常に大きいですね。そうでなければもっと大きなボリュームと対峙する必要がありました……。</p>
<p>しかも <code>Scroll-margin-top</code> で上部固定ナビゲーションバーの裏側にアンカーリンクが隠れてしまう問題を回避できる、というのも JS コードを削減できた要因の一つなので、この2つのプロパティは個人的にかなり神がかっていると感じます。細かいイージングは犠牲になりますが、今回は全然目を瞑ることができるレベルなので良しとします。</p>
<h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2>
<h3 id="複数のクラス指定で要素を取得"><a href="#%E8%A4%87%E6%95%B0%E3%81%AE%E3%82%AF%E3%83%A9%E3%82%B9%E6%8C%87%E5%AE%9A%E3%81%A7%E8%A6%81%E7%B4%A0%E3%82%92%E5%8F%96%E5%BE%97">複数のクラス指定で要素を取得</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://1-notes.com/javascript-multiple-with-getelementsbyclassname/">JavaScript | getElementsByClassNameで複数のclass名を指定して取得する方法 | ONE NOTES</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://q-az.net/none-jquery-find/">脱jQuery .find() | q-Az</a></li>
</ul>
<h3 id=".on()"><a href="#.on%28%29">.on()</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://q-az.net/without-jquery-on-off/">脱jQuery .on() .off() | q-Az</a></li>
</ul>
<h3 id="複数の要素に対するイベントハンドラ"><a href="#%E8%A4%87%E6%95%B0%E3%81%AE%E8%A6%81%E7%B4%A0%E3%81%AB%E5%AF%BE%E3%81%99%E3%82%8B%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%83%8F%E3%83%B3%E3%83%89%E3%83%A9">複数の要素に対するイベントハンドラ</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://www.mitsue.co.jp/knowledge/blog/frontend/201805/24_0917.html">どうして!?document.querySelectorAll(selector).addEventListener()が動かないわけ | フロントエンドBlog | ミツエーリンクス</a></li>
</ul>
<h3 id="親要素・祖先要素"><a href="#%E8%A6%AA%E8%A6%81%E7%B4%A0%E3%83%BB%E7%A5%96%E5%85%88%E8%A6%81%E7%B4%A0">親要素・祖先要素</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://yuyauver98.me/js-parentnode-closest/">【Javascript】親要素や祖先要素を取得する方法 parentNode\/closest | ゆうやの雑記ブログ</a></li>
</ul>
<h3 id="子要素"><a href="#%E5%AD%90%E8%A6%81%E7%B4%A0">子要素</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://webty.jp/staffblog/production/post-1745/">脱jQuery!DOM要素取得コードの素のJavaScriptへの書き換え │ Webty Staff Blog</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://the-zombis.sakura.ne.jp/wp/blog/2019/09/16/post-4014/">【JavaScript】脱JQuery!?メソッドを比べてみた!要素取得編 - Web.fla</a></li>
</ul>
<h3 id="クラス名全てを取得"><a href="#%E3%82%AF%E3%83%A9%E3%82%B9%E5%90%8D%E5%85%A8%E3%81%A6%E3%82%92%E5%8F%96%E5%BE%97">クラス名全てを取得</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://shanabrian.com/web/javascript/get-class.php">クラス名(class属性の値)をすべて取得 | JavaScript逆引き | Webサイト制作支援 | ShanaBrian Website</a></li>
</ul>
<h3 id="子孫要素の中から探す"><a href="#%E5%AD%90%E5%AD%AB%E8%A6%81%E7%B4%A0%E3%81%AE%E4%B8%AD%E3%81%8B%E3%82%89%E6%8E%A2%E3%81%99">子孫要素の中から探す</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://q-az.net/none-jquery-find/">脱jQuery .find() | q-Az</a></li>
</ul>
<h3 id="ウインドウ幅"><a href="#%E3%82%A6%E3%82%A4%E3%83%B3%E3%83%89%E3%82%A6%E5%B9%85">ウインドウ幅</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://wemo.tech/470">スクリーン・ウインドウ・画面サイズをjavascriptで取得する方法まとめ | WEMO</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://q-az.net/without-jquery-innerheight-width-outerheight-width/">脱jQuery .innerHeight() .innerWidth() .outerHeight() .outerWidth() | q-Az</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://ja.javascript.info/size-and-scroll-window">ウィンドウサイズとスクローリング</a></li>
</ul>
<h3 id="クラスの存在チェック"><a href="#%E3%82%AF%E3%83%A9%E3%82%B9%E3%81%AE%E5%AD%98%E5%9C%A8%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF">クラスの存在チェック</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://q-az.net/none-jquery-addclass-has-remove-toggle/">脱jQuery .addClass() .hasClass() .removeClass() .toggleClass() | q-Az</a></li>
</ul>
<h3 id="イベントハンドラの指定とイベント発火要素の取得"><a href="#%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%83%8F%E3%83%B3%E3%83%89%E3%83%A9%E3%81%AE%E6%8C%87%E5%AE%9A%E3%81%A8%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E7%99%BA%E7%81%AB%E8%A6%81%E7%B4%A0%E3%81%AE%E5%8F%96%E5%BE%97">イベントハンドラの指定とイベント発火要素の取得</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://note.com/yamanoborer/n/n2e4cc40328b7">【JavaScript】addEventListenerで関数に引数をわたす|北の南|note</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://www.mitsue.co.jp/knowledge/blog/frontend/201912/02_0000.html">脱jQueryに向けた第一歩 | フロントエンドBlog | ミツエーリンクス</a></li>
</ul>
<h3 id="イベント発火"><a href="#%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E7%99%BA%E7%81%AB">イベント発火</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://mae.chab.in/archives/2821#post2821-5">jQueryを使わない書き方 ajax, each, trigger, on\/off, extend, deferred, animate, css編 | maesblog</a></li>
</ul>
<h3 id="その他"><a href="#%E3%81%9D%E3%81%AE%E4%BB%96">その他</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/nightyknite/items/668c112c40931515ed67">JQueryをVanilla JSに緩やかに置き換える - Qiita</a></li>
</ul>
<h3 id="Bootstrap 5 のブレークポイント"><a href="#Bootstrap+5+%E3%81%AE%E3%83%96%E3%83%AC%E3%83%BC%E3%82%AF%E3%83%9D%E3%82%A4%E3%83%B3%E3%83%88">Bootstrap 5 のブレークポイント</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://bootstrap-guide.com/components/navbar">ナビゲーションバー~Bootstrap5設置ガイド</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://bootstrap-guide.com/layout/breakpoints">ブレークポイント~Bootstrap5設置ガイド</a></li>
</ul>
arm-band
tag:crieit.net,2005:PublicArticle/18180
2022-04-29T23:58:08+09:00
2022-04-29T23:58:08+09:00
https://crieit.net/posts/mojibake-pdf-20220501
PDF からテキストをコピペしたときに稀に見られる潰れた文字のメモ
<p>あるとき、エディタ上で「入」の文字が化けてしまって困ったので原因を探ったのでメモしておきます。</p>
<h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2>
<p>エディタ上でファイル名をコピペしたら「入」の文字が化けてしまいました。エクスプローラ上で見ると一見普通の「入」の文字のように見えたのですが……。</p>
<p><a href="https://crieit.now.sh/upload_images/872413ab26ee95ba2c1f3249702a580f626bfc86aeb9e.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/872413ab26ee95ba2c1f3249702a580f626bfc86aeb9e.jpg?mw=700" alt="左が文字化けした入、右が手入力した入" /></a></p>
<p>メモ帳に貼り付けたところ、明らかに様子がおかしいことに気付きました。</p>
<p>左が文字化けした「入」、右が手入力した「入」です。</p>
<p>何故か今回文字化けした「入」は半分くらいに潰れていますね……。字形が異なるということは、もちろん、文字コードも別です。ユニコードで見ると左が <code>\u2F0A</code> 、右が <code>\u5165</code> です。</p>
<p>何でしょうこれ……。</p>
<h2 id="調査"><a href="#%E8%AA%BF%E6%9F%BB">調査</a></h2>
<p>調べてみると、他にもこうした文字があることが分かりました。</p>
<pre><code>⼊入
⼈人
⻄西
⾃自
⾞車
⼒力
⼿手
⾮非
⼤大
⼯工
</code></pre>
<p>今回採集した例が上述。</p>
<p><a href="https://crieit.now.sh/upload_images/a676d153f7a87ba4255ed1b6400ec489626bfc9218be3.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/a676d153f7a87ba4255ed1b6400ec489626bfc9218be3.jpg?mw=700" alt="半分潰れたような文字の採集結果" /></a></p>
<p>メモ帳で見るといずれも半分くらいの大きさに潰れています。エクスプローラで見るとほとんど違いが分からないのですが、先述の通り文字コードが別なのでファイル名はしっかり別物と認識されます。</p>
<p>そのため、同じファイル名が複数あるように見えるというなかなか<del>面白い</del>困った状況に陥ります。</p>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://logmi.jp/tech/articles/324366">PDFをコピペするとなぜ“文字化け”が起きてしまうのか 変換テーブル“ToUnicode CMap”が原因だった - ログミーTech</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://logmi.jp/tech/articles/324412">PDFに文字化けを起こさせない対策法 もらったファイルは正規化で、作成ツールは対応済みを使え - ログミーTech</a></li>
</ul>
<p>さらに調べた結果、原因が判明しました。原因は PDF でした。</p>
<ul>
<li>PDFの中のテキストは文字コードとは別の固有の番号で文字を出力しているが、Unicodeのような文字コードとは互換性はない</li>
<li>一応文字コードとPDF固有の番号の変換テーブルはある</li>
<li>が、この変換テーブルがない PDF が存在する</li>
<li>そうした場合、昇順で文字を参照するが本来の文字ではなく部首に当たる文字が先にヒットしてしまう</li>
<li>この部首に当たる文字が先の半分潰れたような文字の正体</li>
<li>結果、コピペ等 PDF の中からテキストを抜く際に本来の文字ではなく、部首に当たる文字が抜き出される。その文字は通常の文字ではないため、一部の環境では化ける</li>
</ul>
<p>ざっくり言うとこんな感じの原因の模様。確かに、今回のケースでも PDF の中から見出しのテキストをコピペしてファイル名にしていました。その際に先の条件に当てはまり、通常の <code>入</code> ではなく、 <code>⼊</code> を混入させてしまった、ということのようです。</p>
<p>原因が分かれば納得ですが、なんと面倒な……。</p>
<h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://logmi.jp/tech/articles/324366">PDFをコピペするとなぜ“文字化け”が起きてしまうのか 変換テーブル“ToUnicode CMap”が原因だった - ログミーTech</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://logmi.jp/tech/articles/324412">PDFに文字化けを起こさせない対策法 もらったファイルは正規化で、作成ツールは対応済みを使え - ログミーTech</a></li>
</ul>
<h3 id="(余談)"><a href="#%28%E4%BD%99%E8%AB%87%29">(余談)</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/libraplanet/items/68a3e6b4a3942c83db50">「文字コードを自動判別する」と言う幻想を打ち砕く👊 - Qiita</a></li>
</ul>
<p>今回の件とは別件ですが、まあ文字コードがらみは厄介ですよね、というお話。</p>
arm-band
tag:crieit.net,2005:PublicArticle/18178
2022-04-28T00:04:15+09:00
2022-04-28T00:04:15+09:00
https://crieit.net/posts/bootstrap5-scroll-margin-top-darkmode-parallax-demo-20220430
Bootstrap 5 + scroll-margin-top + ダークモード + パララックス についてメモ
<h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2>
<p><a target="_blank" rel="nofollow noopener" href="https://labor.ewigleere.net/">Bootstrap 5 で JavaScript の記述なしにアンカーリンクへのアニメーション付きスクロールが <code>scroll-behavior</code> で実装されていることを最小限構成で確認した</a>ので、改めて Bootstrap 5 で検証したいと思います。</p>
<h2 id="デモ"><a href="#%E3%83%87%E3%83%A2">デモ</a></h2>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://arm-band.github.io/test-scroll-mt-vault-celestia/">Home - Vault Celestia</a></li>
</ul>
<p>早速ですがデモを。</p>
<h2 id="scroll-margin-top"><a href="#scroll-margin-top">scroll-margin-top</a></h2>
<p>上述の通り、 Bootostrap 5 ではアンカーリンクへのアニメーション付きスクロールは <code>scroll-behavior: smooth;</code> で実装されているのでこの部分はそちらに任せて、 <code>scroll-margin-top</code> のみ自前で記述。</p>
<pre><code class="scss">// foundation/_variable.scss
$navbar-height: 56px;
/* css変数 + Scss変数 */
:root {
--navbar-height: #{$navbar-height};
}
// object/utility/_u-ssnapmt.scss
.u-ssnapmt {
scroll-margin-top: var(--navbar-height);
}
</code></pre>
<p>今回は css変数 + Scss の併用で記述してみました。</p>
<h2 id="css変数 + ページごとのキービジュアル画像の変更"><a href="#css%E5%A4%89%E6%95%B0+%2B+%E3%83%9A%E3%83%BC%E3%82%B8%E3%81%94%E3%81%A8%E3%81%AE%E3%82%AD%E3%83%BC%E3%83%93%E3%82%B8%E3%83%A5%E3%82%A2%E3%83%AB%E7%94%BB%E5%83%8F%E3%81%AE%E5%A4%89%E6%9B%B4">css変数 + ページごとのキービジュアル画像の変更</a></h2>
<p>キービジュアル (ページ最上部にある画像 + 見出し + キャッチコピー等のパーツ) の背景画像について、ページごとに異なる画像を表示させたい、というのはよくある話だとは思います。</p>
<pre><code class="html"><head>
<style>
.c-eyecatch {
/* css変数で キービジュアル の画像をページごとに切替 */
background-image: var(--img);
}
</style>
</head>
<body>
<!-- css変数で キービジュアル の画像をページごとに切替 -->
<div class="p-3 p-sm-5 c-eyecatch" style="--img:url('https://source.unsplash.com/ROVBDer29PQ/1920x1080')">
<div class="container">
<h1 class="display-4">ほげ</h1>
<p class="lead">ふがふが</p>
</div>
</div>
<main class="l-main">
<section class="container py-5">
<h1 class="display-4">ぴよ</h1>
<p>ほげら</p>
</section>
<!-- 流用 -->
<div class="p-3 p-sm-5 mb-4 c-eyecatch" style="--img:url('https://source.unsplash.com/lNxMcE8mvIM/1920x1080')"></div>
</body>
</code></pre>
<p>今回は css変数 を使って</p>
<ul>
<li>予め <code>head</code>タグ 内の <code>style</code>タグ では <code>background-image: var(--img);</code> でcss変数で指定しておく</li>
<li>各要素で変数の値を都度差し替える</li>
</ul>
<p>という形で画像を切り替える形式を採ってみました。</p>
<p>なお、今回はデモなので Jumbotron風 の実装で。</p>
<h2 id="ダークモード"><a href="#%E3%83%80%E3%83%BC%E3%82%AF%E3%83%A2%E3%83%BC%E3%83%89">ダークモード</a></h2>
<pre><code class="scss">// layout/_l-main.scss
@charset "utf-8";
@use "../foundation" as f;
.l-main {
background-color: f.$bg-color;
color: f.$color;
#dark > & {
/* ダークモード対応 */
@media (prefers-color-scheme: dark) {
background-color: f.$color;
color: f.$bg-color;
}
}
}
</code></pre>
<p>メディアクエリ <code>@media (prefers-color-scheme: dark)</code> によるダークモードを試しに記述。</p>
<p>ブログのような色に対して中立性の高いコンテンツの場合は使えそうですが、ブランドカラーが決まっている場合は難しそうな感。</p>
<p>個人的には基本ダークモードの方がありがたいのですが……。</p>
<h2 id="パララックス"><a href="#%E3%83%91%E3%83%A9%E3%83%A9%E3%83%83%E3%82%AF%E3%82%B9">パララックス</a></h2>
<p>最後はパララックス。これについてはパッケージを利用しました。</p>
<pre><code class="json"> "dependencies": {
"ukiyojs": "^3.0.0"
},
</code></pre>
<pre><code class="js">import Ukiyo from 'ukiyojs';
window.addEventListener( 'load', () => {
const ukiyoParallaxes = document.querySelectorAll( '.ukiyo' );
ukiyoParallaxes.forEach( ( ukiyoParallax ) => {
new Ukiyo( ukiyoParallax, {
scale: 1.75,
speed: 1.75,
} );
});
});
</code></pre>
<p>公式のドキュメント通りです。</p>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://github.com/yitengjun/ukiyo-js">GitHub - yitengjun\/ukiyo-js: ⛰️Modern parallax library for picture elements and any images</a></li>
</ul>
<p>検索すると自前実装も出てくるのですが、後々を考えるとパッケージの方が良さそうな気がするので。</p>
<h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2>
<h3 id="scroll-margin-top"><a href="#scroll-margin-top">scroll-margin-top</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://www.aizulab.com/blog/sticky-header-overlap/">アンカーリンクの遷移先が隠れる…。追従ヘッダーの重なりを回避するCSS | 会津ラボ</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://tech.arms-soft.co.jp/entry/2020/01/29/090000">アンカーリンクのズレをscroll-snapを使って直せるか試してみた - arms inc. Engineers' Blog</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://wb-hp.com/blog/2021/10/04/google-ie-support-ended.html">scroll-margin-top でヘッダー固定されたページのアンカーリンクの座標を調整する | ホワイトボードオフィシャルブログ</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://zenn.dev/catnose99/articles/75d3c69bf71bad">【追記: Safariでも動くようになった!】scroll-margin-topがsafariでうまく効かない問題と現状のワークアラウンド</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://developer.mozilla.org/ja/docs/Web/CSS/scroll-margin-top">scroll-margin-top - CSS: カスケーディングスタイルシート | MDN</a></li>
</ul>
<h3 id="Bootstrap 5 での jumbotron"><a href="#Bootstrap+5+%E3%81%A7%E3%81%AE+jumbotron">Bootstrap 5 での jumbotron</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://bootstrap-guide.com/sample/jumbotron-bgimage">背景が画像のジャンボトロンの実例~Bootstrap5設置ガイド</a></li>
</ul>
<h3 id="css変数 で背景画像指定"><a href="#css%E5%A4%89%E6%95%B0+%E3%81%A7%E8%83%8C%E6%99%AF%E7%94%BB%E5%83%8F%E6%8C%87%E5%AE%9A">css変数 で背景画像指定</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/sygnas/items/5a08d2462b6fc46850d7">CSS変数でbackground-imageを指定するとChromeとSafariで挙動が異なるのを解決する - Qiita</a></li>
</ul>
<h3 id="ダークモード"><a href="#%E3%83%80%E3%83%BC%E3%82%AF%E3%83%A2%E3%83%BC%E3%83%89">ダークモード</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://www.webcreatorbox.com/tech/dark-mode">Webサイトをダークモードに対応させよう | Webクリエイターボックス</a></li>
</ul>
<h3 id="パララックス"><a href="#%E3%83%91%E3%83%A9%E3%83%A9%E3%83%83%E3%82%AF%E3%82%B9">パララックス</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://zenn.dev/nye/articles/b3cbcf347a0291">モダンアニメーションのパララックス(視差効果)背景のライブラリを作った</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://github.com/yitengjun/ukiyo-js">GitHub - yitengjun\/ukiyo-js: ⛰️Modern parallax library for picture elements and any images</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://www.npmjs.com/package/ukiyojs">ukiyojs - npm</a></li>
</ul>
arm-band
tag:crieit.net,2005:PublicArticle/18177
2022-04-27T23:58:57+09:00
2022-04-27T23:58:57+09:00
https://crieit.net/posts/css-scroll-behavior-and-scroll-margin-top-note-20220428
(css) scroll-behavior と scroll-margin-top についてメモ
<p>css の <code>scroll-behavior</code> と <code>scroll-margin-top</code> について検証したのでメモ。</p>
<h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2>
<p>Bootstrap 5 で何気なく検証をしていたら、特に何をしたわけでもないのに同一ページ内のアンカーリンクにアニメーション付きでスクロールしたので気になりました。</p>
<p>自前の JavaScript では何も記述していないプレーンな状態だったので、 Bootstrap 5 の JavaScript かと思ったのですが、該当の CDN からの読み込みの <code>script</code>タグ を削除しても動作したので「すわ css 側か?」となり調べてみることにしました。</p>
<h2 id="調査結果"><a href="#%E8%AA%BF%E6%9F%BB%E7%B5%90%E6%9E%9C">調査結果</a></h2>
<p>結果、 <code>scroll-behavior</code>プロパティ に依るものと分かりました。具体的には、</p>
<pre><code class="css">html {
scroll-behavior: smooth;
}
</code></pre>
<p>この記述でページ全体にスクロールアニメーションが付与されていました。</p>
<p>しかも調べてみると、別ページのアンカーリンクに遷移する場合は</p>
<ol>
<li>ページ遷移</li>
<li>遷移先のページでアンカーリンクまで自動的にアニメーション付きでスクロール</li>
</ol>
<p>という挙動をすることが分かりました。</p>
<p>css で別ページのアンカーリンクまで対応しているのであれば、上述の挙動が気になる場合を除けばもはやこれ1つで済んでしまうのではないでしょうか……。</p>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://caniuse.com/?search=scroll-behavior">"scroll-behavior" | Can I use... Support tables for HTML5, CSS3, etc</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/yoshitake_1201/items/05a13fd77c18ff380eb6">iOSのバージョンと Safariバージョンの対応表(2022\/4\/6 現在 - Qiita</a></li>
</ul>
<p>注意点としては、 iOS Safari が15.4(2022/3/14リリース、2022/4/27記事執筆時点で最新版) のみサポートとなっているため、最新版以外の Safari のサポートが必要な場合は polyfill 等の代替手段を講じる必要性がありそうです。</p>
<p>PJAX 等のデフォルトではない遷移をした際の挙動については未検証なので何ともですが少し気になります。</p>
<p>それと、もう一点気になったのは Bootstrap のケースで言うとナビゲーションバーに <code>.fixed-top</code> を付けていた場合。つまりナビゲーションバーが画面上部に固定表示で追従するパターンですね。</p>
<p>頻出するパターンですが、このケースでは工夫しないとアンカーリンク移動時にナビゲーションバーの裏側にコンテンツが隠れてしまうという課題がありました。</p>
<p>かつてはこれを <code>margin-top: -80px; padding-top: 80px;</code> 等とネガティブマージンとパディングで調整・相殺するテクニックを使っていましたが……。</p>
<h2 id="scroll-margin-top"><a href="#scroll-margin-top">scroll-margin-top</a></h2>
<p>ここで兼ねてより目を付けていたのが <code>scroll-margin-top</code> 。 <code>scroll-snap</code> と呼ばれるスクロール時に特定の位置で止めたり誘導するプロパティの1つで、上述のアンカーリンク遷移時にナビゲーションバーの裏側に隠れてしまう問題を解決する方法として利用できます。</p>
<pre><code class="css">.anchor-link {
scroll-margin-top: 80px;
}
</code></pre>
<p>これまで上述のようなネガティブマージョンとパディングの相殺や、あるいは <code>before</code>疑似要素 を利用した方法で調整していた手間が、上述の通り1行指定するだけで解決できます。</p>
<p>css変数を使用するならば、</p>
<pre><code class="css">:root {
/* 変数定義 */
--navbar-height: 80px;
}
html {
/* スムーススクロール */
scroll-behavior: smooth;
}
.header {
/* ナビゲーションバーを上部固定表示 */
position: fixed;
top: 0;
left: 0;
width: 100%;
z-index: 10;
/* css変数を使用して高さを指定 */
height: var(--navbar-height);
}
.main {
/* スクロール可能なコンテンツ領域の上端部分はめり込むのでここは普通に margin-top でナビゲーションバー分下げる。css変数使用。 */
margin-top: var(--navbar-height);
}
.anchor-link {
/* アンカーリンクが裏側に隠れるのを抑止。css変数使用。 */
scroll-margin-top: var(--navbar-height);
}
</code></pre>
<p>このようなイメージですね。</p>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://caniuse.com/?search=scroll-margin-top">"scroll-margin-top" | Can I use... Support tables for HTML5, CSS3, etc</a></li>
</ul>
<p>ちなみにこちらは iOS Safari 14.7 以降で有効ですが、やはりそこそこ新しめの Safari である必要があるので、こちらもケースによっては注意が必要そうです。</p>
<h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2>
<h3 id="scroll-behavior"><a href="#scroll-behavior">scroll-behavior</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://coliss.com/articles/build-websites/operation/work/scroll-page-smoothly-by-css-and-javascript.html">CSSだけでも実装できる!ページ内アンカーやページ上部にアニメーションでスクロールさせるCSS, JavaScriptのまとめ | コリス</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://web.havincoffee.com/design/2020/09/2009281.html#inpage-ank21">CSSでページ内リンクをなめらかにスクロールする|web design-havin' a coffee break|珈琲とウェブデザイン</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://caniuse.com/?search=scroll-behavior">"scroll-behavior" | Can I use... Support tables for HTML5, CSS3, etc</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/yoshitake_1201/items/05a13fd77c18ff380eb6">iOSのバージョンと Safariバージョンの対応表(2022\/4\/6 現在 - Qiita</a></li>
</ul>
<h3 id="ネガティブマージョンとパディングの調整"><a href="#%E3%83%8D%E3%82%AC%E3%83%86%E3%82%A3%E3%83%96%E3%83%9E%E3%83%BC%E3%82%B8%E3%83%A7%E3%83%B3%E3%81%A8%E3%83%91%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0%E3%81%AE%E8%AA%BF%E6%95%B4">ネガティブマージョンとパディングの調整</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://designsupply-web.com/media/programming/1999/">ネガティブマージン・パディングで固定ヘッダー使用時のアンカーリンクに対応する</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://www.softel.co.jp/blogs/tech/archives/6083">【CSS】ページ内リンクのジャンプ先の位置を調整する at softelメモ</a>
<ul>
<li>疑似要素を使う手法もあり</li>
</ul></li>
</ul>
<h3 id="scroll-margin-top"><a href="#scroll-margin-top">scroll-margin-top</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://www.aizulab.com/blog/sticky-header-overlap/">アンカーリンクの遷移先が隠れる…。追従ヘッダーの重なりを回避するCSS | 会津ラボ</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://tech.arms-soft.co.jp/entry/2020/01/29/090000">アンカーリンクのズレをscroll-snapを使って直せるか試してみた - arms inc. Engineers' Blog</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://wb-hp.com/blog/2021/10/04/google-ie-support-ended.html">scroll-margin-top でヘッダー固定されたページのアンカーリンクの座標を調整する | ホワイトボードオフィシャルブログ</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://zenn.dev/catnose99/articles/75d3c69bf71bad">【追記: Safariでも動くようになった!】scroll-margin-topがsafariでうまく効かない問題と現状のワークアラウンド</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://developer.mozilla.org/ja/docs/Web/CSS/scroll-margin-top">scroll-margin-top - CSS: カスケーディングスタイルシート | MDN</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://caniuse.com/?search=scroll-margin-top">"scroll-margin-top" | Can I use... Support tables for HTML5, CSS3, etc</a></li>
</ul>
arm-band