<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
   <channel>
      <title>バシャログ。</title>
      <link>https://bashalog.c-brains.jp/</link>
      <description>横浜のシーブレイン Web制作スタッフによる技術情報ブログ。WebデザインやFireworks、WordPress、EC-CUBE、CakePHPなどの情報が満載！</description>
      <language>ja</language>
      <copyright>Copyright 2024</copyright>
      <lastBuildDate>Fri, 06 Jan 2023 15:37:25 +0900</lastBuildDate>
      <generator>http://www.sixapart.com/movabletype/?v=5.2</generator>
      <docs>http://blogs.law.harvard.edu/tech/rss</docs>

      
      <item>
         <title>【お知らせ】シーブレインではWebエンジニアを大募集中です！</title>
            <description>
                  <![CDATA[<p>採用ページを更新しました。</p>

<p>案件急増のためにWebエンジニアを募集しています。<br>
完全リモートワークの働きやすい環境で、弊社のプロジェクトに関わってみませんか！<br>
みなさまのご応募を心よりお待ちしております。</p>
<p>詳細は下記をご確認ください。<br>
<a href="https://web.c-brains.jp/recruit/" target="_blank" rel="noopener noreferrer">https://web.c-brains.jp/recruit/</a></p>]]>
            </description>
         <link>https://bashalog.c-brains.jp/23/01/06-153725.php</link>
         <guid>https://bashalog.c-brains.jp/23/01/06-153725.php</guid>
         <category>お知らせ</category>
         <pubDate>Fri, 06 Jan 2023 15:37:25 +0900</pubDate>
         <author>nakagiri</author>
      </item>
      
      <item>
         <title>【お知らせ】バシャログ。メンバーが所属する株式会社シーブレインのWebサイト制作サービスのサイトがリニューアルしました</title>
            <description>
                  <![CDATA[<p>本日は弊社シーブレインの<a href="https://web.c-brains.jp/" target="_blank">Webサイト制作サービスのサイト</a>をリニューアルしましたので、お知らせします。</p>

<p><a href="https://bashalog.c-brains.jp/images/old_layout_sample_220705.png" target="_blank">→リニューアル以前のデザインはこちら</a></p>

<p>今回のリニューアルにより、下記の2点が主に変更になっています。<br>（その他細かな部分も修正しています。）</p>
<ul>
<li>キービジュアルの変更</li>
<li>構成の変更</li>
</ul>

<h2>キービジュアルの変更について</h2>
<img alt="kv_220705.png" src="https://bashalog.c-brains.jp/images/kv_220705.png" width="1440" height="668" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto 20px;" />
<p>爽やかな青空が広がる横浜の風景にキャッチコピーを載せています。</p>
<p>弊社のWeb制作事業部がもっとも大切にしていること、それは「お客様に対してどこまでも誠実な対応を誇りとする」ということです。<br>
ファーストビュー（最初に目にする部分）を変更したことにより、コンセプトが一目で伝わるデザインとなりました。</p>

<h2>構成について</h2>
<p>リニューアル以前は縦長1ページで構成していたのですが、今回のリニューアルで「シーブレインにできること」「制作実績」「料金例」「会社概要」をそれぞれ階層に分けました。</p>
<p>縦長1ページの良さは、自然な導線で読んでもらうことができる点が挙げられますが、欲しい情報に辿り着けないとページを離脱してしまいます。<br>
そこで今回のリニューアルでは、TOPのページでは、弊社が提供するWebサービスの内容（＋お知らせ）を伝え、階層ページでその他の内容お伝えする構成に変更しました。</p>

<p>ぜひサイトを訪れてみてください。<br>
また、Webサイトのリニューアルでお悩みの方も、ぜひ弊社にご相談ください。</p>

<ul><li><a href="https://web.c-brains.jp/" target="_blank">株式会社シーブレイン Webサイト制作サービス</a></li></ul>]]>
            </description>
         <link>https://bashalog.c-brains.jp/22/07/05-115850.php</link>
         <guid>https://bashalog.c-brains.jp/22/07/05-115850.php</guid>
         <category>お知らせ</category>
         <pubDate>Tue, 05 Jul 2022 11:58:50 +0900</pubDate>
         <author>nakagiri</author>
      </item>
      
      <item>
         <title>【Docker】初心者向けDocker基礎編</title>
            <description>
                  <![CDATA[<p>こんにちは。nakagiriです。<br>
今年は梅雨が明けるのが早くて毎日真夏並みに暑いですね。( ´∵｀)<br>
みなさんも熱中症にならないように気をつけてください。</p>]]>
            </description>
         <link>https://bashalog.c-brains.jp/22/06/23-152746.php</link>
         <guid>https://bashalog.c-brains.jp/22/06/23-152746.php</guid>
         <category>docker</category>
         <pubDate>Thu, 23 Jun 2022 15:27:46 +0900</pubDate>
         <author>nakagiri</author>
      </item>
      
      <item>
         <title>【React】シンプルな「TODOアプリ」を作ってみる</title>
            <description>
                  <![CDATA[<p>こんにちわ。kyamashitaです。</p>
<p>「React」の連載第三弾ということで、シンプルなTODOアプリを実際に作ってみたいと思います。</p>]]>
            </description>
         <link>https://bashalog.c-brains.jp/22/06/09-183509.php</link>
         <guid>https://bashalog.c-brains.jp/22/06/09-183509.php</guid>
         <category>React.js</category>
         <pubDate>Thu, 09 Jun 2022 18:35:09 +0900</pubDate>
         <author>kyamashita</author>
      </item>
      
      <item>
         <title>【React】「Create React App」を用いた「React」の環境構築手順について</title>
            <description>
                  <![CDATA[<p>こんにちわ山崎です。今回は「React」の連載第二弾として、Facebook社が公式で出している「React」の環境構築ライブラリの「Create React App」を用いたReactの環境構築について書きたいと思います。</p>

<p>なお、「Create React App」の公式サイトは以下になります。<br>
<a href="https://ja.reactjs.org/docs/create-a-new-react-app.html" title="https://ja.reactjs.org/docs/create-a-new-react-app.html">https://ja.reactjs.org/docs/create-a-new-react-app.html "https://ja.reactjs.org/docs/create-a-new-react-app.html</a></p>
]]>
            </description>
         <link>https://bashalog.c-brains.jp/22/06/02-203802.php</link>
         <guid>https://bashalog.c-brains.jp/22/06/02-203802.php</guid>
         <category>React.js</category>
         <pubDate>Thu, 02 Jun 2022 20:38:02 +0900</pubDate>
         <author>yamazaki</author>
      </item>
      
      <item>
         <title>【React】イラストで基礎を解説してみる（初心者向け）</title>
            <description>
                  <![CDATA[<p>はじめまして。去年の9月にコーダーとして入社したnakagiriです！<br>
今日は、勉強中のReactについて解説します。</p>

<p>勉強する前は、先輩がReactをつかって開発をしているのは知っているけど、実際よく分からない。<br>
そんな状態だったので、この機会にReactについてどのような特徴があるのか一つ一つ整理してまとめてみます。</p>]]>
            </description>
         <link>https://bashalog.c-brains.jp/22/05/19-141946.php</link>
         <guid>https://bashalog.c-brains.jp/22/05/19-141946.php</guid>
         <category>React.js</category>
         <pubDate>Thu, 19 May 2022 14:19:46 +0900</pubDate>
         <author>nakagiri</author>
      </item>
      
      <item>
         <title>ブラウザで録音した音源とアップロードした音源を合成する</title>
            <description>
                  <![CDATA[<p>こんにちわ。kyamashitaです。</p>
<p>前回はブラウザから録音させてみましたので、今回は録音した音源とアップロードした音源を合成させてみます。</p>]]>
            </description>
         <link>https://bashalog.c-brains.jp/22/03/15-174728.php</link>
         <guid>https://bashalog.c-brains.jp/22/03/15-174728.php</guid>
         <category>JavaScript</category>
         <pubDate>Tue, 15 Mar 2022 17:47:28 +0900</pubDate>
         <author>kyamashita</author>
      </item>
      
      <item>
         <title>【WordPress】ページや権限によってアクセス制限をかける</title>
            <description>
                  <![CDATA[<p>みなさんこんにちは、yamashitaです。</p>

<p>今回はWordPressでログインの有無やユーザー権限によってアクセスできるページを設定する方法を紹介します。</p>

<p>これにより、クローズドな情報などを掲載することができます。
パスワード保護もありますが、複数ページを同時にアクセス制限する場合などに使えます。</p>
]]>
            </description>
         <link>https://bashalog.c-brains.jp/21/12/28-145959.php</link>
         <guid>https://bashalog.c-brains.jp/21/12/28-145959.php</guid>
         <category>WordPress</category>
         <pubDate>Tue, 28 Dec 2021 14:59:59 +0900</pubDate>
         <author>yamashita</author>
      </item>
      
      <item>
         <title>Compoships 関連モデルを複数の外部キーで紐付ける[Laravel]</title>
            <description>
                  <![CDATA[<p>どうもfujiharaです。今回はLaravelで関連モデルを設定する時に複数の外部キーで紐付ける方法をご紹介します。
同じ悩みを抱えてる方がこの記事のタイトルで発見してくれるか不安です。</p>

<h2>背景</h2>
<p>外部サービスからデータをインポートする時に以下のような状態でした。（例）</p>
<img alt="20211217_fujihara_01.png" src="https://bashalog.c-brains.jp/images/20211217_fujihara_01.png" width="835" height="606" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto 20px;" />
<p>Cityのcodeがユニークかと思ったら、prefecture_codeとの複合ユニークでHospitalと紐付けるには
外部キーを複数で設定する必要がありました。<br>
インポートする時にidで紐付けるようにすればとも思ったんですが、インポート時に検索クエリを出したくないのもあり
そのままインポートしました。</p>

<h2>Compoships</h2>
<p>今回の場合、HospitalはCityにBelongsToなのですが、LaravelのBelongsToを
設定する際に設定できる外部キーは1つだけなので<a href="https://github.com/topclaudy/compoships">Compoships</a>を導入しました。</p>
<p>設定する時は双方のModelでComposhipを使用します。</p>

<pre>
<code>
App\Models\Hospital.php内 (設定箇所のみ

<p>    use Compoships;</p>

<p>    public function city(): BelongsTo<br />
    {<br />
        return $this->belongsTo(<br />
            City::class,<br />
            ['city_code', 'prefecture_code'], //Hospital側のキー<br />
            ['code', 'prefecture_code'], //City側のキー<br />
        );<br />
    }</p>

<p>App\Models\City.php内（設定箇所のみ</p>

<p>    use Compoships;<br />
</code><br />
</pre></p>

<h2>まとめ</h2>
<p>その他にhasOne, HasManyにも使えます。それでは素敵なRelation生活を。</p>]]>
            </description>
         <link>https://bashalog.c-brains.jp/21/12/17-170000.php</link>
         <guid>https://bashalog.c-brains.jp/21/12/17-170000.php</guid>
         <category>Laravel</category>
         <pubDate>Fri, 17 Dec 2021 17:00:00 +0900</pubDate>
         <author>fujihara</author>
      </item>
      
      <item>
         <title>ShifterやWP2Staticで出力されないパスを出力する（リクエストする）[WordPress]</title>
            <description>
                  <![CDATA[<p>どうもfujiharaです。
今回は<a href="https://www.getshifter.io/">Shifter</a>や<a href="https://wp2static.com/download/">WP2Static</a>で
任意のパスを書き出したいときのTips紹介になります。</p>

<h2>背景</h2>
<p>Jamstack流行ってますね。WordPressでもちらほら対応することが増えてきました。
ShifterやWP2Staticは自動でパスを探して出力してくれるのですが、
追加でwp-jsonのパスも出力して欲しいことがある場合もあります
（URL検索ではヒットしない）。
そんな時に、出力時このパスもチェックしてね！という機能があるのでご紹介します。
</p>

<h2>Shifter</h2>
<p><a href="https://support.getshifter.io/en/articles/4233156-appending-urls-to-the-artifacts" target="_blank">ShifterURLS::AppendURLtoAll</a>
フィルターを使います.</p>

<pre>
<code>
function append_additional_url ( $urls ) {
    $urls[] = home_url('任意のパス');
    return $urls;
}
add_action( 'init', function(){
    add_filter( 'ShifterURLS::AppendURLtoAll', 'append_additional_url' );
} );
</code>
</pre>

<p>$urlsの配列にどんどん追加して欲しいパスを書いていきます</p>

<h2>WP2Static</h2>
<p><a href="https://wp2static.com/developers/extending-wp2static" target="_blank">Extending WP2Static</a>
の各種Filterを使います。</p>

<pre>
<code>
//追加
add_filter( 'wp2static_modify_initial_crawl_list', function ( $queue ) {
	$queue[]  = '任意のパス';

<p>	return $queue;<br />
} );<br />
</code><br />
</pre></p>

<pre>
<code>
//削除
add_filter('wp2static_detect_xxxxxxx', '__return_false');
</code>
</pre>

<p>WP2Staticはxxxxxの箇所にsitemaps,authorsなどがあるので書き出したくないパスがあれば
それも設定できます。カテゴリーパスを書き出す時に日本語のままで書き出され、CloudFrontで
日本語エラーが発生する時はwp2static_detect_categoriesで削除して、wp2static_modify_initial_crawl_list
で追加してあげれば良いです。
</p>

<h2>まとめ</h2>
<p>それでは素敵なWordPress Jamstack生活を</p>]]>
            </description>
         <link>https://bashalog.c-brains.jp/21/12/17-150000.php</link>
         <guid>https://bashalog.c-brains.jp/21/12/17-150000.php</guid>
         <category>WordPress</category>
         <pubDate>Fri, 17 Dec 2021 15:00:00 +0900</pubDate>
         <author>fujihara</author>
      </item>
      
      <item>
         <title>React Testing Libraryとjestを使ってReactアプリケーションのテストをしてみた</title>
            <description>
                  <![CDATA[<p>こんにちわ、山崎です。久々の投稿になります。</p>

<p>今まで自動テストをやってこなかったので、「React Testing Library」と「jest」を組み合わせてReactアプリケーションのテストをする方法を学んだので、アプリケーションテストの簡単なサンプルを紹介したいと思います。</p>

<h2>create-react-appでReactアプリの雛形を作成する</h2>

<p>まず<code>create-react-app</code>でReactアプリケーションの雛形を作成します。
（今回仕様したnode.jsのバージョンは<code>v15.14.0</code>になります。）</p>

<pre>
<code>npx create-react-app rts
</code>
</pre>

<h2>テスト対象のコンポーネントを作成する</h2>

<p>そして、次にテストするReactのコンポーネントを作成します。
今回は超シンプルなToDoアプリを作成しました。</p>

<p>まず、<code>src</code>ディレクトリに<code>Todo.js</code>を作成します。</p>

<pre>
<code>import React from "react";

const Todo = () =&gt; {
    const [input, setInput] = React.useState('');
    const [list, setList] = React.useState(['タスクA', 'タスクB', 'タスクC']);
    const updateValue = (e) =&gt; {
        setInput(e.target.value);
    }
    const addNew = () =&gt; {
        let copiedList = [...list];
        copiedList.push(input);
        setList(copiedList);
        setInput('');
    }
    return (
        &lt;div className="todo"&gt;
            &lt;input type="text" onChange={updateValue} value={input} /&gt;
            &lt;button disabled={!input} onClick={addNew}&gt;Todoを追加&lt;/button&gt;
            &lt;ul&gt;
                {list.map((item, index) =&gt; (
                    &lt;li key={index}&gt;{item}&lt;/li&gt;
                ))}
            &lt;/ul&gt;
        &lt;/div&gt;
    )
}

export default Todo;
</code>
</pre>

<p>今回はあくまでテストの記事なのでコードの説明は省略しますが、仕様としてはテキストボックスを入力したらボタンのdisabledが解除されボタンを押したらTodoのリストに入力した内容が追加され、入力した内容がクリアされるという超シンプルなものです。</p>

<h2>テストファイルを作成する</h2>

<p>次に上記で作成したコンポーネントに対するテストファイルを作成していきます。</p>

<p><code>create-react-app</code>でインストールした雛形には標準でテストが搭載されています。</p>

<p><code>Todo.js</code>のコンポーネントに対するテストファイルは<code>Todo.test.js</code>という名前で作成します。</p>

<p>まず最初に必要なツールをimportしましょう。</p>

<pre>
<code>import React from 'react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import Todo from './Todo';
</code>
</pre>

<p>まず、最初にreactをimportします。</p>

<pre>
<code>import React from 'react';
</code>
</pre>

<p>そして次に、<code>@testing-library/react</code>から<code>render</code>関数と<code>screen</code>関数をimportします。
<code>'@testing-library/user-event</code>からrenderしたDOM要素に対してイベントを発火させる<code>userEvent</code>をimportします。</p>

<pre>
<code>import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
</code>
</pre>

<p>そして最後に先ほど作成したコンポーネントをimportします。</p>

<pre>
<code>import Todo from './Todo';
</code>
</pre>

<p>これでテストの準備が整いました。</p>

<h2>ここから実際にテストを書いていきます。</h2>

<h3>テスト1</h3>

<p>まず、初期状態でボタンがdisabledになっているかテストを書いていきます。</p>

<pre>
<code>describe('Todoコンポーネントテスト', () =&gt; {
  it('初期状態でdisabledになっているか', () =&gt; {
    render(&lt;Todo /&gt;);
    const button = screen.getByRole('button');
    expect(button).toBeDisabled();
  });
});
</code>
</pre>

<p><code>describe</code>というブロックの中に入れ子で<code>it</code>関数を使ってそれぞれテストケースを書いていきます。
<code>it</code>関数の第一引数にはテストケースの名前を定義し、第二引数にテストで行う一連の処理を記述します。</p>

<p>まず、第一にコンポーネントをレンダーします。</p>

<pre>
<code>render(&lt;Todo /&gt;);
</code>
</pre>

<p>そしてレンダーしたコンポーネント内の要素にアクセスするには<code>React Testing Library</code>の<code>screen</code>を使用します。
<code>getByRole</code>メソッドの引数に<code>button</code>を使用するとレンダーしたDOMの最初のbuttonタグの要素にアクセスすることができます。
（ボタン要素が複数ある可能性があるので本来であればid指定などで要素を取得した方が良いです。）</p>

<pre>
<code>const button = screen.getByRole('button');
</code>
</pre>

<p>次にexpect関数を使って実際にテストをします。</p>

<pre>
<code>expect(button).toBeDisabled();
</code>
</pre>

<p>仕組みとしてはとてもシンプルで<code>expect</code>関数の第一引数に検証したい値を代入し、
その後に検証したい内容に応じて、メソッドを呼び出します。
上記の例では<code>expect</code>の引数に入れたボタン要素がdisabledになっているかを検証しています。</p>

<p>テストケースが1つできたので、実際にこのテストを実行してみます。
Reactプロジェクトのルートディレクトリで、以下のコマンドをうちます。</p>

<pre>
<code>npm run test
</code>
</pre>

<p>すると下記の通り、テストが実行され、1つのテストがパスしているのが分かります。
<img alt="スクリーンショット 2021-09-29 21.28.02.png" src="https://bashalog.c-brains.jp/images/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88%202021-09-29%2021.28.02.png" width="510" height="232" class="mt-image-none" style="" /></p>

<p>逆にこのテストがちゃんと正しく動作しているか確かめる為にあえて下記のようにボタンのdisabled属性を外してしまいます。</p>

<pre>
<code>&lt;button onClick={addNewTodo}&gt;Todoを追加&lt;/button&gt;
</code>
</pre>

<p>すると、<code>Recieved element is not disabled</code>となっている通り、
button要素がdisabledになっていないので、テストが失敗していることが分かりました。</p>

<p><img alt="スクリーンショット 2021-09-29 21.35.29.png" src="https://bashalog.c-brains.jp/images/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88%202021-09-29%2021.35.29.png" width="604" height="276" class="mt-image-none" style="" /></p>

<h3>テスト2</h3>

<p>次にまた<code>it</code>関数を使用して別のテストを書いていきます。
inputに値を入力すると、ボタンのdisabledが解除されるかどうかをテストします。</p>

<pre>
<code>it('inputに値を入力するとボタンのdisabledが解除されるか', () =&gt; {
    render(&lt;Todo /&gt;);
    const input = screen.getByRole('textbox');
    const button = screen.getByRole('button');
    userEvent.type(input, 'hoge');
    expect(button).not.toBeDisabled();
});
</code>
</pre>

<p>まず、先ほどのテストと同じように要素を取得します。inputタグの場合はテキストボックスの分類になるので、
<code>screen.getByRole('textbox')</code>で取得することができます。</p>

<pre>
<code>render(&lt;Todo /&gt;);
const input = screen.getByRole('textbox');
const button = screen.getByRole('button');
</code>
</pre>

<p>そしていよいよ<code>userEvent</code>の出番です。
<code>type</code>メソッドを使用することで、inputタグに値を入力することができます。
第一引数にイベントを発火させる要素、第二引数に入力する文字列を加えます。</p>

<pre>
<code>userEvent.type(input, 'hoge');
</code>
</pre>

<p>そして、OOでないことを検証したい場合は下記のように、
expectのメソッドの前に<code>.not</code>とつけます。</p>

<pre>
<code>expect(button).not.toBeDisabled();
</code>
</pre>

<p>こうすることによってボタンのdisabledが解除されているか検証できるようになりました。</p>

<h3>テスト3</h3>

<p>最後に、ボタンをクリックした後に、Todoがリストに追加されるかを検証します。</p>

<pre>
<code>it('ボタンをクリックするとTodoが追加されるか', () =&gt; {
    render(&lt;Todo /&gt;);
    const input = screen.getByRole('textbox');
    const button = screen.getByRole('button');
    const listItem = screen.getAllByRole('listitem');
    userEvent.type(input, 'hoge');
    userEvent.click(button);
    const listItemAfterClicked = screen.getAllByRole('listitem');
    expect(listItemAfterClicked.length).toBeGreaterThan(listItem.length);
    expect(listItemAfterClicked[listItemAfterClicked.length - 1].textContent)
        .toEqual('hoge');
});
</code>
</pre>

<p>まず、使用する要素を定義します。liは複数存在するので、<code>getAllByRole('listitem')</code>とすることで要素の配列を取得することができます。</p>

<pre>
<code>render(&lt;Todo /&gt;);
const input = screen.getByRole('textbox');
const button = screen.getByRole('button');
const listItem = screen.getAllByRole('listitem');
</code>
</pre>

<p>次にinputに値を入力した後にボタンをクリックします。
<code>click</code>メソッドでは第一引数にクリックしたい要素をいれるのみです。</p>

<pre>
<code>userEvent.type(input, 'hoge');
userEvent.click(button);
</code>
</pre>

<p>そして、改めて<code>screen.getAllByRole('listitem')</code>でli要素を取得し、
<code>toBeGreaterThan</code>メソッドでボタンクリック前のli要素の数とクリック後のli要素の数を比較しています。</p>

<p>expectで渡した数値が上回っていると期待れる数値を<code>toBeGreaterThan</code>要素の引数に加えます。</p>

<pre>
<code>const listItemAfterClicked = screen.getAllByRole('listitem');
expect(listItemAfterClicked.length).toBeGreaterThan(listItem.length);
</code>
</pre>

<p>そして、最後のli要素が<code>hoge</code>であることも検証する場合は下記のようにします。
<code>toEqual</code>はexpectで渡した文字列（もしくは数値）が引数と一致するかを検証します。</p>

<pre>
<code>expect(listItemAfterClicked[listItemAfterClicked.length - 1].textContent)
    .toEqual('hoge');
</code>
</pre>

<h3>最後に</h3>

<p>下記が<code>Todo.test.js</code>の全コードになります。</p>

<pre>
<code>
import React from 'react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import Todo from './Todo';

describe('Todoコンポーネントテスト', () =&gt; {
  it('初期状態でボタンがdisabledになっているか', () =&gt; {
    render(&lt;Todo /&gt;);
    const button = screen.getByRole('button');
    expect(button).toBeDisabled();
  });

  it('inputに値を入力するとボタンのdisabledが解除されるか', () =&gt; {
    render(&lt;Todo /&gt;);
    const input = screen.getByRole('textbox');
    const button = screen.getByRole('button');
    userEvent.type(input, 'hoge');
    expect(button).not.toBeDisabled();
  });

  it('ボタンをクリックするとTodoが追加されるか', () =&gt; {
    render(&lt;Todo /&gt;);
    const input = screen.getByRole('textbox');
    const button = screen.getByRole('button');
    const listItem = screen.getAllByRole('listitem');
    userEvent.type(input, 'hoge');
    userEvent.click(button);
    const listItemAfterClicked = screen.getAllByRole('listitem');
    expect(listItemAfterClicked.length).toBeGreaterThan(listItem.length);
    expect(listItemAfterClicked[listItemAfterClicked.length - 1].textContent)
        .toEqual('hoge');
    expect(input.value)
        .toEqual('');
  });
});
</code>
</pre>

<p>今回は非常にシンプルなアプリケーションだった為、このサンプルではテストの恩恵があまり感じられないかと思いますが、
テストケースが多かったり複雑だったりするパターンや、コードが複雑でちょっといじっただけで動かなくなってしまったっといったことを事前に防ぐ為にも今後はしっかり自動テストを活用していけたらと思います。</p>
]]>
            </description>
         <link>https://bashalog.c-brains.jp/21/10/01-180719.php</link>
         <guid>https://bashalog.c-brains.jp/21/10/01-180719.php</guid>
         <category>React.js</category>
         <pubDate>Fri, 01 Oct 2021 18:07:19 +0900</pubDate>
         <author>yamazaki</author>
      </item>
      
      <item>
         <title>getUserMedia、MediaRecorderを使ってブラウザで録音する</title>
            <description>
                  <![CDATA[<p>久しぶりの投稿となりました。kyamashitaです。</p>
<p>今回はgetUserMedia、MediaRecorderを使ってブラウザで録音してみます。</p>]]>
            </description>
         <link>https://bashalog.c-brains.jp/21/09/10-102902.php</link>
         <guid>https://bashalog.c-brains.jp/21/09/10-102902.php</guid>
         <category>JavaScript</category>
         <pubDate>Fri, 10 Sep 2021 10:29:02 +0900</pubDate>
         <author>kyamashita</author>
      </item>
      
      <item>
         <title>BrowserSyncでAliasっぽい設定</title>
            <description>
                  <![CDATA[<p>どうもfujiharaです。ジメジメして生活しづらい季節ですね。本日はBrowserSyncでAliasを使いたいと思い調べていたら、似たように
使用できる方法があったのでご紹介いたします。（知らなかった。。。）</p>

<h2>背景</h2>
<p>弊社ではシステム開発する際にコーディングリポジトリとシステムリポジトリを分けることがあるのですが、
コーディングのCSS,JS,IMGをシステムリポジトリに反映するのが結構手間で、以前ブログに書きました
<a href='https://bashalog.c-brains.jp/20/05/19-123000.php'>gulp-directory-sync</a>を使い、同じリポジトリで開発できるようしていました。
しかしこの方法だとファイルが2重管理になるので嬉しくありませんでした。</p>
<p>でAliasの設定ってないのかな（あるだろう）と思い調べていたら、
なかったのですがオプションで<a href='https://browsersync.io/docs/options#option-server' target='_blank'>Multiple base directories</a>というものがありました。
これを使えばdirectory-syncを使わずに効率良くできます。</p>

<h2>例</h2>
<p>以下のようになっているとします。本来srcにある各種ファイルをgulp, webpackなどでコーディング確認用のhtdocs/assetsに書き出していたのを、
フレームワーク用のfw_webroot/assetsに書き出します。
さらにbrowser-syncでデザイン確認時にはhtdocs/assetsが参照できるようにします。</p>
<pre>
<code>
[~/Downloads/bashalog]$ tree -I node_modules
.
├── bs-config.js
├── fw_webroot (システム用webroot)
│   └── assets (システム用assets)
│       ├── css
│       │   └── style.css
│       ├── img
│       └── js
│           └── index.js
├── htdocs
│   ├── assets (<---ここをAliasにしたい)
│   └── index.html
├── package.json
├── src (各種ソースファイル )
└── yarn.lock
</code>
</pre>

<p>BrowserSync設定(serverオプション部分のみ)</p>
<pre>
<code>

<p>server: {<br />
    baseDir: "htdocs",<br />
    index: "index.htm"<br />
}</p>

<p>↓</p>

<p>server: {<br />
    baseDir: ["htdocs", "fw_webroot"],<br />
    index: "index.htm"<br />
}<br />
</code><br />
</pre></p>

<h2>まとめ</h2>
<p>たったこれだけでhtdocs/assetsがなくてもfw_webrootもBaseDirectoryになるのでファイルを参照できるようになります。オプションはちゃんと見ましょう（自戒）。
ぜひお試しください</p>]]>
            </description>
         <link>https://bashalog.c-brains.jp/21/06/28-193000.php</link>
         <guid>https://bashalog.c-brains.jp/21/06/28-193000.php</guid>
         <category>gulp</category>
         <pubDate>Mon, 28 Jun 2021 19:30:00 +0900</pubDate>
         <author>fujihara</author>
      </item>
      
      <item>
         <title>【MovableType】include_blogsを使わないテンプレート運用</title>
            <description>
                  <![CDATA[<p>どうもfujiharaです。今年は全国的に梅雨が入り早いそうです。人は話題が無い場合天気の話をすることが多いらしいです。<br>
本日はMovableTypeの<a href='https://www.movabletype.jp/documentation/mt6/design/multiblog/tags.html#blogids' target='_blank'>マルチブログ機能</a>
のinclude_blogsを使わないで、ターゲットとなるブログ情報を取得する方法をご紹介します。</p>

<h2>背景</h2>
<p>ブログ（サイト）で別のブログを出力する場合以下のようにするのが一般的かと思います。</p>

<pre>
<code>
&lt;mt:Entries include_blogs='x'&gt;
...
...
...
&lt;/mt:Entries&gt;
</code>
</pre>

<p>ただ、サイトをバックアップ、復元するとブログ、サイトのIDがずれるのでそのたびに直さないといけません。
なのでブログ名で判定し以下のようにしていました。(今回の場合ブログ名はtarget_blog_name）</p>

<pre>
<code>
&lt;mt:Blogs&gt;
&lt;mt:If tag="BlogName" eq="target_blog_name"&gt;
...
...
&lt;/mt:If&gt;
&lt;/mt:Blogs&gt;
</code>
</pre>

<h2>新しい方法</h2>
<p>ここ何年かで弊社ではMovableType.netで構築するほうが圧倒的に多く、その時に.netの機能でブログ（サイト）に識別子を付けられ、
そこから対象のデータを表示できます。そこからヒントを得てMovableTypeにそれを実装します。<a href='https://movabletype.net/tags/multiblog.html#blogids' target='_blank'>参考URL</a>
</p>

<p>まず、システムで子サイト用のカスタムフィールドをblog_identifier(任意）:テキストで作成します。</p>

<p><img alt="fujihara_20210517" src="https://bashalog.c-brains.jp/images/Screen%20Shot%202021-05-17%20at%2020.19.02.png" width="499" height="904" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto 20px;" /></p>

<p>その後、ターゲットとなるブログの設定画面からblog_identifierを入力します。</p>

<p><img alt="fujihara_20210517_2.png" src="https://bashalog.c-brains.jp/images/Screen%20Shot%202021-05-17%20at%2020.21.02.png" width="508" height="423" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto 20px;" /></p>

<p>最後にテンプレートで以下のようにします。今回の場合identifierはsample</p>

<pre>
<code>
&lt;mt:Blogs&gt;
&lt;mt:If tag="blog_identifier" eq="sample"&gt;
&lt;mt:Entries&gt;
...
...
&lt;/mt:Entries&gt;
&lt;/mt:If&gt;
&lt;/mt:Blogs&gt;
</code>
</pre>

<h2>まとめ</h2>
<p>これで、include_blogsのIDズレに惑わされることなく、テンプレートの運用ができます。
include_blogsで疲れた方は是非お試しください。
</p>]]>
            </description>
         <link>https://bashalog.c-brains.jp/21/05/17-203000.php</link>
         <guid>https://bashalog.c-brains.jp/21/05/17-203000.php</guid>
         <category>MovableType</category>
         <pubDate>Mon, 17 May 2021 20:30:00 +0900</pubDate>
         <author>fujihara</author>
      </item>
      
      <item>
         <title>URLクラスを使って、クエリストリングを簡単に扱う</title>
            <description>
                  <![CDATA[<p>みなさん。こんにちは。
久しぶりの投稿となります。</p>

<p>今回はとある案件でURLにクエリストリングを付与したり削除したりという機能を開発していた時、URLのクエリ部分をオブジェクトにしたり、そのオブジェクトをクエリストリングに戻したりという処理がすごく面倒だったので、
クエリストリングの操作が楽になるライブラリを探していた所、JavaScript(ES6以降)標準で入っているURLクラスで簡単にいったので記事にしてみました。</p>

<p>今回は、チェックボックスにチェックがついたら<code>newKey</code>というパラメーターがURLに追加されるようにしようと思います。</p>

<h2>デモ</h2>

<p><a href="https://bashalog.c-brains.jp/videos/%E7%94%BB%E9%9D%A2%E5%8F%8E%E9%8C%B2%202021-04-14%2016.56.24.mov">画面収録 2021-04-14 16.56.24.mov</a></p>

<h2>HTML</h2>

<pre>
<code>&lt;!DOCTYPE html&gt;
&lt;html lang="ja"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;title>URLSearchParamsを使ってみた&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;label&gt;
    &lt;input type="checkbox" id="js-checkbox"&gt;
    &lt;span>クエリを付与&lt;/span&gt;
&lt;/label&gt;
&lt;script src="./url-polyfill.js">&lt;/script>&lt;!-- IE11対応 --&gt;
&lt;script> //JSの処理 &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
&lt;/code&gt;
&lt;/pre&gt;</code>
</pre>

<h2>JavaScript</h2>

<pre>
<code>const checkbox = document.getElementById('js-checkbox');
let paramsStr = window.location.search.substring(1); // window.location.searchから先頭の「?」を抜いた文字列を代入
let paramsArray = paramsStr.split('&'); // クエリストリングを「&」で分割したものを配列にする。
let paramsObject = {}; // 各キーと値のオブジェクト
let newParamsStr = '?'; // クエリストリング
if (paramsStr) {
    paramsArray.forEach(function (val, i) {
        let param = paramsArray[i].split('='); // キーと値を分離
        paramsObject[param[0]] = param[1]; // オブジェクトのキーを作成
    });
} // ここまでで、やっとオブジェクトが作成される。

function resetParamsStr() { // paramsObjectが変更された時にクエリストリングを再生成する関数
    newParamsStr = '?';
    let keys = Object.keys(paramsObject); // オブジェクトのキーを配列に
    let lastKeyIndex = keys.length - 1; // 最後のキーのindex番号
    if (keys.length) {
        keys.forEach(function (key, index) {
            newParamsStr += key+'='+paramsObject[key];
            if (index === lastKeyIndex) { // ループの最後だったら後ろに「&」はいらないので終了
                return;
            }
            newParamsStr += '&'; // 「&」を後ろにつける。
        });
    } else {
        newParamsStr = ''; // キーがない（objectが空だった場合）文字列を空にする
    }
    history.pushState(null, null, window.location.pathname + newParamsStr + window.location.hash); // URLを変更
}

checkbox.addEventListener('change', function () { // checkboxにイベントを追加
    let checked = checkbox.checked; // checkboxがチェックされているかどうか
    if (checked) {
        if ('newKey' in paramsObject) {
            return; // 既にキーが存在していたら何もしない
        }
        paramsObject['newKey'] = 'newValue'; // paramsObjectにキーを追加
        resetParamsStr(); // クエリストリングを再生成
    } else {
        if (!('newKey' in paramsObject)) {
            return; // キーが存在しなかったら何もしない
        }
        delete paramsObject.newKey; // paramsObjectからキーを削除
        resetParamsStr(); // クエリストリングを再生成
    }
});</code>
</pre>

<p>クエリストリングをオブジェクトに変換するだけでsplitで分割して、キーと値をさらに分割して...などの色んな処理を挟み、とても長くすっきりしないコードになってしまいました。</p>

<p>※アンカーリンクを併用する場合は、下記の順番で入れます。
<code>「URL + クエリストリング + #アンカー」</code>
もし、
<code>「URL + #アンカー + クエリストリング」</code>
という順番で入れてしまうと<code>window.location.search</code>が取得できないようです。</p>

<p>これをURLクラスを使って書き換えると以下になります。</p>

<pre>
<code>const checkbox = document.getElementById('js-checkbox');
const url = new URL(window.location.href); // 現在のURLを素としたURLクラスのインスタンスを作り、url定数に格納
checkbox.addEventListener('change', function () {
    let checked = checkbox.checked;
    if (checked) {
        if (url.searchParams.has('newKey')) {
            return;
        }
        url.searchParams.append('newKey', 'newValue'); // クエリパラメーターにnewKeyを追加
    } else {
        if (!url.searchParams.has('newKey')) {
            return;
        }
        url.searchParams.delete('newKey'); // クエリパラメーターからnewKeyを削除
    }
    history.pushState(null, null, url.href); // URLを変更
});</code>
</pre>

<p>とてもすっきりとしたコードになりました。
まず、二行目で現在のURLを素としたURLクラスのインスタンスを作り、url定数に格納します。</p>

<pre><code>const url = new URL(window.location.href);</code></pre>

<p>checkboxにチェックが入っていたら、下記のようにクエリパラメーターのキーを追加します。</p>

<pre><code>url.searchParams.append('newKey', 'newValue');</code></pre>

<p>checkboxのチェックがはずれていたら、下記のようにクエリパラメーターのキーを削除します。</p>

<pre><code>url.searchParams.append('newKey', 'newValue');</code></pre>

<p>最後にURLを書き換えます。<code>url.href</code>の中身は<code>url.searchParams</code>のキーを追加したり削除したりすることでクエリストリング部分が変化します。</p>

<pre><code>history.pushState(null, null, url.href);</code></pre>

<p>上のコードと比べていちいち、文字列→オブジェクト、オブジェクト→文字列と変換する手間もなくよっぽどシンプルになったかと思います。</p>

<p>また、URLクラスはIE11では非対応なので、IE11でも動作させたい場合は、下記のpolyfillを使ってください。
<a href="https://www.npmjs.com/package/url-polyfill">https://www.npmjs.com/package/url-polyfill</a></p>

<p>最後まで読んでいただき、ありがとうございました。</p>
]]>
            </description>
         <link>https://bashalog.c-brains.jp/21/04/14-165729.php</link>
         <guid>https://bashalog.c-brains.jp/21/04/14-165729.php</guid>
         <category>JavaScript</category>
         <pubDate>Wed, 14 Apr 2021 16:57:29 +0900</pubDate>
         <author>yamazaki</author>
      </item>
      
      <item>
         <title>Browser Image Compressionで画像をアップロード前に圧縮する[Javascript]</title>
            <description>
                  <![CDATA[<p>どうもfujiharaです。本日は画像をアップロード前に圧縮できる
<a href="https://www.npmjs.com/package/browser-image-compression" target='_blank'>BrowserImageCompression</a>をご紹介します。</p>

<h2>インストール</h2>
<pre>
<code>
npm install browser-image-compression --save
or
yarn add browser-image-compression
</code>
</pre>

<h2>コード</h2>
<p>以下が簡単な確認用コードになります。(react-create-appで作成)</p>

<pre>
<code>
import './App.css';
import { useState } from 'react';
import imageCompression from 'browser-image-compression';

<p>function App() {<br />
  const [image_url, setImageUrl] = useState('');<br />
  const compressOption = {<br />
    maxSizeMB: 1,<br />
    maxWidthOrHeight: 1024<br />
  };</p>

<p>  return (<br />
    &lt;div className="App"&gt;<br />
      &lt;form onSubmit={async (e) =&gt; {<br />
        e.preventDefault();<br />
        const form = new FormData(e.target);<br />
        const postForm = new FormData();<br />
        const file = form.get('image');<br />
        const compressFile = await imageCompression(file ,compressOption);<br />
        postForm.append('image', compressFile, file.name);<br />
        alert(`default_size: ${file.size} \n compressed_size: ${compressFile.size}`);</p>

<p>        // fetch postForm<br />
      }}<br />
      &gt;<br />
        &lt;input name="image" accept="image/jpeg,image/png" type="file"<br />
          onChange={async (e) =&gt; {<br />
            setImageUrl('');<br />
            if (!e.target.files[0]) {<br />
              return;<br />
            }<br />
            const img = e.target.files[0];<br />
            try {<br />
              const compressFile = await imageCompression(img, compressOption);<br />
              const url = await imageCompression.getDataUrlFromFile(compressFile);<br />
              setImageUrl(url);<br />
            } catch (error) {<br />
              console.log(error);<br />
            }<br />
          }}<br />
        /&gt;<br />
        {image_url && &lt;figure&gt;<br />
          &lt;img src={image_url} alt=''/&gt;<br />
        &lt;/figure&gt;<br />
        }<br />
        &lt;div className='mod-button'&gt;<br />
          &lt;button type="submit"&gt;送信&lt;/button&gt;<br />
        &lt;/div&gt;<br />
      &lt;/form&gt;<br />
    &lt;/div&gt;<br />
  );<br />
}</p>

<p>export defau&lt; App;<br />
</code><br />
</pre></p>

<p><a href="https://bashalog.c-brains.jp/images/fujihara_20210304.gif"><img alt="fujihara_20210304.gif" src="https://bashalog.c-brains.jp/assets_c/2021/03/fujihara_20210304-thumb-1148x716-3582.gif" width="1148" height="716" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto 20px;" /></a></p>

<h2>説明</h2>
<p>ファイルを選択したときと、送信ボタンを押した時に圧縮処理が入っています。
圧縮時のオプションですが、最大サイズを1Mにし最大縦横を1024pxにしました。
まずonChange箇所です。inputが変わった時にまず
imgタグに使用するsrcの初期化を行い、ファイルが選択されていない場合は終了。選択されている場合は
圧縮しURLを取得するとimgタグのsrcに入るようしています。
(表示された画像は圧縮後のもの）</p>

<pre>
<code>
          onChange={async (e) =&gt; {
            setImageUrl('');
            if (!e.target.files[0]) {
              return;
            }
            const img = e.target.files[0];
            try {
              const compressFile = await imageCompression(img, compressOption);
              const url = await imageCompression.getDataUrlFromFile(compressFile);
              setImageUrl(url);
            } catch (error) {
              console.log(error);
            }
          }}
</code>
</pre>

<p>次にformのsubmit時です。onChangeの時と同様に、通常ファイルを取得・圧縮して、
FormDataにアタッチしています。
(ファイルオブジェクトをステートに持ちたくないため、onChangeの時と圧縮処理が2重になっています）
今回は送信せずに圧縮前後でのファイルサイズをalertするだけになっています。
</p>

<pre>
<code>
        e.preventDefault();
        const form = new FormData(e.target);
        const postForm = new FormData();
        const file = form.get('image');
        const compressFile = await imageCompression(file ,compressOption);
        postForm.append('image', compressFile, file.name);
        alert(`default_size: ${file.size} \n compressed_size: ${compressFile.size}`);

<p>        //fetch data....<br />
</code><br />
</pre></p>

<h2>まとめ</h2>
<p>いかがでしたでしょうか、これでユーザーがスマホの場合などに送信するパケットを抑えることができます。
今回設定したオプションは最大ファイルサイズ、縦横幅（比率を保ったまま圧縮）です。
他のオプションもあるので公式サイトで確認してみてください。
</p>]]>
            </description>
         <link>https://bashalog.c-brains.jp/21/03/08-190000.php</link>
         <guid>https://bashalog.c-brains.jp/21/03/08-190000.php</guid>
         <category>JavaScript</category>
         <pubDate>Mon, 08 Mar 2021 19:00:00 +0900</pubDate>
         <author>fujihara</author>
      </item>
      
      <item>
         <title>SVG画像のHTMLでの使い方</title>
            <description>
                  <![CDATA[<p>こんにちは。koyaです。  </p>

<p>今回は<a href="https://greetings.c-brains.jp/2021_winter/">「コロナに負けない！シーブレイン的、withコロナ時代の楽しみ方」</a>でも使用したSVG画像の使い方について説明をします。</p>
]]>
            </description>
         <link>https://bashalog.c-brains.jp/21/03/01-150000.php</link>
         <guid>https://bashalog.c-brains.jp/21/03/01-150000.php</guid>
         <category>HTML&amp;CSS</category>
         <pubDate>Mon, 01 Mar 2021 15:00:00 +0900</pubDate>
         <author>koya</author>
      </item>
      
      <item>
         <title>【Gulp】gulp-renameで出力ファイルを簡単操作</title>
            <description>
                  <![CDATA[<p>どうもfujiharaです。本日はgulpの便利プラグイン gulp-rename をご紹介します。</p>

<h2>背景</h2>
<p>gulpを使う場合src, dist でソース、出力先を指定するとかと思います。 </p>
<h3>gulpfile.js</h3>
<pre>
<code>
import gulp from 'gulp';
const { src, dest } = gulp;

<p>export default () => {<br />
  return src('src/js/*.js')<br />
    .pipe(processA()) // 処理A<br />
    .pipe(processB()) // 処理B<br />
    .pipe(dest('dist/js'));<br />
}<br />
</code><br />
</pre><br />
<h3>tree (ディレクトリ抜粋)</h3><br />
<pre><br />
<code><br />
├── dist<br />
│   └── js<br />
│       └── index.js<br />
├── gulpfile.js<br />
└── src<br />
    └── js<br />
        └── index.js<br />
</code><br />
</pre></p>

<h2>gulp-rename</h2>
<p><a href="https://www.npmjs.com/package/gulp-rename" target="_blank">gulp-rename</a>をかませると以下のようになります。</p>
<pre>
<code>
import gulp from 'gulp';
import rename from 'gulp-rename';
const { src, dest } = gulp;

<p>export default () => {<br />
  return src('src/js/*.js')<br />
    .pipe(processA()) // 処理A<br />
    .pipe(processB()) // 処理B<br />
    .pipe(rename((path) => {<br />
      //path = {dirname: '.', basename: 'index', extname: '.js'} <- (上記 tree内の src/js/index.jsの場合)<br />
      return path;<br />
    }))<br />
    .pipe(dest('dist/js'));<br />
}<br />
</code><br />
</pre></p>

<p>ここで上記のpath.dirname, path.basename, path.extname を書き換えると出力を任意に変えられます。</p>
<pre>
<code>
path.dirname = '../change-dir'; //-> 出力先が dist/change-dir になります。
path.basename = 'change-name'; // -> 出力ファイルが change-name.js になります。
path.extname = '.txt'; // -> 出力ファイルが index.txt になります。
</code>
</pre>

<h2>まとめ</h2>
<p>使い所ですが、どうしても特定のファイルのみ出力時にファイル名を変えたい場合などに使用してみてください。
出力先だけを変えるなら src, dist 設定をタスク別で分けてあげれば良いんですが、1ファイルだけ対象などのときには積極的に使ってみてはいかがでしょうか？</p>]]>
            </description>
         <link>https://bashalog.c-brains.jp/21/02/25-181500.php</link>
         <guid>https://bashalog.c-brains.jp/21/02/25-181500.php</guid>
         <category>gulp</category>
         <pubDate>Thu, 25 Feb 2021 18:15:00 +0900</pubDate>
         <author>fujihara</author>
      </item>
      
      <item>
         <title>Autolinkerでテキスト内のURLなどをリンクに変換する(Javascript)</title>
            <description>
                  <![CDATA[<p>どうもfujiharaです。本日はテキストで記載れたURLなどをリンクタグに変換してくれる便利なAutolinkerを紹介します。</p>

<h2>Autolinker.js</h2>
<p>テキストエリアで入力されたURLなどを自動でリンクにするために
npmで探していたら出会いました。
<a href="https://github.com/gregjacobs/Autolinker.js" target="_blank" rel="noopener">Autolinker.js</a>
はURLだけでなく他にも色々変換してくれます
</p>

<h2>使い方</h2>
<p>以下のようにAntolinker.link('変換対象', option) で変換してくれます。</p>

<pre>
<code>
&lt;div id="before-link"&gt;
URL:  https://c-brains.jp&lt;br&gt;
Mail:  mail-check@test.mail.com&lt;br&gt;
Tel: 0120-000-9999&lt;br&gt;
Mention: @matsu_bouzu&lt;br&gt;
HashTag: #コロナ
&lt;/div&gt;
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/autolinker/3.14.2/Autolinker.min.js"&gt;&lt;/script&gt;
&lt;script&gt;
  const target = document.getElementById('before-link');
  target.innerHTML = Autolinker.link(target.innerHTML, {
    stripPrefix: true, //https, http を削除した状態にしてくれる
    mention: 'twitter', //instagramなどあり
    hashtag: 'twitter' //facebook, instagramなどあり
  });
&lt;/script&gt;
</code>
</pre>

<p>変換前</p>
<img alt="fujihara_20210129_2.png" src="https://bashalog.c-brains.jp/images/fujihara_20210129_2.png" width="364" height="182" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto 20px;" />

<p>変換後</p>
<img alt="fujihara_20200129_1.png" src="https://bashalog.c-brains.jp/images/fujihara_20200129_1.png" width="333" height="159" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto 20px;" />

<h2>まとめ</h2>
<p>デフォルトでURL,メール、電話番号をやってくれます。 mention, hashタグはユーザーによってどれか
検討がつかないので、必ず変換されることを通知しない限り使わないほうが良いかなと思います。
良かったら使ってみて下さい。
</p>
]]>
            </description>
         <link>https://bashalog.c-brains.jp/21/01/29-200000.php</link>
         <guid>https://bashalog.c-brains.jp/21/01/29-200000.php</guid>
         <category>JavaScript</category>
         <pubDate>Fri, 29 Jan 2021 20:00:00 +0900</pubDate>
         <author>fujihara</author>
      </item>
      
      <item>
         <title>429 (Too Many Requests)を回避する(Laravel 8)</title>
            <description>
                  <![CDATA[<p>どうもfujiharaです。先日ステータスコード429というのに出会いました。
調べると利用していたLaravel8が出していました、今回はこれを回避する方法をご紹介します。</p>

<h2>背景</h2>
<p>先日アップした<a href="https://bashalog.c-brains.jp/20/12/23-190000.php">分割アップロード</a>の
バックエンドの処理をLaravel8で行っていました。ローカルでは問題なく動いてたのですが本番で動作確認を行うと
ファイルが大きいときにエラーを出していました。</p>
<p>レスポンスコードを見ると、&quot;429 (Too Many Requests)&quot;とはじめはNginx側で制限とかあるのかなと
調べていたのですが、LaravelのApi Routeは1分間で60回のリクエスト制限があることがわかりました。
</p>

<h2>解決方法</h2>
<p>変更方法は本サイトの<a href="https://laravel.com/docs/8.x/routing#rate-limiting" target="_blank">Rate Limiting</a>
を参考にRouteServiceProviderに以下のようにしました。</p>

<pre>
<code>
    protected function configureRateLimiting()
    {
        //元々設定された 1分間60分
        RateLimiter::for('api', function (Request $request) {
            return Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip());
        });

<p>        //追加分<br />
        RateLimiter::for('seventy', function () {<br />
            return Limit::perMinute(70);<br />
        });<br />
    }<br />
</code><br />
</pre></p>

<p>また元々設定されているKernel.php内の $middlewareGroups 内のthrottleをコメントアウトします。</p>

<pre>
<code>
        'api' => [
            //'throttle:api',
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],
</code>
</pre>

<p>最後にapi.phpを以下のように変えました。</p>

<pre>
<code>
Route::get('/get', [CheckController::class, 'get']);
↓
Route::middleware(['throttle:seventy'])->group(function() {
    Route::get('/get', [CheckController::class, 'get']);
});

<p></code><br />
</pre></p>

<h2>結果</h2>

<p>before (70回リクエスト)</p>

<p><img alt="fujihara_20201225_01.gif" src="https://bashalog.c-brains.jp/images/fujihara_20201225_01.gif" width="1976" height="1066" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto 20px;" /></p>

<p>after (80回リクエスト)</p>

<p><img alt="fujihara_20201225_02.gif" src="https://bashalog.c-brains.jp/images/fujihara_20201225_02.gif" width="1976" height="1066" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto 20px;" /></p>

<h2>まとめ</h2>
<p>Kernel.phpの記載を削除しないと routes/api.php内のものは駄目でした。api.php とは別のファイルを作り、Kernel.phpはそのままにroutes/xxxx.php を新たに作って
別個で管理するのも手だと思います。くれぐれも開けすぎにはご注意下さい。もちろんフロントでRequest数を調整するのもありかと思います。それでは皆さん良いお年を</p>]]>
            </description>
         <link>https://bashalog.c-brains.jp/20/12/25-170000.php</link>
         <guid>https://bashalog.c-brains.jp/20/12/25-170000.php</guid>
         <category>PHP</category>
         <pubDate>Fri, 25 Dec 2020 17:00:00 +0900</pubDate>
         <author>fujihara</author>
      </item>
      
      <item>
         <title>ファイルを分割してアップロードする(Javascript, PHP)</title>
            <description>
                  <![CDATA[<p>どうもfujiharaです。本日はファイルを分割してアップロードする方法をご紹介します</p>

<h2>背景</h2>
<p>ファイルをアップロードするシステムを作るときに容量の要望にアップロードサイズ、アップロード時間の変更で対応するには
大きすぎるサイズだったので分割してアップロードして、結合するという方法にしました。
</p>

<h2>ファイル構成</h2>
<p>以下のようになります。uploadsはアップロード先になりますので作成しておいて下さい。</p>
<pre>
<code>
├── js
│   └── upload.js
├── index.html
├── upload.php
└── uploads
</code>
</pre>

<h2>HTML</h2>
<p>まずは簡易HTMLです</p>
<pre>
<code>
&lt;!DOCTYPE html&gt;
&lt;html lang="ja"&gt;
&lt;head&gt;
&lt;meta charset="UTF-8"&gt;
&lt;title&gt;Upload&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;p&gt;input size &lt;span id="input-size"&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;progress &lt;span id="progress"&gt;&lt;/span&gt;%&lt;/p&gt;
&lt;form id="form"&gt;
&lt;input id="file" type="file" name="file"/&gt;
&lt;button type="submit"&gt;アップロード&lt;/button&gt;
&lt;/form&gt;
&lt;p&gt;upload size &lt;span id="upload-size"&gt;&lt;/span&gt;&lt;/p&gt;
&lt;script src="/js/upload.js"&gt;&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</code>
</pre>

<h2>javascript</h2>
<p>サンプルなので変数名などはすっ飛ばしてもらえると</p>
<pre>
<code>
(() => {
  const form = document.getElementById('form');
  let file = null;
  let slice_size = 2 * 1024 * 1024; //切り取るサイズ 2M
  const input = form.querySelector('input');
  let size = 0;
  let count = 0;
  let postForm = null;
  let splitData = null;
  const inputSize = document.getElementById('input-size'); //インプットサイズ
  const uploadSize = document.getElementById('upload-size'); //アップロード結合サイズ
  const progress = document.getElementById('progress'); //進捗
  form.addEventListener('submit', async (e) => {
    e.preventDefault();
    file = input.files[0];
    size = file.size;
    const name = `${(new Date()).getTime()}-${file.name}`;  //アップロード時の名前を固定するように
    inputSize.innerText = size.toString(); //インプット時に計測されるファイルサイズを表示
    count = Math.ceil(size / slice_size); //分割数を計算
    for (let k = 0; k < count; k++) {  //分割アップロード実施
      splitData = file.slice(k * slice_size, (k + 1) * slice_size); //該当箇所をスライス
      postForm = new FormData(); //アップロード用フォーム
      postForm.append('file', splitData); //スライスしたファイルをフォームにセット
      postForm.append('name', name); //名前
      progress.innerText = Math.ceil(100 * (k + 1) / count).toString(); //進捗率を更新
      let result = await fetch('/upload.php', {
        body: postForm,
        method: 'POST',
        headers: { Accept: 'application/json' }
      }).then(res => res.json());
      uploadSize.innerText = result.size; //アップロード結合したサイズを表示
      await new Promise(res => setTimeout(() => {
        res();
      }, 1000)); //ゆっくり見せたいのでpost完了後、次のpostまで1000ms
    }
    return false;
  });

<p>})();<br />
</code><br />
</pre></p>

<h2>PHP</h2>
<p>PHPは以下になります(簡素)</p>
<pre>
<code>
&lt;?php

<p>$file_place = __DIR__."/uploads/{$_POST['name']}"; //結合するファイル場所</p>

<p>$file = $_FILES['file'];<br />
file_put_contents($file_place, file_get_contents($file['tmp_name']), FILE_APPEND); //FILE_APPENDで足していく</p>

<p>echo json_encode(['size' => filesize($file_place)]); //結果を出力<br />
</code><br />
</pre></p>

<h2>動作</h2>
<p>以下のようになります。PHPが入っていれば、ビルトインサーバーでも動きます。
ファイルを選択してアップロードを押すと、フロント側でのファイルサイズを表示し、アップロード毎に進捗率、アップロードされたファイルのサイズを表示します。
</p>

<p><img alt="fujihara_20201224.gif" src="https://bashalog.c-brains.jp/images/fujihara_20201224.gif" width="1976" height="1066" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto 20px;" /></p>

<p><br />
<h2>まとめ</h2><br />
<p>いかがでしたでしょうか？簡単に実装できると思いませんか？<br />
実際にはこの他に、分割アップロード時のエラー・リトライ処理・セキュリティ設定<br />
をしっかりする必要がありますのでご注意下さい。<br><br />
ライブラリだと<a href="https://github.com/23/resumable.js">Resumable.js</a>が有名ですね</p></p>]]>
            </description>
         <link>https://bashalog.c-brains.jp/20/12/23-190000.php</link>
         <guid>https://bashalog.c-brains.jp/20/12/23-190000.php</guid>
         <category>JavaScript</category>
         <pubDate>Wed, 23 Dec 2020 19:00:00 +0900</pubDate>
         <author>fujihara</author>
      </item>
      
      <item>
         <title>【Amazon Linux】【PHP5.6】EC2にLAMP環境を作る</title>
            <description>
                  <![CDATA[<p>こんにちわ。kyamashitaです。</p>

<p>EC2にPHP5.6でLAMP環境を作る必要があったのですが、ちょっとハマったので備忘録として残します。</p>
]]>
            </description>
         <link>https://bashalog.c-brains.jp/20/12/22-164751.php</link>
         <guid>https://bashalog.c-brains.jp/20/12/22-164751.php</guid>
         <category>PHP</category>
         <pubDate>Tue, 22 Dec 2020 16:47:51 +0900</pubDate>
         <author>kyamashita</author>
      </item>
      
      <item>
         <title>便利なモーダルSweetAlert2</title>
            <description>
                  <![CDATA[<p>どうもfujiharaです。
恥ずかしながら最近まで知らなかった便利なモーダルSweetAlert2をご紹介させていただきます。</p>

<h2>背景</h2>
<p>フォーム実装していて通信中にモーダル出したいと思い、npmで探していたら
出会いました。Start数(12k超:2020/12/17時点)、Weekly Downloads数共に凄い数です。
</p>

<h2>使い方</h2>
<p>いくつかご紹介します。詳しい使い方は<a href="https://sweetalert2.github.io/" target="_blank">公式サイト</a> を参照下さい。</p>

<h3>読み込み</h3>

<pre>
<code>
&lt;script src="sweetalert2/dist/sweetalert2.all.min.js"&gt;&lt;/script&gt;
&lt;script src="https://cdn.jsdelivr.net/npm/promise-polyfill@8/dist/polyfill.js"&gt;&lt;/script&gt;
</code>
</pre>

<p>もしくは npm install して importします。<br>

<p>一番簡単な開くは以下です。</p></p>

<pre>
<code>
Swal.fire('開け　モーダル');
</code>
</pre>

<p><img alt="fujiwara_20201218_01.png" src="https://bashalog.c-brains.jp/images/fujiwara_20201218_01.png" width="1626" height="772" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto 20px;" /></p>

<p>上の場合の文字はタイトルになり、タイトル、テキストの場合は2つ目の引数を設定します</p>

<pre>
<code>
Swal.fire('タイトル', 'テキストコンテンツだよ&lt;br&gt;確認してね');
</code>
</pre>

<p><img alt="fujihara_20201218_02.png" src="https://bashalog.c-brains.jp/images/fujihara_20201218_02.png" width="1590" height="562" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto 20px;" /></p>

<p>基本はそれと Swal.close()で開いてるのを閉じてもらえば良いんですが凝った事をするときには
Swal.fire(option)　を使います。</p>

<pre>
<code>
Swal.fire({
  icon: 'success',
  title: '保存しました',
});
</code>
</pre>

<p><img alt="fujihara_20201218_03.png" src="https://bashalog.c-brains.jp/images/fujihara_20201218_03.png" width="1622" height="696" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto 20px;" /></p>

<p>上記のようにiconパラメータを渡すとアイコンを表示してくれます。
iconは他にerror, info, warning, questionとあります</p>

<p>他のオプションでは以下をよく使いました。</p>

<pre>
<code>
Swal.fire({
  icon: 'warning',
  showCancelButton: true, //キャンセルボタンを表示
  showCloseButton: true, //閉じるボタンを表示
  allowEscapeKey: false, //エスケープキーでモーダルを閉じないようにする
  allowOutsideClick: false, //黒いところクリックで閉じないように
});
</code>
</pre>

<p><img alt="fujihara_20201218_04.png" src="https://bashalog.c-brains.jp/images/fujihara_20201218_04.png" width="1620" height="602" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto 20px;" /></p>

<p>次は押したボタンの状態を以下で取れます</p>

<pre>
<code>
Swal.fire({
  icon: 'warning',
  showCancelButton: true, //キャンセルボタンを表示
  showCloseButton: true, //閉じるボタンを表示
  showDenyButton: true, //NGボタンを表示
}).then((result) => {
  console.log(result);
  //result.isConfirmed, isDissmied, isDenied 
});
</code>
</pre>

<p>上記のresultで何が押されたかによって処理を変えたりできます</p>

<pre>
<code>
  Swal.fire({
    icon: 'info',
    title: '投稿中'
  });
  Swal.showLoading();
  setTimeout(() => {
    Swal.update({
      icon: 'success',
      title: '保存しました'
    });
    Swal.hideLoading();
  },1500);
</code>
</pre>

<p><img alt="fujihara_20201218_05.gif" src="https://bashalog.c-brains.jp/images/fujihara_20201218_05.gif" width="1490" height="1066" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto 20px;" /><br />
<p>setTimeの箇所を実処理に変えてもらい、Swal.updateでコンテンツを更新できます。（更新できないプロパティーもあります）</p></p>

<h2>まとめ</h2>
<p>いかがでしたでしょうか？ 詳しい箇所は本サイト見てもらればと思います。Swal.fire(), Swal.close(), Swal.update() で
だいたいのことは出来ます。　preConfirm, queueもおすすめなので挑戦してみて下さい。</p>
]]>
            </description>
         <link>https://bashalog.c-brains.jp/20/12/17-200000.php</link>
         <guid>https://bashalog.c-brains.jp/20/12/17-200000.php</guid>
         <category>JavaScript</category>
         <pubDate>Thu, 17 Dec 2020 20:00:00 +0900</pubDate>
         <author>fujihara</author>
      </item>
      
      <item>
         <title>Movable Type, JavaScript) Data API を使ってコメントをする</title>
            <description>
                  <![CDATA[<p>どうもお久しぶりですfujiharaです。弊社は最近引っ越しをしたのですが、最寄り駅が日本大通りという駅になりました。
名前の由来とかは全くわからないのですが、大それた名前じゃんと毎回思っています。<br>

<p>本日はMovable Type Data APIでコメントをする方法をお伝えします。</p></p>

<h2>背景</h2>
<p>フロントからコメントをする場合にはMTの通常機能でもできるのですが、コメントのカスタムフィールドも更新したかったため
Data APIを使用しました。</p>

<h2>仕様</h2>
<p><a href="https://www.movabletype.jp/developers/data-api/v4-reference.html#comments-comments-collection-by-entry" target="_blank">こちら</a>になります。
前回ご紹介した投稿時と基本は同じです。</p>

<h2>コード</h2>
<pre>
<code>
//記事基本データ
const postComment = {
  body: 'body',
  customFields: []
 };

<p>//カスタムフィールド設定<br />
postComment.customFields.push({<br />
   'basename': '{カスタムフィールドの値}',<br />
   'value': '{カスタムフィールドに入れる値}'<br />
});</p>

<p>//記事作成処理<br />
const body = new FormData();<br />
body.append('comment', JSON.stringify(postComment));<br />
// コメントする記事のsite_id, entry_id が必要になります<br />
// 例:  /mt/mt-data-api.cgi/v4/sites/10/entries/101/comments<br />
const result = fetch({path_to_data_api/sites/{site_id}/entries/{entry_id}/comments}, {<br />
  method: 'post',<br />
  body: body,<br />
  headers: {<br />
    'X-MT-Authorization': `MTAuth accessToken={DataAPIアクセストークン}`<br />
  }<br />
})<br />
  .then(res => res.json())<br />
  .then(json => !json.error);<br />
</code><br />
</pre></p>

<h2>まとめ</h2>
<p>これでコメントをData APIで保存することができました。<br>
一点注意ですが、このコメントを書き込むユーザーに権限を与えすぎないように注意下さい。コメントさえ書き込められば良いので、
システムでのユーザー作成には以下で（Data APIでのサインインのみ）</p>

<p><img alt="20201030_fujiwara_2.png" src="https://bashalog.c-brains.jp/images/20201030_fujiwara_2.png" width="1569" height="182" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto 20px;" /></p>

<p>またブログでのユーザー権限はコメント投稿者のみにしておくことにご注意下さい。</p>

<p><img alt="20201030_fujiwara_1.png" src="https://bashalog.c-brains.jp/images/20201030_fujiwara_1.png" width="797" height="678" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto 20px;" /></p>]]>
            </description>
         <link>https://bashalog.c-brains.jp/20/10/30-200000.php</link>
         <guid>https://bashalog.c-brains.jp/20/10/30-200000.php</guid>
         <category>JavaScript</category>
         <pubDate>Fri, 30 Oct 2020 20:00:00 +0900</pubDate>
         <author>fujihara</author>
      </item>
      
      <item>
         <title>【PowerCMS】【MTPager】全〇件中〇～〇件表示のページング</title>
            <description>
                  <![CDATA[<p>こんにちわ。</p>

<p>kyamashitaです。</p>

<p>PowerCMSのMTPagerを使ってページングを実装したのでご紹介します。</p>]]>
            </description>
         <link>https://bashalog.c-brains.jp/20/10/30-173658.php</link>
         <guid>https://bashalog.c-brains.jp/20/10/30-173658.php</guid>
         <category>MovableType</category>
         <pubDate>Fri, 30 Oct 2020 17:36:58 +0900</pubDate>
         <author>kyamashita</author>
      </item>
      
      <item>
         <title>【Photoshop】CC 2021の新機能「空を置き換え」を試してみる</title>
            <description>
                  <![CDATA[<p>こんにちは。お久しぶりです。sitoです。 <br />
10月20日から21日に、Adobe MAX 2020がオンライン開催されましたね。恥ずかしながら当日の朝に開催を知り、慌てて参加登録をしました。</p>

<p>さて、今回は、そこで発表されたPhotoshopの新機能「空を置き換え」が楽しそうだったので試して見たいと思います。</p>

<h2>「空を置き換え」の使い方</h2>

<p><img alt="201002.gif" src="https://bashalog.c-brains.jp/images/201002.gif" width="1302" height="756" class="mt-image-none" style="" /></p>

<h3>編集→空を置き換えを選択</h3>

<p><img alt="201003.png" src="https://bashalog.c-brains.jp/images/201003.png" width="1299" height="799" class="mt-image-none" style="" /></p>

<p>空を置き換えの属性ボックスが表示されます。</p>

<h3>空を置き換えの属性ボックスで、使いたい空を選択。</h3>

<p><img alt="201004.png" src="https://bashalog.c-brains.jp/images/201004.png" width="1299" height="773" class="mt-image-none" style="" /></p>

<p>「空」の項目にある矢印マークをクリックすると、登録されている空の画像が表示されます。 <br />
置き換えたい空の画像を選択します。</p>

<p><img alt="201005.png" src="https://bashalog.c-brains.jp/images/201005.png" width="1301" height="774" class="mt-image-none" style="" /></p>

<h3>設定を調整してなじませる</h3>

<p><img alt="201006.png" src="https://bashalog.c-brains.jp/images/201006.png" width="1302" height="776" class="mt-image-none" style="" /></p>

<p>左上の移動ツールでは適用した空の画像の位置を調整できます。 <br />
空ブラシツールは、置き換える空の範囲を編集できます。 <br />
手のひらツール、ズームツールは作業中のキャンバスの大きさ・表示位置の調整に使います。</p>

<p><img alt="201007.gif" src="https://bashalog.c-brains.jp/images/201007.gif" width="1302" height="756" class="mt-image-none" style="" /></p>

<p>その他の各設定の説明はこちらのページを参照ください。 <br />
<a href="https://blogs.adobe.com/japan/cc-photo-photoshop-the-worlds-most-advanced-ai-application-for-creatives/">https://blogs.adobe.com/japan/cc-photo-photoshop-the-worlds-most-advanced-ai-application-for-creatives/</a></p>

<p>上記ページから引用すると、以下のような機能になります。</p>

<blockquote>
  <p><strong>エッジをシフト</strong>：空と元の画像との境界の開始位置を指定します。 <br />
<strong>エッジをフェード</strong>：エッジに沿って空画像から元の写真へのフェードまたはぼかしの度合いを設定します。 <br />
<strong>明るさ</strong>：空の明るさを調整します。 <br />
<strong>色温度</strong>：色温度を暖色寄りまたは寒色寄りに調整します。 <br />
<strong>拡大・縮小</strong>：空の画像のサイズを変更します。 <br />
<strong>反転</strong>：空の画像を水平方向に反転します。 <br />
<strong>照明モード</strong>：調整に使用する描画モードを指定します。 <br />
<strong>照明の調整</strong>：空とブレンドするメイン画像を明るくしたり暗くしたりする不透明度スライダーです。0 に設定すると、調整は行われません。 <br />
<strong>カラー調整</strong>：前景と空のカラーの調和の強さを指定する不透明度スライダーです。0 に設定すると、調整は行われません。 <br />
<strong>出力</strong>：画像に対する変更を新規レイヤー（マスクを含む名前付きレイヤーグループ）またはレイヤーを複製（単一の統合されたレイヤー）のどちらに配置するかを選択できます。  </p>
</blockquote>

<p>空との境界線に違和感があるときは、エッジのシフト・フェードを調整してみるとよいと思います。 <br />
前景（空以外の部分）の照明やカラーを調整すると空ときれいになじんできます。</p>

<h3>出力方法を選択して「OK」をクリック</h3>

<p><img alt="201008.png" src="https://bashalog.c-brains.jp/images/201008.png" width="1300" height="776" class="mt-image-none" style="" /></p>

<p>出力方法には「新規レイヤー」と、「レイヤーを複製」の２種類があります。 <br />
「新規レイヤー」の方が後からマスクや色味の調整ができるので、通常はこちらの設定にしておくのがよさそうです。</p>

<p><img alt="201009.png" src="https://bashalog.c-brains.jp/images/201009.png" width="1133" height="447" class="mt-image-none" style="" /></p>

<p>こんな感じで簡単に空を置き換えることができました。</p>

<p><img alt="201010.jpg" src="https://bashalog.c-brains.jp/images/201010.jpg" width="1200" height="800" class="mt-image-none" style="" /></p>

<h2>新しい空の画像を追加して使用する</h2>

<p>自分で新しく空の画像を追加して使用することもできます。</p>

<p><img alt="201011.png" src="https://bashalog.c-brains.jp/images/201011.png" width="564" height="710" class="mt-image-none" style="" /></p>

<p>空を選択するウィンドウの右下にある「＋」マークをクリックして、追加したい空を選択。 <br />
これで新しい空の画像が追加されます。</p>

<p><img alt="201012.png" src="https://bashalog.c-brains.jp/images/201012.png" width="1299" height="835" class="mt-image-none" style="" /></p>

<p>あとは先程同様に、色味などを調整して完成です。</p>

<p><img alt="201013.jpg" src="https://bashalog.c-brains.jp/images/201013.jpg" width="1200" height="800" class="mt-image-none" style="" /></p>

<p>好きな画像をつかえるので、こんな感じのおもしろ画像もつくれちゃいます！</p>

<p><img alt="201014.jpg" src="https://bashalog.c-brains.jp/images/201014.jpg" width="1200" height="800" class="mt-image-none" style="" /></p>

<p>新機能の空を置き換え、便利な上に、工夫次第でいろいろな使い方ができそうですね！</p>

<p>参考： <br />
<a href="https://helpx.adobe.com/jp/photoshop/using/replace-sky.htm">https://helpx.adobe.com/jp/photoshop/using/replace-sky.htm</a>
<a href="https://blogs.adobe.com/japan/cc-photo-photoshop-the-worlds-most-advanced-ai-application-for-creatives/l">https://blogs.adobe.com/japan/cc-photo-photoshop-the-worlds-most-advanced-ai-application-for-creatives/l</a></p>
]]>
            </description>
         <link>https://bashalog.c-brains.jp/20/10/30-142939.php</link>
         <guid>https://bashalog.c-brains.jp/20/10/30-142939.php</guid>
         <category>Photoshop</category>
         <pubDate>Fri, 30 Oct 2020 14:29:39 +0900</pubDate>
         <author>sito</author>
      </item>
      
      <item>
         <title>【WordPress】一部のページをテーマテンプレートから切り離して実装する</title>
            <description>
                  <![CDATA[<p>みなさんこんにちは、yamashitaです。<br>
日中は窓を開けてると気持ちいいですが、日が落ちると寒くなってきましたね。<br>
気温の上下に振り回されてる日々を送っています。<br>
<br>
今回はWordPressの一部ページをテーマから切り離した状態で表示したい時の処理について紹介します。<br>
あまり使用することもないかもしれませんが、特別な案件で、テーマ内の既存のテンプレートファイルを編集できないとのことで、この方法を使用したので手順等を記したいと思います。</p>
]]>
            </description>
         <link>https://bashalog.c-brains.jp/20/10/02-110000.php</link>
         <guid>https://bashalog.c-brains.jp/20/10/02-110000.php</guid>
         <category>WordPress</category>
         <pubDate>Fri, 02 Oct 2020 11:00:00 +0900</pubDate>
         <author>yamashita</author>
      </item>
      
      <item>
         <title>【WordPress】ブロックパターンおすすめのプラグイン</title>
            <description>
                  <![CDATA[<p>こんにちは。koyaです。 <br />
テレワークを機に自宅を引っ越しました。 <br />
自宅にいる時間が長くなると広い部屋が欲しくなりますよね。通勤時間を気にしなくていいので物件探しも幅が広くてよかったです。 <br />
OculusQuestやSwitchを所持していても狭い部屋だとやれることが限られていたのでとても快適です。</p>
]]>
            </description>
         <link>https://bashalog.c-brains.jp/20/09/18-170000.php</link>
         <guid>https://bashalog.c-brains.jp/20/09/18-170000.php</guid>
         <category>WordPress</category>
         <pubDate>Fri, 18 Sep 2020 17:00:00 +0900</pubDate>
         <author>koya</author>
      </item>
      
      <item>
         <title>【three.js】【parcel】【dat.GUI】環境を作る</title>
            <description>
                  <![CDATA[<p>こんにちは。kyamashitaです。</p>

<p>前回は three.js の example を修正して海を表示してみました。</p>

<p>今回は環境を作ってみましたので、ご紹介しようと思います。</p>
]]>
            </description>
         <link>https://bashalog.c-brains.jp/20/08/31-184809.php</link>
         <guid>https://bashalog.c-brains.jp/20/08/31-184809.php</guid>
         <category>JavaScript</category>
         <pubDate>Mon, 31 Aug 2020 18:48:09 +0900</pubDate>
         <author>kyamashita</author>
      </item>
      
      <item>
         <title>Movable Type, JavaScript) Data API で記事のカスタムフィールドにアセットを紐付ける</title>
            <description>
                  <![CDATA[<p>どうもfujiharaです。リモート勤務になりめっきり平日に歩かなくなりました。（1日外出しないことも多々あり）</p>
<p>本日はMovable TypeのData APIを使って記事を操作する時に、カスタムフィールドに画像アセットがある場合の登録方法をご紹介いたします。</p>

<h2>はじめに</h2>
<p>Data APIを使うと管理画面にログインせずに各種データの操作を行えます。<br>
<a href="https://www.movabletype.jp/developers/data-api/getting-started/">詳細</a><br><br>

<h2>説明</h2>
<p>本来だと以下の流れになります、今回は記事の登録箇所のみ説明いたします。(3)</p>
<ol>
<li>ユーザー認証(2,3の際のリクエスト時に取得したアクセストークンを使用)</li>
<li>画像を登録</li>
<li>登録した画像のデータを付与して記事登録</li>
</ol>

<h2>コード</h2>

<pre>
<code>
//記事基本データ
const postEntry = {
  title: 'title',
  body: 'body',
  excerpt: '',
  status: 'Publish',
  allowComments: true,
  keywords: '',
  allowTrackbacks: false,
  basename: 'basename',
  customFields: []
 };

<p>//カスタムフィールド設定<br />
postEntry.customFields.push({<br />
   'basename': 'cf_image',<br />
   'value': sanitizeHtml(`&lt;form mt:asset-id='{アセットID}' class='mt-enclosure mt-enclosure-{アセットクラス}' style='display: inline-block;'&gt;&lt;a href='{アセットURL}'&gt;{アセットファイル名}&lt;/a&gt;&lt;/form&gt;`, {<br />
     allowedTags: [ 'form', 'a' ],<br />
     allowedAttributes: {<br />
       a: [ 'href' ],<br />
       form: ['mt:asset-id', 'class', 'style']<br />
     }<br />
   })<br />
});</p>

<p>//記事作成処理<br />
const body = new FormData();<br />
body.append('entry', JSON.stringify(postEntry);<br />
fetch({DATA API 記事作成エントリーポイント}, {<br />
  method: 'post',<br />
  ////修正しました。(20200918)<br />
  //body: 'entry=' + JSON.stringify(postEntry),<br />
  body: body,<br />
  headers: {<br />
    //修正しました。(20200918)<br />
    //'Content-Type': 'application/x-www-form-urlencoded',<br />
    'X-MT-Authorization': `MTAuth accessToken={1で取得したアクセストークン}`<br />
  }<br />
})<br />
  .then(res => res.json())<br />
  .then(json => !json.error);<br />
</code><br />
</pre></p>

<h2>説明</h2>
<p>ポイントはカスタムフィールド設定です。(投稿基本データ, 投稿作成処理は説明省きます)。
カスタムフィールドの登録は配列でbasename、value のオブジェクトをカスタムフィールド分追加して登録します。<br>
<a href="https://www.movabletype.jp/developers/data-api/v4-reference.html#entries-entry-collection-post" target="_blank">開発者サイト</a><br>
<a href="https://github.com/movabletype/Documentation/wiki/data-api-resource-entries" target="_blank">github</a><br>
<br>
問題は設定する値です。MovableTypeの管理画面の記事のカスタムフィールドhiddenフィールドを見るとカスタムフィールドに紐付いた画像は
記事自体に登録されているのでなく、アセットと紐付いているだけなので紐付ける用の値を指定してあげます。
実際には以下のようなものになります。</p>

<pre>
<code>
&lt;form mt:asset-id="{asset_id}" class="mt-enclosure mt-enclosure-{asset_class}" style="display:inline-block"&gt;&lt;a href="{asset_url}"&gt;{asset_filename}&lt;/a&gt;&lt;/form&gt;
</code>
</pre>

<p>ここで各 asset_id, asset_class, asset_url, asset_filename は画像の登録時もしくは画像データ取得すると返される値を使用します。<br>
<a href="https://www.movabletype.jp/developers/data-api/v4-reference.html#assets-upload-post" target="_blank">参考</a>
<p>data.id, data.class, data.url, data.filename</p>

<p>これでカスタムフィールドと画像紐付け用の値ができました。<br>
しかしこれだけだとData API側からエラーが返され登録ができません(status code 500)。さらに作成した値をサニタイズしてあげる必要があります。<br>
今回は<a href="https://www.npmjs.com/package/sanitize-html" target="_blank">sanitize-html</a>を使用しました。
使用しているタグとプロパティーを許可するため以下のように設定しています。</p>

<pre>
<code>
sanitizeHtml({変換前の文字}, {
  allowedTags: [ 'form', 'a' ],
    allowedAttributes: {
      a: [ 'href' ],
      form: ['mt:asset-id', 'class', 'style']
    }
});
</code>
</pre>

<h2>まとめ</h2>
<p>いかがでしたでしょうか？みなさんも素敵なData API生活をお送りください。<br>
参考</p>

<ul>
<li><a href="https://www.movabletype.jp/developers/data-api/v4-reference.html#assets-upload-post">Data APIドキュメント</a></li>
<li><a href="https://gist.github.com/shinchit/ca2fe2b7e3a507d5c3ea">画像タイプのカスタムフィールドを持つエントリーをData APIを使って投稿する</a></li>
<li><a href="https://www.npmjs.com/package/sanitize-html">sanitize-html</a></li>
</ul>]]>
            </description>
         <link>https://bashalog.c-brains.jp/20/08/31-133000.php</link>
         <guid>https://bashalog.c-brains.jp/20/08/31-133000.php</guid>
         <category>MovableType</category>
         <pubDate>Mon, 31 Aug 2020 13:30:00 +0900</pubDate>
         <author>fujihara</author>
      </item>
      
   </channel>
</rss>
