[{"data":1,"prerenderedAt":10061},["ShallowReactive",2],{"tag-php-1":3},{"count":4,"content":5},16,[6,2344,3992,4474,4950,5448,5533,5843,8170,8645],{"id":7,"title":8,"body":9,"category":2331,"createdAt":2333,"description":8,"extension":2334,"index":2335,"meta":2336,"navigation":231,"path":2337,"publish":231,"seo":2338,"series":2335,"seriesTitle":2335,"stem":2339,"tag":2340,"thumbnail":2342,"updatedAt":2333,"__hash__":2343},"articles\u002Farticles\u002Fmuch-post-migration-wp.md","別CMSで作成された4万件分の大量投稿をwordpressに引越しする",{"type":10,"value":11,"toc":2313},"minimark",[12,16,23,26,31,34,41,54,57,65,71,81,84,87,98,101,104,107,122,125,128,135,138,141,169,172,183,189,192,195,489,492,498,501,504,508,515,521,527,537,574,580,583,586,589,710,713,716,810,829,869,872,878,947,950,1069,1075,1078,1081,1084,1087,1093,1096,1102,1109,1115,1118,1122,1125,1128,1262,1268,1553,1560,1611,1614,1665,1675,1678,1681,1808,1822,1825,1828,1831,1978,1986,2216,2223,2237,2248,2260,2263,2267,2270,2276,2283,2286,2292,2295,2306,2309],[13,14,15],"p",{},"こんにちはjunです。会社でとてつもない量のCMSのデータをwordpressに移行する計画がありました。色々と課題がある中なんとかwordpressにデータをマイグレーションして再構築できたので共有したいと思います。",[13,17,18,19],{},"データが４万件もあるのでプログラム的にwordpressを操作する必要がありました。日本語で検索してもなかなかヒットしなかったので、少し苦労。 ",[20,21,22],"strong",{},"しかしCLIがわかって、使う関数を把握すれば意外と簡単です。",[13,24,25],{},"引越しの背景から説明するので、「ささっと移行手順を見せろや！」という人は「wordpress側の構築概要」から見てください。",[27,28,30],"h2",{"id":29},"背景データ移行の課題","背景＆データ移行の課題",[13,32,33],{},"2006年に構築されたサイトを弊社が受け持っており、その環境が古くなってきたので移行することになりました。PHP5、centos6というレガシーな環境で動いており非常に危なっかしい上に、ろくに保守もされてないのでページングとか表示もおかしい部分も出てきました。",[13,35,36,37,40],{},"移行するサイトはwordpressではない ",[20,38,39],{},"別のCMSで構築されており、移行の際にはDBからデータを一回ダンプして加工してwordpressにマイグレーションをする必要がありました。"," しかしそのデータは",[42,43,44,48,51],"ul",{},[45,46,47],"li",{},"投稿４万件",[45,49,50],{},"ユーザー8200人",[45,52,53],{},"カテゴリー90件",[13,55,56],{},"というデータが膨大であり必然的にプログラム的にデータを移行する必要があります。とりあえず担当の方と移行するデータを精査しました。元々はコミュニティサイトとして使用していたのでユーザーが非常に多く、移行すべきアクティブなユーザーは100人程度だったのでユーザーはかなり減りました。しかし投稿は全部移行でした（泣",[13,58,59,60,64],{},"旧CMSでの「カテゴリー」はブログの種類に当てはまりました。ブログを管理、投稿する部署が異なっていることが判明し、投稿データにも ",[61,62,63],"code",{},"categoruid"," の様に区別するカラムがありました。さらに言えばユーザー情報にも紐づいています笑。",[13,66,67,70],{},[20,68,69],{},"投稿データは旧CMSで結び付けられたユーザーID、カテゴリーIDの関係性を維持しながら移行する必要があります。"," さらに投稿データには",[72,73,78],"pre",{"className":74,"code":76,"language":77},[75],"language-text","[img]http:\u002F\u002F~~~~~[\u002Fimg]\n","text",[61,79,76],{"__ignoreMap":80},"",[13,82,83],{},"という様なそのCMS独自のタグが存在したので、wordpressに移行する前に正規表現で置換する必要がありました。（今回はその解説はしません。別途の記事で）",[13,85,86],{},"まとめると",[42,88,89,92,95],{},[45,90,91],{},"データ数が膨大。",[45,93,94],{},"記事はカテゴリー、ユーザーとの関係性を維持する。",[45,96,97],{},"記事データの独自記法をwordpress用に置換または削除する。",[13,99,100],{},"という課題がありました。",[27,102,103],{"id":103},"wordpress側の構築概要",[13,105,106],{},"今回の移行手順としては前準備に",[108,109,110,113,116,119],"ol",{},[45,111,112],{},"旧CMSからデータをダンプ(mysql）してローカルに入れておく。",[45,114,115],{},"データ構成をよく観察する。",[45,117,118],{},"必要なデータをSQLを用いて取得、JSONで取得",[45,120,121],{},"JSONを元にPHPスクリプトでデータを加工",[13,123,124],{},"この様にデータの加工をしてwordpressに入れ込む準備をしました。加工済みデータJSONとwordpressの関数を用いてこれらのデータをwordpressに移行しました。",[13,126,127],{},"移行の特に厄介だったのが旧CMSでは部署ごとにカテゴリーという名前でブログ種が分けられていたことです。wordpressのカテゴリーとは別の概念です。さらに管理ユーザーもそのカテゴリーで区別されていました。",[13,129,130,131,134],{},"そこで今回は ",[20,132,133],{},"wordpressをマルチサイト構成にして構築しました。"," wordpressには一つのwordpressシステムを用いて複数の異なるブログを構築する機能があります。詳しくはこちらの公式を参照。マルチサイトにすることで複数のブログに分け、さらにそのブログごとにユーザーの割当が可能になります。",[27,136,137],{"id":137},"引越し手順",[13,139,140],{},"手順としては以下の通りです。",[108,142,143,145,147,149,151,154,157,160,163,166],{},[45,144,112],{},[45,146,115],{},[45,148,118],{},[45,150,121],{},[45,152,153],{},"wordpressプロジェクト内に上記のデータを移行、挿入用PHPスクリプトを作成",[45,155,156],{},"マルチサイト 構成をONにしてブログネットワークを機械的に作成",[45,158,159],{},"ユーザーを作成して適切なブログネットワークに割り当てる。",[45,161,162],{},"投稿データを対応するユーザーとブログネットワークに割り当てる。",[45,164,165],{},"画像などを移行する。（今回はやりません）",[45,167,168],{},"全てのブログネットワークに共通のテーマを設定する。",[27,170,171],{"id":171},"dockerで検証環境を構築",[13,173,174,175,182],{},"まずはローカルでの検証環境を作りましょう。失敗するとDBが結構汚れるのですぐにリセットできるdockerを用います。",[176,177,181],"a",{"href":178,"rel":179},"https:\u002F\u002Fhub.docker.com\u002F_\u002Fwordpress",[180],"nofollow","wordpress公式のdockerHub","の通りにすれば簡単に構築できます。ディレクトリ構成は以下の通りです。",[72,184,187],{"className":185,"code":186,"language":77},[75],"docker-wordpress\u002F\n|\n|-scrips\u002F\n|-docker-compose.yml\n",[61,188,186],{"__ignoreMap":80},[13,190,191],{},"scripts\u002F にはwordpressに挿入するためのPHPスクリプトを入れておくためのディレクトリです。最終的にこのwordpress dockerコンテナの中に入って、このスクリプトをコマンドで実行します。",[13,193,194],{},"docker-compose.yml は以下の通りです。（ほとんど公式と同じ。一部改修",[72,196,201],{"className":197,"code":198,"filename":199,"language":200,"meta":80,"style":80},"language-yml shiki shiki-themes material-theme-ocean","version: '3.1'\n\nservices:\n\n  wordpress:\n    image: wordpress\n    restart: always\n    ports:\n      - 8080:80\n    environment:\n      WORDPRESS_DB_HOST: db\n      WORDPRESS_DB_USER: exampleuser\n      WORDPRESS_DB_PASSWORD: examplepass\n      WORDPRESS_DB_NAME: exampledb\n    volumes:\n      - .\u002Fscripts:\u002Fvar\u002Fwww\u002Fhtml\u002Fscripts\n\n  db:\n    image: mysql:5.7\n    restart: always\n    environment:\n      MYSQL_DATABASE: exampledb\n      MYSQL_USER: exampleuser\n      MYSQL_PASSWORD: examplepass\n      MYSQL_RANDOM_ROOT_PASSWORD: '1'\n    volumes:\n      - db_wp:\u002Fvar\u002Flib\u002Fmysql\n\nvolumes:\n  .\u002Fscripts:\n  db_wp:\n","docker-compose.yml","yml",[61,202,203,226,233,242,247,255,266,277,285,294,302,313,324,335,346,354,361,366,374,384,393,400,410,420,430,445,452,460,465,473,481],{"__ignoreMap":80},[204,205,208,212,216,219,223],"span",{"class":206,"line":207},"line",1,[204,209,211],{"class":210},"s-wAU","version",[204,213,215],{"class":214},"sAklC",":",[204,217,218],{"class":214}," '",[204,220,222],{"class":221},"sfyAc","3.1",[204,224,225],{"class":214},"'\n",[204,227,229],{"class":206,"line":228},2,[204,230,232],{"emptyLinePlaceholder":231},true,"\n",[204,234,236,239],{"class":206,"line":235},3,[204,237,238],{"class":210},"services",[204,240,241],{"class":214},":\n",[204,243,245],{"class":206,"line":244},4,[204,246,232],{"emptyLinePlaceholder":231},[204,248,250,253],{"class":206,"line":249},5,[204,251,252],{"class":210},"  wordpress",[204,254,241],{"class":214},[204,256,258,261,263],{"class":206,"line":257},6,[204,259,260],{"class":210},"    image",[204,262,215],{"class":214},[204,264,265],{"class":221}," wordpress\n",[204,267,269,272,274],{"class":206,"line":268},7,[204,270,271],{"class":210},"    restart",[204,273,215],{"class":214},[204,275,276],{"class":221}," always\n",[204,278,280,283],{"class":206,"line":279},8,[204,281,282],{"class":210},"    ports",[204,284,241],{"class":214},[204,286,288,291],{"class":206,"line":287},9,[204,289,290],{"class":214},"      -",[204,292,293],{"class":221}," 8080:80\n",[204,295,297,300],{"class":206,"line":296},10,[204,298,299],{"class":210},"    environment",[204,301,241],{"class":214},[204,303,305,308,310],{"class":206,"line":304},11,[204,306,307],{"class":210},"      WORDPRESS_DB_HOST",[204,309,215],{"class":214},[204,311,312],{"class":221}," db\n",[204,314,316,319,321],{"class":206,"line":315},12,[204,317,318],{"class":210},"      WORDPRESS_DB_USER",[204,320,215],{"class":214},[204,322,323],{"class":221}," exampleuser\n",[204,325,327,330,332],{"class":206,"line":326},13,[204,328,329],{"class":210},"      WORDPRESS_DB_PASSWORD",[204,331,215],{"class":214},[204,333,334],{"class":221}," examplepass\n",[204,336,338,341,343],{"class":206,"line":337},14,[204,339,340],{"class":210},"      WORDPRESS_DB_NAME",[204,342,215],{"class":214},[204,344,345],{"class":221}," exampledb\n",[204,347,349,352],{"class":206,"line":348},15,[204,350,351],{"class":210},"    volumes",[204,353,241],{"class":214},[204,355,356,358],{"class":206,"line":4},[204,357,290],{"class":214},[204,359,360],{"class":221}," .\u002Fscripts:\u002Fvar\u002Fwww\u002Fhtml\u002Fscripts\n",[204,362,364],{"class":206,"line":363},17,[204,365,232],{"emptyLinePlaceholder":231},[204,367,369,372],{"class":206,"line":368},18,[204,370,371],{"class":210},"  db",[204,373,241],{"class":214},[204,375,377,379,381],{"class":206,"line":376},19,[204,378,260],{"class":210},[204,380,215],{"class":214},[204,382,383],{"class":221}," mysql:5.7\n",[204,385,387,389,391],{"class":206,"line":386},20,[204,388,271],{"class":210},[204,390,215],{"class":214},[204,392,276],{"class":221},[204,394,396,398],{"class":206,"line":395},21,[204,397,299],{"class":210},[204,399,241],{"class":214},[204,401,403,406,408],{"class":206,"line":402},22,[204,404,405],{"class":210},"      MYSQL_DATABASE",[204,407,215],{"class":214},[204,409,345],{"class":221},[204,411,413,416,418],{"class":206,"line":412},23,[204,414,415],{"class":210},"      MYSQL_USER",[204,417,215],{"class":214},[204,419,323],{"class":221},[204,421,423,426,428],{"class":206,"line":422},24,[204,424,425],{"class":210},"      MYSQL_PASSWORD",[204,427,215],{"class":214},[204,429,334],{"class":221},[204,431,433,436,438,440,443],{"class":206,"line":432},25,[204,434,435],{"class":210},"      MYSQL_RANDOM_ROOT_PASSWORD",[204,437,215],{"class":214},[204,439,218],{"class":214},[204,441,442],{"class":221},"1",[204,444,225],{"class":214},[204,446,448,450],{"class":206,"line":447},26,[204,449,351],{"class":210},[204,451,241],{"class":214},[204,453,455,457],{"class":206,"line":454},27,[204,456,290],{"class":214},[204,458,459],{"class":221}," db_wp:\u002Fvar\u002Flib\u002Fmysql\n",[204,461,463],{"class":206,"line":462},28,[204,464,232],{"emptyLinePlaceholder":231},[204,466,468,471],{"class":206,"line":467},29,[204,469,470],{"class":210},"volumes",[204,472,241],{"class":214},[204,474,476,479],{"class":206,"line":475},30,[204,477,478],{"class":210},"  .\u002Fscripts",[204,480,241],{"class":214},[204,482,484,487],{"class":206,"line":483},31,[204,485,486],{"class":210},"  db_wp",[204,488,241],{"class":214},[13,490,491],{},"DBは永続化して、そしてスクリプトもローカルで編集してすぐに実行できる様にボリュームにマウントしておきます。これで準備完了です。",[72,493,496],{"className":494,"code":495,"language":77},[75],"jun@MacBook-Pro docker-wordpress % docker-compose up -d\n",[61,497,495],{"__ignoreMap":80},[13,499,500],{},"ブラウザを開いてlocalhost:8080にアクセスするとwordpressのインストール画面が開きます。DBの設定などは済んでいるので、初期ユーザーの設定だけで終わります。",[27,502,503],{"id":503},"マルチサイトを機械的に作成",[505,506,507],"h3",{"id":507},"マルチサイトの有効化",[13,509,510,511,514],{},"それではまず旧CMSのカテゴリーにあたる、マルチサイトを機械的に作成しましょう。その前に",[61,512,513],{},"wp-config.php","でやることがあります。以下の様なコードを追記してマルチサイト化を有効にします。",[72,516,519],{"className":517,"code":518,"language":77},[75],"define('WP_ALLOW_MULTISITE', true);\n",[61,520,518],{"__ignoreMap":80},[522,523],"image-render",{":src":524,":width":525,":center":526},"'_mix\u002Fwpml-197x300.png'","'300px'","true",[13,528,529,530,532,533,536],{},"有効にすると「ツール」から「サイトネットワークの設置」というメニューが出現します。これをクリックしてサイトネットワークの設定を行います。そして新しくコードを追記しろと言われるので以下の様に",[61,531,513],{}," と ",[61,534,535],{},".htaccess","に追記します。",[72,538,542],{"className":539,"code":540,"filename":513,"language":541,"meta":80,"style":80},"language-php shiki shiki-themes material-theme-ocean","define('MULTISITE', true);\ndefine('SUBDOMAIN_INSTALL', false);\ndefine('DOMAIN_CURRENT_SITE', 'localhost');\ndefine('PATH_CURRENT_SITE', '\u002F');\ndefine('SITE_ID_CURRENT_SITE', 1);\ndefine('BLOG_ID_CURRENT_SITE', 1);\n","php",[61,543,544,549,554,559,564,569],{"__ignoreMap":80},[204,545,546],{"class":206,"line":207},[204,547,548],{},"define('MULTISITE', true);\n",[204,550,551],{"class":206,"line":228},[204,552,553],{},"define('SUBDOMAIN_INSTALL', false);\n",[204,555,556],{"class":206,"line":235},[204,557,558],{},"define('DOMAIN_CURRENT_SITE', 'localhost');\n",[204,560,561],{"class":206,"line":244},[204,562,563],{},"define('PATH_CURRENT_SITE', '\u002F');\n",[204,565,566],{"class":206,"line":249},[204,567,568],{},"define('SITE_ID_CURRENT_SITE', 1);\n",[204,570,571],{"class":206,"line":257},[204,572,573],{},"define('BLOG_ID_CURRENT_SITE', 1);\n",[72,575,578],{"className":576,"code":577,"filename":535,"language":77,"meta":80},[75],"RewriteEngine On\nRewriteBase \u002F\nRewriteRule ^index\\.php$ - [L]\n\n# add a trailing slash to \u002Fwp-admin\nRewriteRule ^([_0-9a-zA-Z-]+\u002F)?wp-admin$ $1wp-admin\u002F [R=301,L]\n\nRewriteCond %{REQUEST_FILENAME} -f [OR]\nRewriteCond %{REQUEST_FILENAME} -d\nRewriteRule ^ - [L]\nRewriteRule ^([_0-9a-zA-Z-]+\u002F)?(wp-(content|admin|includes).*) $2 [L]\nRewriteRule ^([_0-9a-zA-Z-]+\u002F)?(.*\\.php)$ $2 [L]\nRewriteRule . index.php [L]\n",[61,579,577],{"__ignoreMap":80},[13,581,582],{},"なお、今回はドメイン別ではなくサブディレクトリ形式のマルチサイト とします。",[505,584,585],{"id":585},"サイト作成プログラム",[13,587,588],{},"移行元のデータはすでにJSONにしてあります。以下の様な構成とします。",[72,590,595],{"className":591,"code":592,"filename":593,"language":594,"meta":80,"style":80},"language-json shiki shiki-themes material-theme-ocean","[\n  ...\n  {\n    \"id\":\"6\",\n    \"name\":\"サイトの名前\",\n    \"sub_title\":\"サイトのキャッチフレーズ的なもの\",\n    \"mailUser\":\"3::1543\"\n  },\n  ...\n]\n","article_category.json","json",[61,596,597,602,608,613,637,657,677,696,701,705],{"__ignoreMap":80},[204,598,599],{"class":206,"line":207},[204,600,601],{"class":214},"[\n",[204,603,604],{"class":206,"line":228},[204,605,607],{"class":606},"s0W1g","  ...\n",[204,609,610],{"class":206,"line":235},[204,611,612],{"class":214},"  {\n",[204,614,615,618,622,625,627,629,632,634],{"class":206,"line":244},[204,616,617],{"class":214},"    \"",[204,619,621],{"class":620},"sJ14y","id",[204,623,624],{"class":214},"\"",[204,626,215],{"class":214},[204,628,624],{"class":214},[204,630,631],{"class":221},"6",[204,633,624],{"class":214},[204,635,636],{"class":214},",\n",[204,638,639,641,644,646,648,650,653,655],{"class":206,"line":249},[204,640,617],{"class":214},[204,642,643],{"class":620},"name",[204,645,624],{"class":214},[204,647,215],{"class":214},[204,649,624],{"class":214},[204,651,652],{"class":221},"サイトの名前",[204,654,624],{"class":214},[204,656,636],{"class":214},[204,658,659,661,664,666,668,670,673,675],{"class":206,"line":257},[204,660,617],{"class":214},[204,662,663],{"class":620},"sub_title",[204,665,624],{"class":214},[204,667,215],{"class":214},[204,669,624],{"class":214},[204,671,672],{"class":221},"サイトのキャッチフレーズ的なもの",[204,674,624],{"class":214},[204,676,636],{"class":214},[204,678,679,681,684,686,688,690,693],{"class":206,"line":268},[204,680,617],{"class":214},[204,682,683],{"class":620},"mailUser",[204,685,624],{"class":214},[204,687,215],{"class":214},[204,689,624],{"class":214},[204,691,692],{"class":221},"3::1543",[204,694,695],{"class":214},"\"\n",[204,697,698],{"class":206,"line":279},[204,699,700],{"class":214},"  },\n",[204,702,703],{"class":206,"line":287},[204,704,607],{"class":606},[204,706,707],{"class":206,"line":296},[204,708,709],{"class":214},"]\n",[13,711,712],{},"旧CMSからはこの様になっており、mailUserがこのカテゴリー（ブログ）を管理するユーザーです。wordpressのマルチサイトを作るには、初期管理ユーザーとサイト名があればとりあえず作れます。",[13,714,715],{},"そして追加スクリプトは以下の通りです。",[72,717,720],{"className":539,"code":718,"filename":719,"language":541,"meta":80,"style":80},"\u003C?php\nrequire_once('..\u002F..\u002Fwp-load.php');\n\n$json = file_get_contents('\u002Fvar\u002Fwww\u002Fhtml\u002Fscripts\u002Fcategory\u002Farticle_categpry.json');\n$cats = json_decode($json,true);\n$new_id = 2;\nforeach($cats as $key=>&$val){\n    wpmu_create_blog('localhost','blog'.$key,$val['name'],1,array('blogdescription'=>$val['sub_title']));\n    \n    if( is_wp_error( $return ) ) {\n        print_r($return->get_error_message().\"\\n\");\n        continue;\n    }\n    \n    $val['new_id']=$new_id;\n    $new_id++;\n}\nfile_put_contents(\"\u002Fvar\u002Fwww\u002Fhtml\u002Fscripts\u002Fcategory\u002Fregistered_article_categpry.json\", json_encode($cats,JSON_UNESCAPED_UNICODE));\n","\u002Fscripts\u002Fcreate_site.php",[61,721,722,727,732,736,741,746,751,756,761,766,771,776,781,786,790,795,800,805],{"__ignoreMap":80},[204,723,724],{"class":206,"line":207},[204,725,726],{},"\u003C?php\n",[204,728,729],{"class":206,"line":228},[204,730,731],{},"require_once('..\u002F..\u002Fwp-load.php');\n",[204,733,734],{"class":206,"line":235},[204,735,232],{"emptyLinePlaceholder":231},[204,737,738],{"class":206,"line":244},[204,739,740],{},"$json = file_get_contents('\u002Fvar\u002Fwww\u002Fhtml\u002Fscripts\u002Fcategory\u002Farticle_categpry.json');\n",[204,742,743],{"class":206,"line":249},[204,744,745],{},"$cats = json_decode($json,true);\n",[204,747,748],{"class":206,"line":257},[204,749,750],{},"$new_id = 2;\n",[204,752,753],{"class":206,"line":268},[204,754,755],{},"foreach($cats as $key=>&$val){\n",[204,757,758],{"class":206,"line":279},[204,759,760],{},"    wpmu_create_blog('localhost','blog'.$key,$val['name'],1,array('blogdescription'=>$val['sub_title']));\n",[204,762,763],{"class":206,"line":287},[204,764,765],{},"    \n",[204,767,768],{"class":206,"line":296},[204,769,770],{},"    if( is_wp_error( $return ) ) {\n",[204,772,773],{"class":206,"line":304},[204,774,775],{},"        print_r($return->get_error_message().\"\\n\");\n",[204,777,778],{"class":206,"line":315},[204,779,780],{},"        continue;\n",[204,782,783],{"class":206,"line":326},[204,784,785],{},"    }\n",[204,787,788],{"class":206,"line":337},[204,789,765],{},[204,791,792],{"class":206,"line":348},[204,793,794],{},"    $val['new_id']=$new_id;\n",[204,796,797],{"class":206,"line":4},[204,798,799],{},"    $new_id++;\n",[204,801,802],{"class":206,"line":363},[204,803,804],{},"}\n",[204,806,807],{"class":206,"line":368},[204,808,809],{},"file_put_contents(\"\u002Fvar\u002Fwww\u002Fhtml\u002Fscripts\u002Fcategory\u002Fregistered_article_categpry.json\", json_encode($cats,JSON_UNESCAPED_UNICODE));\n",[13,811,812,815,816,818,819,822,823],{},[61,813,814],{},"wpmu_create_blog"," という関数を用いて作成します。",[61,817,814],{}," を使用するためには上記のコード二行目にある ",[61,820,821],{},"require_once('..\u002F..\u002Fwp-load.php');"," が必要になります。 ",[20,824,825,828],{},[61,826,827],{},"wp-load.php"," を使用することでwordpress関数が使用できる様になります。引数は以下の様に取ります。",[72,830,832],{"className":539,"code":831,"language":541,"meta":80,"style":80},"wpmu_create_blog(\n  ブログのドメイン（必須）,\n  ブログのサブディレクトリ名（必須）,\n  サイト名（必須）,\n  管理ユーザーID（必須）,\n  そのほかの情報（配列）,\n)\n",[61,833,834,839,844,849,854,859,864],{"__ignoreMap":80},[204,835,836],{"class":206,"line":207},[204,837,838],{},"wpmu_create_blog(\n",[204,840,841],{"class":206,"line":228},[204,842,843],{},"  ブログのドメイン（必須）,\n",[204,845,846],{"class":206,"line":235},[204,847,848],{},"  ブログのサブディレクトリ名（必須）,\n",[204,850,851],{"class":206,"line":244},[204,852,853],{},"  サイト名（必須）,\n",[204,855,856],{"class":206,"line":249},[204,857,858],{},"  管理ユーザーID（必須）,\n",[204,860,861],{"class":206,"line":257},[204,862,863],{},"  そのほかの情報（配列）,\n",[204,865,866],{"class":206,"line":268},[204,867,868],{},")\n",[13,870,871],{},"上記スクリプトは非常に単純です。JSONにある旧CMSに登録されたブログカテゴリー分だけforeachで回して関数を実行しているだけです。",[13,873,874,875],{},"しかしブログカテゴリーは後でユーザーと投稿を挿入する際に必要となるので、 ",[20,876,877],{},"「wordpressでのブログIDと以前のブログカテゴリーIDを対応させる」様にしておきます。下記の様に工夫をしておきます。",[72,879,881],{"className":539,"code":880,"language":541,"meta":80,"style":80},"$new_id = 2; \u002F\u002F 新しいブログIDの最初の値\nforeach($cats as $key=>&$val){ \u002F\u002F参照渡しをしておく\n    wpmu_create_blog('localhost','blog'.$key,$val['name'],1,array('blogdescription'=>$val['sub_title']));\n    \n    if( is_wp_error( $return ) ) {\n        print_r($return->get_error_message().\"\\n\");\n        continue;\n    }\n    \u002F\u002F エラーが起きなかったら new_id という新しいカラムと共にwordpressのブログIDを記録\n    $val['new_id']=$new_id;\n    $new_id++;\n}\n\n\u002F\u002F wordpressと旧CMSとの関係性を記録したJSONを出力\nfile_put_contents(\"\u002Fvar\u002Fwww\u002Fhtml\u002Fscripts\u002Fcategory\u002Fregistered_article_categpry.json\", json_encode($cats,JSON_UNESCAPED_UNICODE));\n",[61,882,883,888,893,897,901,905,909,913,917,922,926,930,934,938,943],{"__ignoreMap":80},[204,884,885],{"class":206,"line":207},[204,886,887],{},"$new_id = 2; \u002F\u002F 新しいブログIDの最初の値\n",[204,889,890],{"class":206,"line":228},[204,891,892],{},"foreach($cats as $key=>&$val){ \u002F\u002F参照渡しをしておく\n",[204,894,895],{"class":206,"line":235},[204,896,760],{},[204,898,899],{"class":206,"line":244},[204,900,765],{},[204,902,903],{"class":206,"line":249},[204,904,770],{},[204,906,907],{"class":206,"line":257},[204,908,775],{},[204,910,911],{"class":206,"line":268},[204,912,780],{},[204,914,915],{"class":206,"line":279},[204,916,785],{},[204,918,919],{"class":206,"line":287},[204,920,921],{},"    \u002F\u002F エラーが起きなかったら new_id という新しいカラムと共にwordpressのブログIDを記録\n",[204,923,924],{"class":206,"line":296},[204,925,794],{},[204,927,928],{"class":206,"line":304},[204,929,799],{},[204,931,932],{"class":206,"line":315},[204,933,804],{},[204,935,936],{"class":206,"line":326},[204,937,232],{"emptyLinePlaceholder":231},[204,939,940],{"class":206,"line":337},[204,941,942],{},"\u002F\u002F wordpressと旧CMSとの関係性を記録したJSONを出力\n",[204,944,945],{"class":206,"line":348},[204,946,809],{},[13,948,949],{},"すると新しく作成されたJSONを見ると",[72,951,954],{"className":591,"code":952,"filename":953,"language":594,"meta":80,"style":80},"[\n...\n  {\n    \"id\":\"6\",\n    \"name\":\"サイトの名前\",\n    \"sub_title\":\"サイトのキャッチフレーズ的なもの\",\n    \"mailUser\":\"3::1543\",\n    \"new_id\":3\n  }\n...\n]\n","registered_article_category.json",[61,955,956,960,965,969,987,1005,1023,1041,1056,1061,1065],{"__ignoreMap":80},[204,957,958],{"class":206,"line":207},[204,959,601],{"class":214},[204,961,962],{"class":206,"line":228},[204,963,964],{"class":606},"...\n",[204,966,967],{"class":206,"line":235},[204,968,612],{"class":214},[204,970,971,973,975,977,979,981,983,985],{"class":206,"line":244},[204,972,617],{"class":214},[204,974,621],{"class":620},[204,976,624],{"class":214},[204,978,215],{"class":214},[204,980,624],{"class":214},[204,982,631],{"class":221},[204,984,624],{"class":214},[204,986,636],{"class":214},[204,988,989,991,993,995,997,999,1001,1003],{"class":206,"line":249},[204,990,617],{"class":214},[204,992,643],{"class":620},[204,994,624],{"class":214},[204,996,215],{"class":214},[204,998,624],{"class":214},[204,1000,652],{"class":221},[204,1002,624],{"class":214},[204,1004,636],{"class":214},[204,1006,1007,1009,1011,1013,1015,1017,1019,1021],{"class":206,"line":257},[204,1008,617],{"class":214},[204,1010,663],{"class":620},[204,1012,624],{"class":214},[204,1014,215],{"class":214},[204,1016,624],{"class":214},[204,1018,672],{"class":221},[204,1020,624],{"class":214},[204,1022,636],{"class":214},[204,1024,1025,1027,1029,1031,1033,1035,1037,1039],{"class":206,"line":268},[204,1026,617],{"class":214},[204,1028,683],{"class":620},[204,1030,624],{"class":214},[204,1032,215],{"class":214},[204,1034,624],{"class":214},[204,1036,692],{"class":221},[204,1038,624],{"class":214},[204,1040,636],{"class":214},[204,1042,1043,1045,1048,1050,1052],{"class":206,"line":279},[204,1044,617],{"class":214},[204,1046,1047],{"class":620},"new_id",[204,1049,624],{"class":214},[204,1051,215],{"class":214},[204,1053,1055],{"class":1054},"sx098","3\n",[204,1057,1058],{"class":206,"line":287},[204,1059,1060],{"class":214},"  }\n",[204,1062,1063],{"class":206,"line":296},[204,1064,964],{"class":606},[204,1066,1067],{"class":206,"line":304},[204,1068,709],{"class":214},[13,1070,1071,1072],{},"new_idというカラムとwordpressでのブログIDが入りました。こうすることで ",[20,1073,1074],{},"「旧CMSでのブログカテゴリーID 6のものはwordpressではブログID 3」という関係性を保存できます。",[13,1076,1077],{},"以上でブログカテゴリーの移行は終了しました。訳70サイトもあるのでこんな感じ↓になります笑。",[522,1079],{":src":1080,":width":525,":center":526},"'_mix\u002Fwp-sites-146x300.jpeg'",[505,1082,1083],{"id":1083},"スクリプトの実行方法",[13,1085,1086],{},"これらスクリプトはコマンドで実行します。dockerで管理しているので",[72,1088,1091],{"className":1089,"code":1090,"language":77},[75],"docker exec -it {wordpressのコンテナ名} \u002Fbin\u002Fbash\n",[61,1092,1090],{"__ignoreMap":80},[13,1094,1095],{},"この様にしてwordpressを立ち上げているコンテナに入って、コマンドを実行しにいきます。",[72,1097,1100],{"className":1098,"code":1099,"language":77},[75],"root@0756d76ddde1:\u002Fvar\u002Fwww\u002Fhtml# \n",[61,1101,1099],{"__ignoreMap":80},[13,1103,1104,1105,1108],{},"コンテナに入るとこの様にターミナルが変化します。リモートサーバーにsshでログインしたみたいな感じです。そしてdockerを立ち上げるときに ",[61,1106,1107],{},"scripts\u002F"," ディレクトリをボリュームしているのでそこに移動します。",[72,1110,1113],{"className":1111,"code":1112,"language":77},[75],"root@0756d76ddde1:\u002Fvar\u002Fwww\u002Fhtml# cd scripts\nroot@0756d76ddde1:\u002Fvar\u002Fwww\u002Fhtml\u002Fscripts# ls\ncreate_site.php\narticle_category.json\n\nroot@0756d76ddde1:\u002Fvar\u002Fwww\u002Fhtml\u002Fscripts# php create_site.php\n",[61,1114,1112],{"__ignoreMap":80},[13,1116,1117],{},"上記の様にphpファイルを指定することでスクリプトが実行されます。",[27,1119,1121],{"id":1120},"ユーザーを機械的に作成割り当て","ユーザーを機械的に作成、割り当て",[505,1123,1124],{"id":1124},"ユーザーの追加",[13,1126,1127],{},"ではそれぞれのブログを作成したので次はユーザーを作成していきます。ユーザーは以下の様なJSONです。",[72,1129,1132],{"className":591,"code":1130,"filename":1131,"language":594,"meta":80,"style":80},"[\n    ...\n    {\n        \"uid\":\"3\",\n        \"loginname\":\"webmaster\",\n        \"name\":\"お名前\",\n        \"email\":\"example@example.com\",\n        \"user_avatar\":\n        \"thumbnail.jpg\",\n    }\n    ...\n]\n","user.json",[61,1133,1134,1138,1143,1148,1169,1189,1208,1228,1239,1250,1254,1258],{"__ignoreMap":80},[204,1135,1136],{"class":206,"line":207},[204,1137,601],{"class":214},[204,1139,1140],{"class":206,"line":228},[204,1141,1142],{"class":606},"    ...\n",[204,1144,1145],{"class":206,"line":235},[204,1146,1147],{"class":214},"    {\n",[204,1149,1150,1153,1156,1158,1160,1162,1165,1167],{"class":206,"line":244},[204,1151,1152],{"class":214},"        \"",[204,1154,1155],{"class":620},"uid",[204,1157,624],{"class":214},[204,1159,215],{"class":214},[204,1161,624],{"class":214},[204,1163,1164],{"class":221},"3",[204,1166,624],{"class":214},[204,1168,636],{"class":214},[204,1170,1171,1173,1176,1178,1180,1182,1185,1187],{"class":206,"line":249},[204,1172,1152],{"class":214},[204,1174,1175],{"class":620},"loginname",[204,1177,624],{"class":214},[204,1179,215],{"class":214},[204,1181,624],{"class":214},[204,1183,1184],{"class":221},"webmaster",[204,1186,624],{"class":214},[204,1188,636],{"class":214},[204,1190,1191,1193,1195,1197,1199,1201,1204,1206],{"class":206,"line":257},[204,1192,1152],{"class":214},[204,1194,643],{"class":620},[204,1196,624],{"class":214},[204,1198,215],{"class":214},[204,1200,624],{"class":214},[204,1202,1203],{"class":221},"お名前",[204,1205,624],{"class":214},[204,1207,636],{"class":214},[204,1209,1210,1212,1215,1217,1219,1221,1224,1226],{"class":206,"line":268},[204,1211,1152],{"class":214},[204,1213,1214],{"class":620},"email",[204,1216,624],{"class":214},[204,1218,215],{"class":214},[204,1220,624],{"class":214},[204,1222,1223],{"class":221},"example@example.com",[204,1225,624],{"class":214},[204,1227,636],{"class":214},[204,1229,1230,1232,1235,1237],{"class":206,"line":279},[204,1231,1152],{"class":214},[204,1233,1234],{"class":620},"user_avatar",[204,1236,624],{"class":214},[204,1238,241],{"class":214},[204,1240,1241,1243,1246,1248],{"class":206,"line":287},[204,1242,1152],{"class":214},[204,1244,1245],{"class":221},"thumbnail.jpg",[204,1247,624],{"class":214},[204,1249,636],{"class":214},[204,1251,1252],{"class":206,"line":296},[204,1253,785],{"class":214},[204,1255,1256],{"class":206,"line":304},[204,1257,1142],{"class":606},[204,1259,1260],{"class":206,"line":315},[204,1261,709],{"class":214},[13,1263,1264,1267],{},[61,1265,1266],{},"groupid"," は旧CMSの権限グループです。そしてユーザー追加スクリプトは以下の様になります。",[72,1269,1271],{"className":539,"code":1270,"language":541,"meta":80,"style":80},"\u003C?php\nrequire_once('..\u002F..\u002Fwp-load.php');\n\n$json = file_get_contents('\u002Fvar\u002Fwww\u002Fhtml\u002Fscripts\u002Fuser\u002Fuser.json');\n$uesrs = json_decode($json,true);\n\n$new_id = 2;\nforeach($uesrs as &$val){\n\n    $role;\n    switch($val['groupid']){\n        case \"1\":\n            $role = 'administrator';\n        break;\n       \n        case \"2\":\n            $role = 'contributor';\n        break;\n\n        case \"3\":\n            $role = 'contributor';\n        break;\n\n        case \"4\":\n            $role = 'administrator';\n        break;\n\n        case \"5\":\n            $role = 'contributor';\n        break;\n\n        case \"6\":\n            $role = 'administrator';\n        break;\n\n        case \"7\":\n            $role = 'administrator';\n        break;\n    }\n\n    $user = [\n        'user_pass'=>'PASS_WORD',\n        'user_login'=>$val['loginname'],\n        'user_email'=>$val['email'],\n        'display_name'=>$val['uid'],\n        'role'=>$role,\n    ];\n    $return = wp_insert_user($user);\n    \n    if( is_wp_error( $return ) ) {\n        print_r($return->get_error_message().':'.$val['loginname'].\"\\n\");\n        continue;\n    }\n    $val['new_id']=$new_id;\n    $new_id++;\n}\nfile_put_contents(\"\u002Fvar\u002Fwww\u002Fhtml\u002Fscripts\u002Fuser\u002Fregistered_user.json\", json_encode($uesrs,JSON_UNESCAPED_UNICODE));\n",[61,1272,1273,1277,1281,1285,1290,1295,1299,1303,1308,1312,1317,1322,1327,1332,1337,1342,1347,1352,1356,1360,1365,1369,1373,1377,1382,1386,1390,1394,1399,1403,1407,1411,1417,1422,1427,1432,1438,1443,1448,1453,1458,1464,1470,1476,1482,1488,1494,1500,1506,1511,1516,1522,1527,1532,1537,1542,1547],{"__ignoreMap":80},[204,1274,1275],{"class":206,"line":207},[204,1276,726],{},[204,1278,1279],{"class":206,"line":228},[204,1280,731],{},[204,1282,1283],{"class":206,"line":235},[204,1284,232],{"emptyLinePlaceholder":231},[204,1286,1287],{"class":206,"line":244},[204,1288,1289],{},"$json = file_get_contents('\u002Fvar\u002Fwww\u002Fhtml\u002Fscripts\u002Fuser\u002Fuser.json');\n",[204,1291,1292],{"class":206,"line":249},[204,1293,1294],{},"$uesrs = json_decode($json,true);\n",[204,1296,1297],{"class":206,"line":257},[204,1298,232],{"emptyLinePlaceholder":231},[204,1300,1301],{"class":206,"line":268},[204,1302,750],{},[204,1304,1305],{"class":206,"line":279},[204,1306,1307],{},"foreach($uesrs as &$val){\n",[204,1309,1310],{"class":206,"line":287},[204,1311,232],{"emptyLinePlaceholder":231},[204,1313,1314],{"class":206,"line":296},[204,1315,1316],{},"    $role;\n",[204,1318,1319],{"class":206,"line":304},[204,1320,1321],{},"    switch($val['groupid']){\n",[204,1323,1324],{"class":206,"line":315},[204,1325,1326],{},"        case \"1\":\n",[204,1328,1329],{"class":206,"line":326},[204,1330,1331],{},"            $role = 'administrator';\n",[204,1333,1334],{"class":206,"line":337},[204,1335,1336],{},"        break;\n",[204,1338,1339],{"class":206,"line":348},[204,1340,1341],{},"       \n",[204,1343,1344],{"class":206,"line":4},[204,1345,1346],{},"        case \"2\":\n",[204,1348,1349],{"class":206,"line":363},[204,1350,1351],{},"            $role = 'contributor';\n",[204,1353,1354],{"class":206,"line":368},[204,1355,1336],{},[204,1357,1358],{"class":206,"line":376},[204,1359,232],{"emptyLinePlaceholder":231},[204,1361,1362],{"class":206,"line":386},[204,1363,1364],{},"        case \"3\":\n",[204,1366,1367],{"class":206,"line":395},[204,1368,1351],{},[204,1370,1371],{"class":206,"line":402},[204,1372,1336],{},[204,1374,1375],{"class":206,"line":412},[204,1376,232],{"emptyLinePlaceholder":231},[204,1378,1379],{"class":206,"line":422},[204,1380,1381],{},"        case \"4\":\n",[204,1383,1384],{"class":206,"line":432},[204,1385,1331],{},[204,1387,1388],{"class":206,"line":447},[204,1389,1336],{},[204,1391,1392],{"class":206,"line":454},[204,1393,232],{"emptyLinePlaceholder":231},[204,1395,1396],{"class":206,"line":462},[204,1397,1398],{},"        case \"5\":\n",[204,1400,1401],{"class":206,"line":467},[204,1402,1351],{},[204,1404,1405],{"class":206,"line":475},[204,1406,1336],{},[204,1408,1409],{"class":206,"line":483},[204,1410,232],{"emptyLinePlaceholder":231},[204,1412,1414],{"class":206,"line":1413},32,[204,1415,1416],{},"        case \"6\":\n",[204,1418,1420],{"class":206,"line":1419},33,[204,1421,1331],{},[204,1423,1425],{"class":206,"line":1424},34,[204,1426,1336],{},[204,1428,1430],{"class":206,"line":1429},35,[204,1431,232],{"emptyLinePlaceholder":231},[204,1433,1435],{"class":206,"line":1434},36,[204,1436,1437],{},"        case \"7\":\n",[204,1439,1441],{"class":206,"line":1440},37,[204,1442,1331],{},[204,1444,1446],{"class":206,"line":1445},38,[204,1447,1336],{},[204,1449,1451],{"class":206,"line":1450},39,[204,1452,785],{},[204,1454,1456],{"class":206,"line":1455},40,[204,1457,232],{"emptyLinePlaceholder":231},[204,1459,1461],{"class":206,"line":1460},41,[204,1462,1463],{},"    $user = [\n",[204,1465,1467],{"class":206,"line":1466},42,[204,1468,1469],{},"        'user_pass'=>'PASS_WORD',\n",[204,1471,1473],{"class":206,"line":1472},43,[204,1474,1475],{},"        'user_login'=>$val['loginname'],\n",[204,1477,1479],{"class":206,"line":1478},44,[204,1480,1481],{},"        'user_email'=>$val['email'],\n",[204,1483,1485],{"class":206,"line":1484},45,[204,1486,1487],{},"        'display_name'=>$val['uid'],\n",[204,1489,1491],{"class":206,"line":1490},46,[204,1492,1493],{},"        'role'=>$role,\n",[204,1495,1497],{"class":206,"line":1496},47,[204,1498,1499],{},"    ];\n",[204,1501,1503],{"class":206,"line":1502},48,[204,1504,1505],{},"    $return = wp_insert_user($user);\n",[204,1507,1509],{"class":206,"line":1508},49,[204,1510,765],{},[204,1512,1514],{"class":206,"line":1513},50,[204,1515,770],{},[204,1517,1519],{"class":206,"line":1518},51,[204,1520,1521],{},"        print_r($return->get_error_message().':'.$val['loginname'].\"\\n\");\n",[204,1523,1525],{"class":206,"line":1524},52,[204,1526,780],{},[204,1528,1530],{"class":206,"line":1529},53,[204,1531,785],{},[204,1533,1535],{"class":206,"line":1534},54,[204,1536,794],{},[204,1538,1540],{"class":206,"line":1539},55,[204,1541,799],{},[204,1543,1545],{"class":206,"line":1544},56,[204,1546,804],{},[204,1548,1550],{"class":206,"line":1549},57,[204,1551,1552],{},"file_put_contents(\"\u002Fvar\u002Fwww\u002Fhtml\u002Fscripts\u002Fuser\u002Fregistered_user.json\", json_encode($uesrs,JSON_UNESCAPED_UNICODE));\n",[13,1554,1555,1556,1559],{},"ちょっと自分のためのコードがありますが、重要なのは ",[61,1557,1558],{},"wp_insert_user"," という関数です。引数は連想配列で入れます。以下のキー名で配列にします。",[72,1561,1563],{"className":539,"code":1562,"language":541,"meta":80,"style":80},"$user = [\n        'user_pass'=>'PASS_WORD',       \u002F\u002F パスワード名\n        'user_login'=>$val['loginname'],\u002F\u002F ログイン名（英数字でないといけない）\n        'user_email'=>$val['email'],    \u002F\u002F 登録アドレス\n        'display_name'=>$val['uid'],    \u002F\u002F 表示名（プロフ名）\n        'role'=>$role,                  \u002F\u002F 権限キー\n    ];\n",[61,1564,1565,1570,1578,1583,1591,1599,1607],{"__ignoreMap":80},[204,1566,1567],{"class":206,"line":207},[204,1568,1569],{},"$user = [\n",[204,1571,1572,1575],{"class":206,"line":228},[204,1573,1574],{},"        'user_pass'=>'PASS_WORD',",[204,1576,1577],{},"       \u002F\u002F パスワード名\n",[204,1579,1580],{"class":206,"line":235},[204,1581,1582],{},"        'user_login'=>$val['loginname'],\u002F\u002F ログイン名（英数字でないといけない）\n",[204,1584,1585,1588],{"class":206,"line":244},[204,1586,1587],{},"        'user_email'=>$val['email'],",[204,1589,1590],{},"    \u002F\u002F 登録アドレス\n",[204,1592,1593,1596],{"class":206,"line":249},[204,1594,1595],{},"        'display_name'=>$val['uid'],",[204,1597,1598],{},"    \u002F\u002F 表示名（プロフ名）\n",[204,1600,1601,1604],{"class":206,"line":257},[204,1602,1603],{},"        'role'=>$role,",[204,1605,1606],{},"                  \u002F\u002F 権限キー\n",[204,1608,1609],{"class":206,"line":268},[204,1610,1499],{},[13,1612,1613],{},"権限キーは文字列で指定します。私のコードでは旧CMSのIDを対応させています。そしてブログの時の様に旧データと新データのIDを対応させる様にします。",[72,1615,1617],{"className":539,"code":1616,"language":541,"meta":80,"style":80},"...\n   $return = wp_insert_user($user);\n\n　　if( is_wp_error( $return ) ) {\n        print_r($return->get_error_message().':'.$val['loginname'].\"\\n\");\n        continue;\n    }\n    $val['new_id']=$new_id;\n    $new_id++;\n}\nfile_put_contents(\"\u002Fvar\u002Fwww\u002Fhtml\u002Fscripts\u002Fuser\u002Fregistered_user.json\", json_encode($uesrs,JSON_UNESCAPED_UNICODE));\n",[61,1618,1619,1623,1628,1632,1637,1641,1645,1649,1653,1657,1661],{"__ignoreMap":80},[204,1620,1621],{"class":206,"line":207},[204,1622,964],{},[204,1624,1625],{"class":206,"line":228},[204,1626,1627],{},"   $return = wp_insert_user($user);\n",[204,1629,1630],{"class":206,"line":235},[204,1631,232],{"emptyLinePlaceholder":231},[204,1633,1634],{"class":206,"line":244},[204,1635,1636],{},"　　if( is_wp_error( $return ) ) {\n",[204,1638,1639],{"class":206,"line":249},[204,1640,1521],{},[204,1642,1643],{"class":206,"line":257},[204,1644,780],{},[204,1646,1647],{"class":206,"line":268},[204,1648,785],{},[204,1650,1651],{"class":206,"line":279},[204,1652,794],{},[204,1654,1655],{"class":206,"line":287},[204,1656,799],{},[204,1658,1659],{"class":206,"line":296},[204,1660,804],{},[204,1662,1663],{"class":206,"line":304},[204,1664,1552],{},[13,1666,1667,1674],{},[20,1668,1669,1670,1673],{},"ちなみにスクリプトを実行するときは ",[61,1671,1672],{},"is_wp_error( $return ) ","でエラーをキャッチできる様にしましょう。"," なぜか私のデータには同じユーザーのデータがあったりなどで、「ユーザーがすでに登録されています」というエラーでIDがずれてしまうという事件がありました。そのためにキャッチできるスクリプトを入れておきましょう。",[505,1676,1677],{"id":1677},"ユーザーをブログに当てはめ",[13,1679,1680],{},"ユーザーのスクリプトを実行するとユーザーが作成され、新旧のIDを対応させたユーザーJSONファイルができました。これとブログカテゴリーのデータを用いて各ブログを管理するユーザーを割り当てていきます。",[72,1682,1685],{"className":539,"code":1683,"filename":1684,"language":541,"meta":80,"style":80},"\u003C?php\nrequire_once('..\u002F..\u002Fwp-load.php');\n\n\u002F\u002F wordpress user IDが入ったuserのファイル\n$user_json = file_get_contents('\u002Fvar\u002Fwww\u002Fhtml\u002Fscripts\u002Fuser\u002Fregistered_user.json');\n$uesrs = json_decode($user_json,true);\n\n\u002F\u002F wordpress blog IDが入ったブログカテゴリーのファイル\n$cat_json = file_get_contents('\u002Fvar\u002Fwww\u002Fhtml\u002Fscripts\u002Fcategory\u002Fregistered_article_categpry.json');\n$cats = json_decode($cat_json,true);\n\nforeach($cats as $cat_key=> $cat_val){\n    $old_user_ids =explode('::',$cat_val['mailUser']);\n    \n    foreach($old_user_ids as $old_id){\n        $user = array_values(array_filter($uesrs,function($ele) use($old_id) {\n            return $ele['uid'] == $old_id && isset($ele['new_id']);\n        }));\n\n        if(!empty($user)){\n            $new_userid = $user[0]['new_id'];\n            $new_cat_id =intval($cat_val['new_id']);\n            add_user_to_blog($new_cat_id,$new_userid,'administrator');\n        }\n    }\n}\n","add_user_blog.php",[61,1686,1687,1691,1695,1699,1704,1709,1714,1718,1723,1728,1733,1737,1742,1747,1751,1756,1761,1766,1771,1775,1780,1785,1790,1795,1800,1804],{"__ignoreMap":80},[204,1688,1689],{"class":206,"line":207},[204,1690,726],{},[204,1692,1693],{"class":206,"line":228},[204,1694,731],{},[204,1696,1697],{"class":206,"line":235},[204,1698,232],{"emptyLinePlaceholder":231},[204,1700,1701],{"class":206,"line":244},[204,1702,1703],{},"\u002F\u002F wordpress user IDが入ったuserのファイル\n",[204,1705,1706],{"class":206,"line":249},[204,1707,1708],{},"$user_json = file_get_contents('\u002Fvar\u002Fwww\u002Fhtml\u002Fscripts\u002Fuser\u002Fregistered_user.json');\n",[204,1710,1711],{"class":206,"line":257},[204,1712,1713],{},"$uesrs = json_decode($user_json,true);\n",[204,1715,1716],{"class":206,"line":268},[204,1717,232],{"emptyLinePlaceholder":231},[204,1719,1720],{"class":206,"line":279},[204,1721,1722],{},"\u002F\u002F wordpress blog IDが入ったブログカテゴリーのファイル\n",[204,1724,1725],{"class":206,"line":287},[204,1726,1727],{},"$cat_json = file_get_contents('\u002Fvar\u002Fwww\u002Fhtml\u002Fscripts\u002Fcategory\u002Fregistered_article_categpry.json');\n",[204,1729,1730],{"class":206,"line":296},[204,1731,1732],{},"$cats = json_decode($cat_json,true);\n",[204,1734,1735],{"class":206,"line":304},[204,1736,232],{"emptyLinePlaceholder":231},[204,1738,1739],{"class":206,"line":315},[204,1740,1741],{},"foreach($cats as $cat_key=> $cat_val){\n",[204,1743,1744],{"class":206,"line":326},[204,1745,1746],{},"    $old_user_ids =explode('::',$cat_val['mailUser']);\n",[204,1748,1749],{"class":206,"line":337},[204,1750,765],{},[204,1752,1753],{"class":206,"line":348},[204,1754,1755],{},"    foreach($old_user_ids as $old_id){\n",[204,1757,1758],{"class":206,"line":4},[204,1759,1760],{},"        $user = array_values(array_filter($uesrs,function($ele) use($old_id) {\n",[204,1762,1763],{"class":206,"line":363},[204,1764,1765],{},"            return $ele['uid'] == $old_id && isset($ele['new_id']);\n",[204,1767,1768],{"class":206,"line":368},[204,1769,1770],{},"        }));\n",[204,1772,1773],{"class":206,"line":376},[204,1774,232],{"emptyLinePlaceholder":231},[204,1776,1777],{"class":206,"line":386},[204,1778,1779],{},"        if(!empty($user)){\n",[204,1781,1782],{"class":206,"line":395},[204,1783,1784],{},"            $new_userid = $user[0]['new_id'];\n",[204,1786,1787],{"class":206,"line":402},[204,1788,1789],{},"            $new_cat_id =intval($cat_val['new_id']);\n",[204,1791,1792],{"class":206,"line":412},[204,1793,1794],{},"            add_user_to_blog($new_cat_id,$new_userid,'administrator');\n",[204,1796,1797],{"class":206,"line":422},[204,1798,1799],{},"        }\n",[204,1801,1802],{"class":206,"line":432},[204,1803,785],{},[204,1805,1806],{"class":206,"line":447},[204,1807,804],{},[13,1809,1810,1811,1814,1815,1817,1818,1821],{},"私のデータの場合、ユーザーが複数人いたので",[61,1812,1813],{},"foreach","の中でさらに",[61,1816,1813],{},"しています。マルチサイト の特定のブログに対してユーザーを割り当てるためには ",[61,1819,1820],{},"add_user_to_blog"," を用います。",[13,1823,1824],{},"第一引数にユーザーID、第二引数に対象のブログID、第三には権限グループを指定することで簡単にブログに対してユーザーを割り当てられます。",[27,1826,1827],{"id":1827},"投稿を機械的に流し込み",[13,1829,1830],{},"最後に投稿を流し込みます。私が使用したデータは以下の様なデータになっています。",[72,1832,1835],{"className":591,"code":1833,"filename":1834,"language":594,"meta":80,"style":80},"[\n...\n    {\n    　\"id\":\"162\",\n    　\"date\":\"2007-08-02\",\n    　\"category_id\":\"6\",\n    　\"uid\":\"1543\",\n    　\"title\":\"タイトル\",\n    　\"content\":\"ここにブログの内容がプレーンテキスト形式で入っています。\"\n    },\n...\n]\n","article.json",[61,1836,1837,1841,1845,1849,1869,1889,1908,1927,1947,1965,1970,1974],{"__ignoreMap":80},[204,1838,1839],{"class":206,"line":207},[204,1840,601],{"class":214},[204,1842,1843],{"class":206,"line":228},[204,1844,964],{"class":606},[204,1846,1847],{"class":206,"line":235},[204,1848,1147],{"class":214},[204,1850,1851,1854,1856,1858,1860,1862,1865,1867],{"class":206,"line":244},[204,1852,1853],{"class":214},"    　\"",[204,1855,621],{"class":620},[204,1857,624],{"class":214},[204,1859,215],{"class":214},[204,1861,624],{"class":214},[204,1863,1864],{"class":221},"162",[204,1866,624],{"class":214},[204,1868,636],{"class":214},[204,1870,1871,1873,1876,1878,1880,1882,1885,1887],{"class":206,"line":249},[204,1872,1853],{"class":214},[204,1874,1875],{"class":620},"date",[204,1877,624],{"class":214},[204,1879,215],{"class":214},[204,1881,624],{"class":214},[204,1883,1884],{"class":221},"2007-08-02",[204,1886,624],{"class":214},[204,1888,636],{"class":214},[204,1890,1891,1893,1896,1898,1900,1902,1904,1906],{"class":206,"line":257},[204,1892,1853],{"class":214},[204,1894,1895],{"class":620},"category_id",[204,1897,624],{"class":214},[204,1899,215],{"class":214},[204,1901,624],{"class":214},[204,1903,631],{"class":221},[204,1905,624],{"class":214},[204,1907,636],{"class":214},[204,1909,1910,1912,1914,1916,1918,1920,1923,1925],{"class":206,"line":268},[204,1911,1853],{"class":214},[204,1913,1155],{"class":620},[204,1915,624],{"class":214},[204,1917,215],{"class":214},[204,1919,624],{"class":214},[204,1921,1922],{"class":221},"1543",[204,1924,624],{"class":214},[204,1926,636],{"class":214},[204,1928,1929,1931,1934,1936,1938,1940,1943,1945],{"class":206,"line":279},[204,1930,1853],{"class":214},[204,1932,1933],{"class":620},"title",[204,1935,624],{"class":214},[204,1937,215],{"class":214},[204,1939,624],{"class":214},[204,1941,1942],{"class":221},"タイトル",[204,1944,624],{"class":214},[204,1946,636],{"class":214},[204,1948,1949,1951,1954,1956,1958,1960,1963],{"class":206,"line":287},[204,1950,1853],{"class":214},[204,1952,1953],{"class":620},"content",[204,1955,624],{"class":214},[204,1957,215],{"class":214},[204,1959,624],{"class":214},[204,1961,1962],{"class":221},"ここにブログの内容がプレーンテキスト形式で入っています。",[204,1964,695],{"class":214},[204,1966,1967],{"class":206,"line":296},[204,1968,1969],{"class":214},"    },\n",[204,1971,1972],{"class":206,"line":304},[204,1973,964],{"class":606},[204,1975,1976],{"class":206,"line":315},[204,1977,709],{"class":214},[13,1979,1980,1982,1983,1985],{},[61,1981,1155],{},"を元にユーザー（著者）と結び付け、",[61,1984,1895],{},"を元にどのブログに投稿するのかを指定します。以下の様なスクリプトを書きました。",[72,1987,1990],{"className":539,"code":1988,"filename":1989,"language":541,"meta":80,"style":80},"\u003C?php\nini_set('memory_limit', '1024M');\nrequire_once('..\u002F..\u002Fwp-load.php');\n\n\u002F\u002F wordpress user IDが入ったuserのファイル\n$user_json = file_get_contents('\u002Fvar\u002Fwww\u002Fhtml\u002Fscripts\u002Fuser\u002Fregistered_user.json');\n$uesrs = json_decode($user_json,true);\n\n\u002F\u002F 旧CMSの投稿データ\n$article_json = file_get_contents('\u002Fvar\u002Fwww\u002Fhtml\u002Fscripts\u002Farticles\u002Farticle_replace.json');\n$articles = json_decode($article_json,true);\n\n\u002F\u002F wordpress blog IDが入ったブログカテゴリーのファイル\n$cat_json = file_get_contents('\u002Fvar\u002Fwww\u002Fhtml\u002Fscripts\u002Fcategory\u002Fregistered_article_categpry.json');\n$cats = json_decode($cat_json,true);\n\nforeach($articles as $key => $a_val){\n    $old_cat_id = $a_val['category_id'];\n\n    if(empty($old_cat_id)) continue;\n\n    $cat = array_values(array_filter($cats,function($ele) use($old_cat_id) {\n        return $ele['id'] == $old_cat_id && isset($ele['new_id']);\n    }));\n\n    if(empty($cat)) continue;\n\n    $new_cat_id = $cat[0]['new_id'];\n\n    $old_user_id = $a_val['uid'];\n    $user = array_values(array_filter($uesrs,function($ele) use($old_user_id) {\n        return $ele['uid'] == $old_user_id && isset($ele['new_id']);\n    }));\n\n    $new_user_id = (empty($user))?1:$user[0]['new_id'];\n    if(!get_user_by('id',intval($new_user_id))) continue;\n\n    switch_to_blog($new_cat_id);\n    $new_post = array(\n        'post_title' => $a_val['title'],\n        'post_content' => $a_val['content'],\n        'post_status' => 'publish',\n        'post_date' => date($a_val['date']),\n        'post_author' => $new_user_id,\n        'post_type' =>'post',\n    );\n    wp_insert_post($new_post);\n    restore_current_blog();\n}\n","insert_post.php",[61,1991,1992,1996,2001,2005,2009,2013,2017,2021,2025,2030,2035,2040,2044,2048,2052,2056,2060,2065,2070,2074,2079,2083,2088,2093,2098,2102,2107,2111,2116,2120,2125,2130,2135,2139,2143,2148,2153,2157,2162,2167,2172,2177,2182,2187,2192,2197,2202,2207,2212],{"__ignoreMap":80},[204,1993,1994],{"class":206,"line":207},[204,1995,726],{},[204,1997,1998],{"class":206,"line":228},[204,1999,2000],{},"ini_set('memory_limit', '1024M');\n",[204,2002,2003],{"class":206,"line":235},[204,2004,731],{},[204,2006,2007],{"class":206,"line":244},[204,2008,232],{"emptyLinePlaceholder":231},[204,2010,2011],{"class":206,"line":249},[204,2012,1703],{},[204,2014,2015],{"class":206,"line":257},[204,2016,1708],{},[204,2018,2019],{"class":206,"line":268},[204,2020,1713],{},[204,2022,2023],{"class":206,"line":279},[204,2024,232],{"emptyLinePlaceholder":231},[204,2026,2027],{"class":206,"line":287},[204,2028,2029],{},"\u002F\u002F 旧CMSの投稿データ\n",[204,2031,2032],{"class":206,"line":296},[204,2033,2034],{},"$article_json = file_get_contents('\u002Fvar\u002Fwww\u002Fhtml\u002Fscripts\u002Farticles\u002Farticle_replace.json');\n",[204,2036,2037],{"class":206,"line":304},[204,2038,2039],{},"$articles = json_decode($article_json,true);\n",[204,2041,2042],{"class":206,"line":315},[204,2043,232],{"emptyLinePlaceholder":231},[204,2045,2046],{"class":206,"line":326},[204,2047,1722],{},[204,2049,2050],{"class":206,"line":337},[204,2051,1727],{},[204,2053,2054],{"class":206,"line":348},[204,2055,1732],{},[204,2057,2058],{"class":206,"line":4},[204,2059,232],{"emptyLinePlaceholder":231},[204,2061,2062],{"class":206,"line":363},[204,2063,2064],{},"foreach($articles as $key => $a_val){\n",[204,2066,2067],{"class":206,"line":368},[204,2068,2069],{},"    $old_cat_id = $a_val['category_id'];\n",[204,2071,2072],{"class":206,"line":376},[204,2073,232],{"emptyLinePlaceholder":231},[204,2075,2076],{"class":206,"line":386},[204,2077,2078],{},"    if(empty($old_cat_id)) continue;\n",[204,2080,2081],{"class":206,"line":395},[204,2082,232],{"emptyLinePlaceholder":231},[204,2084,2085],{"class":206,"line":402},[204,2086,2087],{},"    $cat = array_values(array_filter($cats,function($ele) use($old_cat_id) {\n",[204,2089,2090],{"class":206,"line":412},[204,2091,2092],{},"        return $ele['id'] == $old_cat_id && isset($ele['new_id']);\n",[204,2094,2095],{"class":206,"line":422},[204,2096,2097],{},"    }));\n",[204,2099,2100],{"class":206,"line":432},[204,2101,232],{"emptyLinePlaceholder":231},[204,2103,2104],{"class":206,"line":447},[204,2105,2106],{},"    if(empty($cat)) continue;\n",[204,2108,2109],{"class":206,"line":454},[204,2110,232],{"emptyLinePlaceholder":231},[204,2112,2113],{"class":206,"line":462},[204,2114,2115],{},"    $new_cat_id = $cat[0]['new_id'];\n",[204,2117,2118],{"class":206,"line":467},[204,2119,232],{"emptyLinePlaceholder":231},[204,2121,2122],{"class":206,"line":475},[204,2123,2124],{},"    $old_user_id = $a_val['uid'];\n",[204,2126,2127],{"class":206,"line":483},[204,2128,2129],{},"    $user = array_values(array_filter($uesrs,function($ele) use($old_user_id) {\n",[204,2131,2132],{"class":206,"line":1413},[204,2133,2134],{},"        return $ele['uid'] == $old_user_id && isset($ele['new_id']);\n",[204,2136,2137],{"class":206,"line":1419},[204,2138,2097],{},[204,2140,2141],{"class":206,"line":1424},[204,2142,232],{"emptyLinePlaceholder":231},[204,2144,2145],{"class":206,"line":1429},[204,2146,2147],{},"    $new_user_id = (empty($user))?1:$user[0]['new_id'];\n",[204,2149,2150],{"class":206,"line":1434},[204,2151,2152],{},"    if(!get_user_by('id',intval($new_user_id))) continue;\n",[204,2154,2155],{"class":206,"line":1440},[204,2156,232],{"emptyLinePlaceholder":231},[204,2158,2159],{"class":206,"line":1445},[204,2160,2161],{},"    switch_to_blog($new_cat_id);\n",[204,2163,2164],{"class":206,"line":1450},[204,2165,2166],{},"    $new_post = array(\n",[204,2168,2169],{"class":206,"line":1455},[204,2170,2171],{},"        'post_title' => $a_val['title'],\n",[204,2173,2174],{"class":206,"line":1460},[204,2175,2176],{},"        'post_content' => $a_val['content'],\n",[204,2178,2179],{"class":206,"line":1466},[204,2180,2181],{},"        'post_status' => 'publish',\n",[204,2183,2184],{"class":206,"line":1472},[204,2185,2186],{},"        'post_date' => date($a_val['date']),\n",[204,2188,2189],{"class":206,"line":1478},[204,2190,2191],{},"        'post_author' => $new_user_id,\n",[204,2193,2194],{"class":206,"line":1484},[204,2195,2196],{},"        'post_type' =>'post',\n",[204,2198,2199],{"class":206,"line":1490},[204,2200,2201],{},"    );\n",[204,2203,2204],{"class":206,"line":1496},[204,2205,2206],{},"    wp_insert_post($new_post);\n",[204,2208,2209],{"class":206,"line":1502},[204,2210,2211],{},"    restore_current_blog();\n",[204,2213,2214],{"class":206,"line":1508},[204,2215,804],{},[13,2217,2218,2219,2222],{},"４万件分のデータとなると非常にメモリを食うので ",[61,2220,2221],{},"ini_set"," でメモリ上限を上げてあります。",[13,2224,2225,2226,2229,2230,2232,2233,2236],{},"投稿データからブログカテゴリーIDとユーザーIDを新しいwordpresの方と紐づけ、 ",[61,2227,2228],{},"wp_insert_post"," を用いて記事を挿入します。 ",[61,2231,2228],{}," 　の気をつける点はデフォルトでは",[61,2234,2235],{},"blogid=1","のブログに記事を作成するということです。",[13,2238,2239,2240,2243,2244,2247],{},"そのためコードに ",[61,2241,2242],{},"switch_to_blog();"," を追加して挿入対象のブログを切り替えています。この関数を使用するときはセットで ",[61,2245,2246],{},"restore_current_blog();"," を使います。",[13,2249,2250,2251,2253,2254,2256,2257],{},"挿入先のブログを切り替えて、",[61,2252,2228],{}," を用いて投稿を挿入します。",[61,2255,2228],{},"は引数に連想配列を入れます。 ",[20,2258,2259],{},"対応するキー名が決まっているので間違えない様にしましょう。",[13,2261,2262],{},"そして同じ様にターミナルでこのPHPを実行すればwordpressに投稿データが入ります。ちなみに４万件は15分かかりました。投稿したデータはエディタで普通に編集できますが、クラシックモードでの編集となります。画像などもきちんとタグとパスが生きていればきちんとレンダーされます。",[27,2264,2266],{"id":2265},"ミスってしまったら","ミスってしまったら..",[13,2268,2269],{},"大量のデータを入れたのにミスってしまったらdockerをリセットしましょう。コンテナーを削除してDBのボリュームも削除します。",[72,2271,2274],{"className":2272,"code":2273,"language":77},[75],"docker volume rm VOLUME_NAME\n",[61,2275,2273],{"__ignoreMap":80},[13,2277,2278,2279,2282],{},"そしてまた ",[61,2280,2281],{},"docker-compose up -d"," を行うことで最初からやり直しができます。",[27,2284,2285],{"id":2285},"意外と簡単でした",[13,2287,2288,2289,2291],{},"以上が旧CMSからwordpressにデータを挿入する方法です。wordpressは ",[61,2290,827],{}," を読み込めばほとんどの関数を使用でき、ターミナルからも実行できます。 スクリプト自体も100行未満で思いつける簡単なものです。",[13,2293,2294],{},"もしプログラム的にwordpressを操作したい場合は日本語だと上手く出てこないので「wordpress how to ~~~ programmatically」と調べるといいです。私が調べたものですと以下の感じです。",[42,2296,2297,2300,2303],{},[45,2298,2299],{},"wordpress how to create post programmatically",[45,2301,2302],{},"wordpress how to create user programmatically",[45,2304,2305],{},"wordpress how to set user role programmatically",[13,2307,2308],{},"ぶっちゃけ旧CMSからデータを引っ張ってきたり、適切に加工したり、構造を把握する方が大変でした。機会があればこのデータ移行の時に一番大変だった、正規表現により独自タグの置換も記事にしたいと思います。",[2310,2311,2312],"style",{},"html pre.shiki code .s-wAU, html code.shiki .s-wAU{--shiki-default:#F07178}html pre.shiki code .sAklC, html code.shiki .sAklC{--shiki-default:#89DDFF}html pre.shiki code .sfyAc, html code.shiki .sfyAc{--shiki-default:#C3E88D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .s0W1g, html code.shiki .s0W1g{--shiki-default:#BABED8}html pre.shiki code .sJ14y, html code.shiki .sJ14y{--shiki-default:#C792EA}html pre.shiki code .sx098, html code.shiki .sx098{--shiki-default:#F78C6C}",{"title":80,"searchDepth":235,"depth":235,"links":2314},[2315,2316,2317,2318,2319,2324,2328,2329,2330],{"id":29,"depth":228,"text":30},{"id":103,"depth":228,"text":103},{"id":137,"depth":228,"text":137},{"id":171,"depth":228,"text":171},{"id":503,"depth":228,"text":503,"children":2320},[2321,2322,2323],{"id":507,"depth":235,"text":507},{"id":585,"depth":235,"text":585},{"id":1083,"depth":235,"text":1083},{"id":1120,"depth":228,"text":1121,"children":2325},[2326,2327],{"id":1124,"depth":235,"text":1124},{"id":1677,"depth":235,"text":1677},{"id":1827,"depth":228,"text":1827},{"id":2265,"depth":228,"text":2266},{"id":2285,"depth":228,"text":2285},[2332],"devstack","2025-11-12","md",null,{},"\u002Farticles\u002Fmuch-post-migration-wp",{"title":8,"description":8},"articles\u002Fmuch-post-migration-wp",[2341,541],"wordpress","_mix\u002FWordPress-logotype-wmark-e1606319357320.png","XygCCNsuG0Q3ZTPUKENvFxlijb3Rx1MHJ5r8mRTS0sk",{"id":2345,"title":2346,"body":2347,"category":3980,"createdAt":3982,"description":2346,"extension":2334,"index":2335,"meta":3983,"navigation":231,"path":3984,"publish":231,"seo":3985,"series":2335,"seriesTitle":2335,"stem":3986,"tag":3987,"thumbnail":3990,"updatedAt":2335,"__hash__":3991},"articles\u002Farticles\u002Flaravel-i18n-json-dump.md","Laravelで多言語用のJSONを出力するコマンドを作る",{"type":10,"value":2348,"toc":3968},[2349,2364,2393,2400,2415,2428,2432,2435,2441,2510,2517,2520,2528,2531,2534,2541,2544,2547,2557,2563,2566,2569,2572,2578,2585,2777,2784,2790,2793,2796,2803,2964,2991,2994,2997,3095,3116,3120,3154,3169,3172,3187,3201,3204,3207,3520,3523,3536,3544,3549,3552,3757,3955,3962,3965],[13,2350,2351,2352,2355,2356,2359,2360,2363],{},"こんにちはjunです。個人開発で多言語対応のLaravelアプリを作っています。多言語では各種言語の単語・文章のマッピング（辞書）をする配列・JSONを作成し、レンダリング時にメソッドを使用して文字を表示します。Laravelでは",[61,2353,2354],{},"resources\u002Flang"," 配下に",[61,2357,2358],{},"en","、",[61,2361,2362],{},"ja","のようなディレクトリを作成し、その中に言語のマッピングをします。大体は以下のような配列を作ります。",[72,2365,2368],{"className":539,"code":2366,"filename":2367,"language":541,"meta":80,"style":80},"\u003C?php\nreturn [\n    'login'=>'ログイン',\n    'logout'=>'ログアウト'\n]\n\n","resources\u002Flang\u002Fja\u002Fwords.php",[61,2369,2370,2374,2379,2384,2389],{"__ignoreMap":80},[204,2371,2372],{"class":206,"line":207},[204,2373,726],{},[204,2375,2376],{"class":206,"line":228},[204,2377,2378],{},"return [\n",[204,2380,2381],{"class":206,"line":235},[204,2382,2383],{},"    'login'=>'ログイン',\n",[204,2385,2386],{"class":206,"line":244},[204,2387,2388],{},"    'logout'=>'ログアウト'\n",[204,2390,2391],{"class":206,"line":249},[204,2392,709],{},[13,2394,2395,2396,2399],{},"そしてビューファイルなどで",[61,2397,2398],{},"__('words.login')","のように使用します。",[2401,2402,2406,2407,2410,2411,2414],"div",{"className":2403},[2404,2405],"alert","alert-info","\n多言語のメソッドが",[61,2408,2409],{},"__","みたいな名称である理由としては、使いまくるので簡単な名称になっています。Vueとかでは",[61,2412,2413],{},"$t()","みたいなものを使用します。\n",[13,2416,2417,2418,2420,2421,2359,2424,2427],{},"上記のようなPHPファイルを作成してもできますが、",[61,2419,2354],{}," 直下に",[61,2422,2423],{},"en.json",[61,2425,2426],{},"ja.json","のようなJSONファイルを作成しても、多言語メソッドで呼び出せます。",[27,2429,2431],{"id":2430},"jsonの弱点","JSONの弱点",[13,2433,2434],{},"JSONで作るメリットはマッピングのデータを他のアプリでも利用できることです。例えば、Vue・ReactではVuei18n、react-i18nextなどを使用します。同じように多言語メソッドを使用します。その際のマッピングデータとしてJSONを使用します。であればJSONファイルを作っておくことで、Vueや外部へマッピングデータを提供しやすくなります。",[13,2436,2437,2438,2440],{},"ただしJSONで作成するとLaravel側で",[61,2439,2398],{},"のような呼び出しができません。この時JSONは以下のようになっています。",[72,2442,2444],{"className":591,"code":2443,"language":594,"meta":80,"style":80},"{\n    \"words\":{\n        \"login\":\"ログイン\",\n        \"logout\":\"ログアウト\"\n    }\n}\n",[61,2445,2446,2451,2463,2484,2502,2506],{"__ignoreMap":80},[204,2447,2448],{"class":206,"line":207},[204,2449,2450],{"class":214},"{\n",[204,2452,2453,2455,2458,2460],{"class":206,"line":228},[204,2454,617],{"class":214},[204,2456,2457],{"class":620},"words",[204,2459,624],{"class":214},[204,2461,2462],{"class":214},":{\n",[204,2464,2465,2467,2471,2473,2475,2477,2480,2482],{"class":206,"line":235},[204,2466,1152],{"class":214},[204,2468,2470],{"class":2469},"s5Dmg","login",[204,2472,624],{"class":214},[204,2474,215],{"class":214},[204,2476,624],{"class":214},[204,2478,2479],{"class":221},"ログイン",[204,2481,624],{"class":214},[204,2483,636],{"class":214},[204,2485,2486,2488,2491,2493,2495,2497,2500],{"class":206,"line":244},[204,2487,1152],{"class":214},[204,2489,2490],{"class":2469},"logout",[204,2492,624],{"class":214},[204,2494,215],{"class":214},[204,2496,624],{"class":214},[204,2498,2499],{"class":221},"ログアウト",[204,2501,695],{"class":214},[204,2503,2504],{"class":206,"line":249},[204,2505,785],{"class":214},[204,2507,2508],{"class":206,"line":257},[204,2509,804],{"class":214},[13,2511,2512,2513,2516],{},"すべて一次元にしてしまうと後で混乱してしまうので、カスケードさせておくと良いです。Vuei18nでは",[61,2514,2515],{},"$t('words.login')","で呼び出せますが、なぜかLaravelではJSONのカスケード配下のキーを呼び出すことができません。",[13,2518,2519],{},"そのため",[42,2521,2522,2525],{},[45,2523,2524],{},"Laravel以外のアプリへの提供→JSON",[45,2526,2527],{},"Laravelの多言語対応→PHP",[13,2529,2530],{},"という２重管理でそれぞれ設定しないといけなくなり、非効率的です。",[27,2532,2533],{"id":2533},"解決方法",[13,2535,2536,2537,2540],{},"解決方法としてはPHPで作成した配列をJSONにダンプすることです。そうすることでPHPファイルとJSONの言語ファイルを作成することができます。そこで今回の記事ではPHPの言語ファイルをカスケードさせたJSONに出力するような",[61,2538,2539],{},"artisan","コマンドを作ってみたいと思います。",[27,2542,2543],{"id":2543},"コマンドの実装",[505,2545,2546],{"id":2546},"対応する言語ディレクトリ",[13,2548,2549,2550,2355,2552,2359,2554,2556],{},"まずはLaravelの言語ファイルのドキュメントの通り、",[61,2551,2354],{},[61,2553,2358],{},[61,2555,2362],{},"のようなディレクトリを作成しておきましょう。今回は英語と日本語にしておきます。",[72,2558,2561],{"className":2559,"code":2560,"language":77},[75],"resources\n├── lang\n│   ├── en\n│   │   ├── auth.php\n│   │   ├── words.php\n│   │   └── exceptions.php\n│   └── ja\n│       ├── auth.php\n│       ├── words.php\n│       └── exceptions.php\n...\n",[61,2562,2560],{"__ignoreMap":80},[13,2564,2565],{},"そしてそのディレクトリごとにphpファイルを分けます。とりあえずこのようにしておきます。",[505,2567,2568],{"id":2568},"コマンドのファイルを作成",[13,2570,2571],{},"それではカスタムのコマンドを作成しましょう。",[72,2573,2576],{"className":2574,"code":2575,"language":77},[75],"php artisan make:command Dumplang\n",[61,2577,2575],{"__ignoreMap":80},[13,2579,2580,2581,2584],{},"コマンドもartisanで作成できます。",[61,2582,2583],{},"app\u002FConsole\u002FCommands","というディレクトリが作成され、そこにファイルが作成されます。",[72,2586,2588],{"className":539,"code":2587,"language":541,"meta":80,"style":80},"\u003C?php\n\nnamespace App\\Console\\Commands;\n\nuse Illuminate\\Console\\Command;\n\nclass Dumblang extends Command\n{\n    \u002F**\n     * The name and signature of the console command.\n     *\n     * @var string\n     *\u002F\n    protected $signature = 'lang:dump';\n\n    \u002F**\n     * The console command description.\n     *\n     * @var string\n     *\u002F\n    protected $description = 'Convert each php lang files to JSON files.';\n\n    \u002F**\n     * Create a new command instance.\n     *\n     * @return void\n     *\u002F\n    public function __construct()\n    {\n        parent::__construct();\n    }\n\n    \u002F**\n     * Execute the console command.\n     *\n     * @return int\n     *\u002F\n    public function handle()\n    {\n        \n    }\n}\n",[61,2589,2590,2594,2598,2603,2607,2612,2616,2621,2625,2630,2635,2640,2645,2650,2655,2659,2663,2668,2672,2676,2680,2685,2689,2693,2698,2702,2707,2711,2716,2720,2725,2729,2733,2737,2742,2746,2751,2755,2760,2764,2769,2773],{"__ignoreMap":80},[204,2591,2592],{"class":206,"line":207},[204,2593,726],{},[204,2595,2596],{"class":206,"line":228},[204,2597,232],{"emptyLinePlaceholder":231},[204,2599,2600],{"class":206,"line":235},[204,2601,2602],{},"namespace App\\Console\\Commands;\n",[204,2604,2605],{"class":206,"line":244},[204,2606,232],{"emptyLinePlaceholder":231},[204,2608,2609],{"class":206,"line":249},[204,2610,2611],{},"use Illuminate\\Console\\Command;\n",[204,2613,2614],{"class":206,"line":257},[204,2615,232],{"emptyLinePlaceholder":231},[204,2617,2618],{"class":206,"line":268},[204,2619,2620],{},"class Dumblang extends Command\n",[204,2622,2623],{"class":206,"line":279},[204,2624,2450],{},[204,2626,2627],{"class":206,"line":287},[204,2628,2629],{},"    \u002F**\n",[204,2631,2632],{"class":206,"line":296},[204,2633,2634],{},"     * The name and signature of the console command.\n",[204,2636,2637],{"class":206,"line":304},[204,2638,2639],{},"     *\n",[204,2641,2642],{"class":206,"line":315},[204,2643,2644],{},"     * @var string\n",[204,2646,2647],{"class":206,"line":326},[204,2648,2649],{},"     *\u002F\n",[204,2651,2652],{"class":206,"line":337},[204,2653,2654],{},"    protected $signature = 'lang:dump';\n",[204,2656,2657],{"class":206,"line":348},[204,2658,232],{"emptyLinePlaceholder":231},[204,2660,2661],{"class":206,"line":4},[204,2662,2629],{},[204,2664,2665],{"class":206,"line":363},[204,2666,2667],{},"     * The console command description.\n",[204,2669,2670],{"class":206,"line":368},[204,2671,2639],{},[204,2673,2674],{"class":206,"line":376},[204,2675,2644],{},[204,2677,2678],{"class":206,"line":386},[204,2679,2649],{},[204,2681,2682],{"class":206,"line":395},[204,2683,2684],{},"    protected $description = 'Convert each php lang files to JSON files.';\n",[204,2686,2687],{"class":206,"line":402},[204,2688,232],{"emptyLinePlaceholder":231},[204,2690,2691],{"class":206,"line":412},[204,2692,2629],{},[204,2694,2695],{"class":206,"line":422},[204,2696,2697],{},"     * Create a new command instance.\n",[204,2699,2700],{"class":206,"line":432},[204,2701,2639],{},[204,2703,2704],{"class":206,"line":447},[204,2705,2706],{},"     * @return void\n",[204,2708,2709],{"class":206,"line":454},[204,2710,2649],{},[204,2712,2713],{"class":206,"line":462},[204,2714,2715],{},"    public function __construct()\n",[204,2717,2718],{"class":206,"line":467},[204,2719,1147],{},[204,2721,2722],{"class":206,"line":475},[204,2723,2724],{},"        parent::__construct();\n",[204,2726,2727],{"class":206,"line":483},[204,2728,785],{},[204,2730,2731],{"class":206,"line":1413},[204,2732,232],{"emptyLinePlaceholder":231},[204,2734,2735],{"class":206,"line":1419},[204,2736,2629],{},[204,2738,2739],{"class":206,"line":1424},[204,2740,2741],{},"     * Execute the console command.\n",[204,2743,2744],{"class":206,"line":1429},[204,2745,2639],{},[204,2747,2748],{"class":206,"line":1434},[204,2749,2750],{},"     * @return int\n",[204,2752,2753],{"class":206,"line":1440},[204,2754,2649],{},[204,2756,2757],{"class":206,"line":1445},[204,2758,2759],{},"    public function handle()\n",[204,2761,2762],{"class":206,"line":1450},[204,2763,1147],{},[204,2765,2766],{"class":206,"line":1455},[204,2767,2768],{},"        \n",[204,2770,2771],{"class":206,"line":1460},[204,2772,785],{},[204,2774,2775],{"class":206,"line":1466},[204,2776,804],{},[13,2778,2779,2780,2783],{},"まずは上記のように、シグネチャと説明を記述します。シグネチャでは",[61,2781,2782],{},"lang:dump"," とすると",[72,2785,2788],{"className":2786,"code":2787,"language":77},[75],"php artisan lang:dump\n",[61,2789,2787],{"__ignoreMap":80},[13,2791,2792],{},"と入力するとこのコマンドを実行できます。",[505,2794,2795],{"id":2795},"言語ディレクトリからファイルを読み込む",[13,2797,2798,2799,2802],{},"コマンドの内容は",[61,2800,2801],{},"handle()","に記述します。全体は以下の通りです。",[72,2804,2806],{"className":539,"code":2805,"language":541,"meta":80,"style":80},"use Illuminate\\Support\\Facades\\File;\nuse Illuminate\\Support\\Facades\\Lang;\n\npublic function handle()\n{\n    $suports = [\"ja\",\"en\"];\n    \n    foreach($suports as $lng){\n        $langdir = resource_path('lang\u002F'.$lng);\n        if(is_dir($langdir)){\n            $files = scandir($langdir);\n            if($files === false) continue;\n\n            $files = array_filter($files,function($f){\n                return strpos($f,'.php') !== false;\n            });\n\n            $trans = [];\n            foreach($files as $f){\n                $content_key = str_replace('.php','',$f);\n                $content = include resource_path(\"lang\u002F$lng\u002F$f\");\n                $trans[$content_key] = $content;\n            }\n\n            $json = json_encode($trans,JSON_UNESCAPED_UNICODE);\n            File::put(resource_path('lang\u002F'.$lng.'.json'),$json);\n            File::put(base_path('nuxt\u002Flang\u002F'.$lng.'.json'),$json);\n        }else{\n            print(\"Lang directory for ${$lng} dose not exisits\");\n        }\n    }\n    return Command::SUCCESS;\n}\n",[61,2807,2808,2813,2818,2822,2827,2831,2836,2840,2845,2850,2855,2860,2865,2869,2874,2879,2884,2888,2893,2898,2903,2908,2913,2918,2922,2927,2932,2937,2942,2947,2951,2955,2960],{"__ignoreMap":80},[204,2809,2810],{"class":206,"line":207},[204,2811,2812],{},"use Illuminate\\Support\\Facades\\File;\n",[204,2814,2815],{"class":206,"line":228},[204,2816,2817],{},"use Illuminate\\Support\\Facades\\Lang;\n",[204,2819,2820],{"class":206,"line":235},[204,2821,232],{"emptyLinePlaceholder":231},[204,2823,2824],{"class":206,"line":244},[204,2825,2826],{},"public function handle()\n",[204,2828,2829],{"class":206,"line":249},[204,2830,2450],{},[204,2832,2833],{"class":206,"line":257},[204,2834,2835],{},"    $suports = [\"ja\",\"en\"];\n",[204,2837,2838],{"class":206,"line":268},[204,2839,765],{},[204,2841,2842],{"class":206,"line":279},[204,2843,2844],{},"    foreach($suports as $lng){\n",[204,2846,2847],{"class":206,"line":287},[204,2848,2849],{},"        $langdir = resource_path('lang\u002F'.$lng);\n",[204,2851,2852],{"class":206,"line":296},[204,2853,2854],{},"        if(is_dir($langdir)){\n",[204,2856,2857],{"class":206,"line":304},[204,2858,2859],{},"            $files = scandir($langdir);\n",[204,2861,2862],{"class":206,"line":315},[204,2863,2864],{},"            if($files === false) continue;\n",[204,2866,2867],{"class":206,"line":326},[204,2868,232],{"emptyLinePlaceholder":231},[204,2870,2871],{"class":206,"line":337},[204,2872,2873],{},"            $files = array_filter($files,function($f){\n",[204,2875,2876],{"class":206,"line":348},[204,2877,2878],{},"                return strpos($f,'.php') !== false;\n",[204,2880,2881],{"class":206,"line":4},[204,2882,2883],{},"            });\n",[204,2885,2886],{"class":206,"line":363},[204,2887,232],{"emptyLinePlaceholder":231},[204,2889,2890],{"class":206,"line":368},[204,2891,2892],{},"            $trans = [];\n",[204,2894,2895],{"class":206,"line":376},[204,2896,2897],{},"            foreach($files as $f){\n",[204,2899,2900],{"class":206,"line":386},[204,2901,2902],{},"                $content_key = str_replace('.php','',$f);\n",[204,2904,2905],{"class":206,"line":395},[204,2906,2907],{},"                $content = include resource_path(\"lang\u002F$lng\u002F$f\");\n",[204,2909,2910],{"class":206,"line":402},[204,2911,2912],{},"                $trans[$content_key] = $content;\n",[204,2914,2915],{"class":206,"line":412},[204,2916,2917],{},"            }\n",[204,2919,2920],{"class":206,"line":422},[204,2921,232],{"emptyLinePlaceholder":231},[204,2923,2924],{"class":206,"line":432},[204,2925,2926],{},"            $json = json_encode($trans,JSON_UNESCAPED_UNICODE);\n",[204,2928,2929],{"class":206,"line":447},[204,2930,2931],{},"            File::put(resource_path('lang\u002F'.$lng.'.json'),$json);\n",[204,2933,2934],{"class":206,"line":454},[204,2935,2936],{},"            File::put(base_path('nuxt\u002Flang\u002F'.$lng.'.json'),$json);\n",[204,2938,2939],{"class":206,"line":462},[204,2940,2941],{},"        }else{\n",[204,2943,2944],{"class":206,"line":467},[204,2945,2946],{},"            print(\"Lang directory for ${$lng} dose not exisits\");\n",[204,2948,2949],{"class":206,"line":475},[204,2950,1799],{},[204,2952,2953],{"class":206,"line":483},[204,2954,785],{},[204,2956,2957],{"class":206,"line":1413},[204,2958,2959],{},"    return Command::SUCCESS;\n",[204,2961,2962],{"class":206,"line":1419},[204,2963,804],{},[72,2965,2967],{"className":539,"code":2966,"language":541,"meta":80,"style":80},"$suports = [\"ja\",\"en\"];\n\nforeach($suports as $lng){\n\n}\n",[61,2968,2969,2974,2978,2983,2987],{"__ignoreMap":80},[204,2970,2971],{"class":206,"line":207},[204,2972,2973],{},"$suports = [\"ja\",\"en\"];\n",[204,2975,2976],{"class":206,"line":228},[204,2977,232],{"emptyLinePlaceholder":231},[204,2979,2980],{"class":206,"line":235},[204,2981,2982],{},"foreach($suports as $lng){\n",[204,2984,2985],{"class":206,"line":244},[204,2986,232],{"emptyLinePlaceholder":231},[204,2988,2989],{"class":206,"line":249},[204,2990,804],{},[13,2992,2993],{},"まずは取得予定の言語の配列を用意します。それを再帰的に処理します。",[13,2995,2996],{},"元となる言語ディレクトリからファイルの一覧を取得します。",[72,2998,3000],{"className":539,"code":2999,"language":541,"meta":80,"style":80},"foreach($suports as $lng){\n    $langdir = resource_path('lang\u002F'.$lng);\n    if(is_dir($langdir)){\n        $files = scandir($langdir);\n        if($files === false) continue;\n\n        $files = array_filter($files,function($f){\n            return strpos($f,'.php') !== false;\n        });\n\n        $trans = [];\n        foreach($files as $f){\n            $content_key = str_replace('.php','',$f);\n            $content = include resource_path(\"lang\u002F$lng\u002F$f\");\n            $trans[$content_key] = $content;\n        }\n\n        \u002F\u002F ....\n    }\n}\n",[61,3001,3002,3006,3011,3016,3021,3026,3030,3035,3040,3045,3049,3054,3059,3064,3069,3074,3078,3082,3087,3091],{"__ignoreMap":80},[204,3003,3004],{"class":206,"line":207},[204,3005,2982],{},[204,3007,3008],{"class":206,"line":228},[204,3009,3010],{},"    $langdir = resource_path('lang\u002F'.$lng);\n",[204,3012,3013],{"class":206,"line":235},[204,3014,3015],{},"    if(is_dir($langdir)){\n",[204,3017,3018],{"class":206,"line":244},[204,3019,3020],{},"        $files = scandir($langdir);\n",[204,3022,3023],{"class":206,"line":249},[204,3024,3025],{},"        if($files === false) continue;\n",[204,3027,3028],{"class":206,"line":257},[204,3029,232],{"emptyLinePlaceholder":231},[204,3031,3032],{"class":206,"line":268},[204,3033,3034],{},"        $files = array_filter($files,function($f){\n",[204,3036,3037],{"class":206,"line":279},[204,3038,3039],{},"            return strpos($f,'.php') !== false;\n",[204,3041,3042],{"class":206,"line":287},[204,3043,3044],{},"        });\n",[204,3046,3047],{"class":206,"line":296},[204,3048,232],{"emptyLinePlaceholder":231},[204,3050,3051],{"class":206,"line":304},[204,3052,3053],{},"        $trans = [];\n",[204,3055,3056],{"class":206,"line":315},[204,3057,3058],{},"        foreach($files as $f){\n",[204,3060,3061],{"class":206,"line":326},[204,3062,3063],{},"            $content_key = str_replace('.php','',$f);\n",[204,3065,3066],{"class":206,"line":337},[204,3067,3068],{},"            $content = include resource_path(\"lang\u002F$lng\u002F$f\");\n",[204,3070,3071],{"class":206,"line":348},[204,3072,3073],{},"            $trans[$content_key] = $content;\n",[204,3075,3076],{"class":206,"line":4},[204,3077,1799],{},[204,3079,3080],{"class":206,"line":363},[204,3081,232],{"emptyLinePlaceholder":231},[204,3083,3084],{"class":206,"line":368},[204,3085,3086],{},"        \u002F\u002F ....\n",[204,3088,3089],{"class":206,"line":376},[204,3090,785],{},[204,3092,3093],{"class":206,"line":386},[204,3094,804],{},[13,3096,3097,3100,3101,3104,3105,3107,3108,3111,3112,3115],{},[61,3098,3099],{},"resource_path('lang\u002F'.$lng)","で先程の言語ディレクトリのパスを取得し、",[61,3102,3103],{},"scandir()","を用いて内部のファイルを配列で取得します。",[61,3106,3103],{},"はphp以外のファイルや",[61,3109,3110],{},".","みたいなSELinuxが勝手に作る謎ファイルも読み取ってしまうので、",[61,3113,3114],{},"array_filter()","を用いてフィルターします。",[505,3117,3119],{"id":3118},"一つの配列に打ち込んでjson化する","一つの配列に打ち込んでJSON化する",[72,3121,3123],{"className":539,"code":3122,"language":541,"meta":80,"style":80},"$trans = [];\nforeach($files as $f){\n    $content_key = str_replace('.php','',$f);\n    $content = include resource_path(\"lang\u002F$lng\u002F$f\");\n    $trans[$content_key] = $content;\n}\n",[61,3124,3125,3130,3135,3140,3145,3150],{"__ignoreMap":80},[204,3126,3127],{"class":206,"line":207},[204,3128,3129],{},"$trans = [];\n",[204,3131,3132],{"class":206,"line":228},[204,3133,3134],{},"foreach($files as $f){\n",[204,3136,3137],{"class":206,"line":235},[204,3138,3139],{},"    $content_key = str_replace('.php','',$f);\n",[204,3141,3142],{"class":206,"line":244},[204,3143,3144],{},"    $content = include resource_path(\"lang\u002F$lng\u002F$f\");\n",[204,3146,3147],{"class":206,"line":249},[204,3148,3149],{},"    $trans[$content_key] = $content;\n",[204,3151,3152],{"class":206,"line":257},[204,3153,804],{},[13,3155,3156,3157,3160,3161,3164,3165,3168],{},"JSONではphpファイル名を一次キーとして利用したいので ",[61,3158,3159],{},"str_replace('.php','',$f)","でファイル名を取得します。",[61,3162,3163],{},"include resource_path(\"lang\u002F$lng\u002F$f\")","でphpファイルの記述を取得ます。そしてJSONにする配列に、ファイル名をキーとして打ち込みます。",[61,3166,3167],{},"$trans[$content_key] = $content;"," それをスキャンした言語PHPファイル全てに行います。",[505,3170,3171],{"id":3171},"ファイルを出力",[72,3173,3175],{"className":539,"code":3174,"language":541,"meta":80,"style":80},"$json = json_encode($trans,JSON_UNESCAPED_UNICODE);\nFile::put(resource_path('lang\u002F'.$lng.'.json'),$json);\n",[61,3176,3177,3182],{"__ignoreMap":80},[204,3178,3179],{"class":206,"line":207},[204,3180,3181],{},"$json = json_encode($trans,JSON_UNESCAPED_UNICODE);\n",[204,3183,3184],{"class":206,"line":228},[204,3185,3186],{},"File::put(resource_path('lang\u002F'.$lng.'.json'),$json);\n",[13,3188,3189,3190,3193,3194,3196,3197,3200],{},"そして１つにまとめた配列を",[61,3191,3192],{},"json_encode","をしておき、それを",[61,3195,2354],{}," 配下します。その際には",[61,3198,3199],{},"言語名.json","となるようにしておきます。",[27,3202,3203],{"id":3203},"全容と実行",[13,3205,3206],{},"コードは以下の通りです。",[72,3208,3210],{"className":539,"code":3209,"language":541,"meta":80,"style":80},"\u003C?php\n\nnamespace App\\Console\\Commands;\n\nuse Illuminate\\Console\\Command;\nuse Illuminate\\Support\\Facades\\File;\nuse Illuminate\\Support\\Facades\\Lang;\n\nclass Dumblang extends Command\n{\n    \u002F**\n     * The name and signature of the console command.\n     *\n     * @var string\n     *\u002F\n    protected $signature = 'lang:dump';\n\n    \u002F**\n     * The console command description.\n     *\n     * @var string\n     *\u002F\n    protected $description = 'Convert each php lang files to JSON files.';\n\n    \u002F**\n     * Create a new command instance.\n     *\n     * @return void\n     *\u002F\n    public function __construct()\n    {\n        parent::__construct();\n    }\n\n    \u002F**\n     * Execute the console command.\n     *\n     * @return int\n     *\u002F\n    public function handle()\n    {\n        $suports = config('app.support_langs');\n        \n        foreach($suports as $lng){\n            $langdir = resource_path('lang\u002F'.$lng);\n            if(is_dir($langdir)){\n                $files = scandir($langdir);\n                if($files === false) continue;\n\n                $files = array_filter($files,function($f){\n                    return strpos($f,'.php') !== false;\n                });\n\n                $trans = [];\n                foreach($files as $f){\n                    $content_key = str_replace('.php','',$f);\n                    $content = include resource_path(\"lang\u002F$lng\u002F$f\");\n                    $trans[$content_key] = $content;\n                }\n\n                $json = json_encode($trans,JSON_UNESCAPED_UNICODE);\n                File::put(resource_path('lang\u002F'.$lng.'.json'),$json);\n            }else{\n                print(\"Lang directory for ${$lng} dose not exisits\");\n            }\n        }\n        return Command::SUCCESS;\n    }\n}\n",[61,3211,3212,3216,3220,3224,3228,3232,3236,3240,3244,3248,3252,3256,3260,3264,3268,3272,3276,3280,3284,3288,3292,3296,3300,3304,3308,3312,3316,3320,3324,3328,3332,3336,3340,3344,3348,3352,3356,3360,3364,3368,3372,3376,3381,3385,3390,3395,3400,3405,3410,3414,3419,3424,3429,3433,3438,3443,3448,3453,3459,3465,3470,3476,3482,3488,3494,3499,3504,3510,3515],{"__ignoreMap":80},[204,3213,3214],{"class":206,"line":207},[204,3215,726],{},[204,3217,3218],{"class":206,"line":228},[204,3219,232],{"emptyLinePlaceholder":231},[204,3221,3222],{"class":206,"line":235},[204,3223,2602],{},[204,3225,3226],{"class":206,"line":244},[204,3227,232],{"emptyLinePlaceholder":231},[204,3229,3230],{"class":206,"line":249},[204,3231,2611],{},[204,3233,3234],{"class":206,"line":257},[204,3235,2812],{},[204,3237,3238],{"class":206,"line":268},[204,3239,2817],{},[204,3241,3242],{"class":206,"line":279},[204,3243,232],{"emptyLinePlaceholder":231},[204,3245,3246],{"class":206,"line":287},[204,3247,2620],{},[204,3249,3250],{"class":206,"line":296},[204,3251,2450],{},[204,3253,3254],{"class":206,"line":304},[204,3255,2629],{},[204,3257,3258],{"class":206,"line":315},[204,3259,2634],{},[204,3261,3262],{"class":206,"line":326},[204,3263,2639],{},[204,3265,3266],{"class":206,"line":337},[204,3267,2644],{},[204,3269,3270],{"class":206,"line":348},[204,3271,2649],{},[204,3273,3274],{"class":206,"line":4},[204,3275,2654],{},[204,3277,3278],{"class":206,"line":363},[204,3279,232],{"emptyLinePlaceholder":231},[204,3281,3282],{"class":206,"line":368},[204,3283,2629],{},[204,3285,3286],{"class":206,"line":376},[204,3287,2667],{},[204,3289,3290],{"class":206,"line":386},[204,3291,2639],{},[204,3293,3294],{"class":206,"line":395},[204,3295,2644],{},[204,3297,3298],{"class":206,"line":402},[204,3299,2649],{},[204,3301,3302],{"class":206,"line":412},[204,3303,2684],{},[204,3305,3306],{"class":206,"line":422},[204,3307,232],{"emptyLinePlaceholder":231},[204,3309,3310],{"class":206,"line":432},[204,3311,2629],{},[204,3313,3314],{"class":206,"line":447},[204,3315,2697],{},[204,3317,3318],{"class":206,"line":454},[204,3319,2639],{},[204,3321,3322],{"class":206,"line":462},[204,3323,2706],{},[204,3325,3326],{"class":206,"line":467},[204,3327,2649],{},[204,3329,3330],{"class":206,"line":475},[204,3331,2715],{},[204,3333,3334],{"class":206,"line":483},[204,3335,1147],{},[204,3337,3338],{"class":206,"line":1413},[204,3339,2724],{},[204,3341,3342],{"class":206,"line":1419},[204,3343,785],{},[204,3345,3346],{"class":206,"line":1424},[204,3347,232],{"emptyLinePlaceholder":231},[204,3349,3350],{"class":206,"line":1429},[204,3351,2629],{},[204,3353,3354],{"class":206,"line":1434},[204,3355,2741],{},[204,3357,3358],{"class":206,"line":1440},[204,3359,2639],{},[204,3361,3362],{"class":206,"line":1445},[204,3363,2750],{},[204,3365,3366],{"class":206,"line":1450},[204,3367,2649],{},[204,3369,3370],{"class":206,"line":1455},[204,3371,2759],{},[204,3373,3374],{"class":206,"line":1460},[204,3375,1147],{},[204,3377,3378],{"class":206,"line":1466},[204,3379,3380],{},"        $suports = config('app.support_langs');\n",[204,3382,3383],{"class":206,"line":1472},[204,3384,2768],{},[204,3386,3387],{"class":206,"line":1478},[204,3388,3389],{},"        foreach($suports as $lng){\n",[204,3391,3392],{"class":206,"line":1484},[204,3393,3394],{},"            $langdir = resource_path('lang\u002F'.$lng);\n",[204,3396,3397],{"class":206,"line":1490},[204,3398,3399],{},"            if(is_dir($langdir)){\n",[204,3401,3402],{"class":206,"line":1496},[204,3403,3404],{},"                $files = scandir($langdir);\n",[204,3406,3407],{"class":206,"line":1502},[204,3408,3409],{},"                if($files === false) continue;\n",[204,3411,3412],{"class":206,"line":1508},[204,3413,232],{"emptyLinePlaceholder":231},[204,3415,3416],{"class":206,"line":1513},[204,3417,3418],{},"                $files = array_filter($files,function($f){\n",[204,3420,3421],{"class":206,"line":1518},[204,3422,3423],{},"                    return strpos($f,'.php') !== false;\n",[204,3425,3426],{"class":206,"line":1524},[204,3427,3428],{},"                });\n",[204,3430,3431],{"class":206,"line":1529},[204,3432,232],{"emptyLinePlaceholder":231},[204,3434,3435],{"class":206,"line":1534},[204,3436,3437],{},"                $trans = [];\n",[204,3439,3440],{"class":206,"line":1539},[204,3441,3442],{},"                foreach($files as $f){\n",[204,3444,3445],{"class":206,"line":1544},[204,3446,3447],{},"                    $content_key = str_replace('.php','',$f);\n",[204,3449,3450],{"class":206,"line":1549},[204,3451,3452],{},"                    $content = include resource_path(\"lang\u002F$lng\u002F$f\");\n",[204,3454,3456],{"class":206,"line":3455},58,[204,3457,3458],{},"                    $trans[$content_key] = $content;\n",[204,3460,3462],{"class":206,"line":3461},59,[204,3463,3464],{},"                }\n",[204,3466,3468],{"class":206,"line":3467},60,[204,3469,232],{"emptyLinePlaceholder":231},[204,3471,3473],{"class":206,"line":3472},61,[204,3474,3475],{},"                $json = json_encode($trans,JSON_UNESCAPED_UNICODE);\n",[204,3477,3479],{"class":206,"line":3478},62,[204,3480,3481],{},"                File::put(resource_path('lang\u002F'.$lng.'.json'),$json);\n",[204,3483,3485],{"class":206,"line":3484},63,[204,3486,3487],{},"            }else{\n",[204,3489,3491],{"class":206,"line":3490},64,[204,3492,3493],{},"                print(\"Lang directory for ${$lng} dose not exisits\");\n",[204,3495,3497],{"class":206,"line":3496},65,[204,3498,2917],{},[204,3500,3502],{"class":206,"line":3501},66,[204,3503,1799],{},[204,3505,3507],{"class":206,"line":3506},67,[204,3508,3509],{},"        return Command::SUCCESS;\n",[204,3511,3513],{"class":206,"line":3512},68,[204,3514,785],{},[204,3516,3518],{"class":206,"line":3517},69,[204,3519,804],{},[13,3521,3522],{},"ちょっと気をつける点としては",[42,3524,3525,3531],{},[45,3526,3527,3530],{},[61,3528,3529],{},"is_dir($langdir)"," で言語ディレクトリの存在チェック",[45,3532,3533,3535],{},[61,3534,3103],{}," で取得したファイルをフィルタする",[13,3537,3538,3539,2359,3541,3543],{},"実行してみると",[61,3540,2426],{},[61,3542,2423],{},"というのが作成され、みてみると",[72,3545,3547],{"className":3546,"code":2560,"language":77},[75],[61,3548,2560],{"__ignoreMap":80},[13,3550,3551],{},"↓",[72,3553,3555],{"className":591,"code":3554,"filename":2426,"language":594,"meta":80,"style":80},"{\n    \"auth\":{\n        \"login\":\"ログイン\",\n        \"logout\":\"ログアウト\",\n    },\n    \"words\":{\n        \"save\":\"保存\",\n        \"update\":\"更新\"\n    },\n    \"exceptions\":{\n        \"401\":\"ログインしてください。\",\n        \"model\":{\n            \"403\":\"このデータにアクセスできません。\",\n            \"404\":\"対応するデータが見つかりません。\"\n        }\n    }\n}\n",[61,3556,3557,3561,3572,3590,3608,3612,3622,3642,3660,3664,3675,3695,3706,3727,3745,3749,3753],{"__ignoreMap":80},[204,3558,3559],{"class":206,"line":207},[204,3560,2450],{"class":214},[204,3562,3563,3565,3568,3570],{"class":206,"line":228},[204,3564,617],{"class":214},[204,3566,3567],{"class":620},"auth",[204,3569,624],{"class":214},[204,3571,2462],{"class":214},[204,3573,3574,3576,3578,3580,3582,3584,3586,3588],{"class":206,"line":235},[204,3575,1152],{"class":214},[204,3577,2470],{"class":2469},[204,3579,624],{"class":214},[204,3581,215],{"class":214},[204,3583,624],{"class":214},[204,3585,2479],{"class":221},[204,3587,624],{"class":214},[204,3589,636],{"class":214},[204,3591,3592,3594,3596,3598,3600,3602,3604,3606],{"class":206,"line":244},[204,3593,1152],{"class":214},[204,3595,2490],{"class":2469},[204,3597,624],{"class":214},[204,3599,215],{"class":214},[204,3601,624],{"class":214},[204,3603,2499],{"class":221},[204,3605,624],{"class":214},[204,3607,636],{"class":214},[204,3609,3610],{"class":206,"line":249},[204,3611,1969],{"class":214},[204,3613,3614,3616,3618,3620],{"class":206,"line":257},[204,3615,617],{"class":214},[204,3617,2457],{"class":620},[204,3619,624],{"class":214},[204,3621,2462],{"class":214},[204,3623,3624,3626,3629,3631,3633,3635,3638,3640],{"class":206,"line":268},[204,3625,1152],{"class":214},[204,3627,3628],{"class":2469},"save",[204,3630,624],{"class":214},[204,3632,215],{"class":214},[204,3634,624],{"class":214},[204,3636,3637],{"class":221},"保存",[204,3639,624],{"class":214},[204,3641,636],{"class":214},[204,3643,3644,3646,3649,3651,3653,3655,3658],{"class":206,"line":279},[204,3645,1152],{"class":214},[204,3647,3648],{"class":2469},"update",[204,3650,624],{"class":214},[204,3652,215],{"class":214},[204,3654,624],{"class":214},[204,3656,3657],{"class":221},"更新",[204,3659,695],{"class":214},[204,3661,3662],{"class":206,"line":287},[204,3663,1969],{"class":214},[204,3665,3666,3668,3671,3673],{"class":206,"line":296},[204,3667,617],{"class":214},[204,3669,3670],{"class":620},"exceptions",[204,3672,624],{"class":214},[204,3674,2462],{"class":214},[204,3676,3677,3679,3682,3684,3686,3688,3691,3693],{"class":206,"line":304},[204,3678,1152],{"class":214},[204,3680,3681],{"class":2469},"401",[204,3683,624],{"class":214},[204,3685,215],{"class":214},[204,3687,624],{"class":214},[204,3689,3690],{"class":221},"ログインしてください。",[204,3692,624],{"class":214},[204,3694,636],{"class":214},[204,3696,3697,3699,3702,3704],{"class":206,"line":315},[204,3698,1152],{"class":214},[204,3700,3701],{"class":2469},"model",[204,3703,624],{"class":214},[204,3705,2462],{"class":214},[204,3707,3708,3711,3714,3716,3718,3720,3723,3725],{"class":206,"line":326},[204,3709,3710],{"class":214},"            \"",[204,3712,3713],{"class":1054},"403",[204,3715,624],{"class":214},[204,3717,215],{"class":214},[204,3719,624],{"class":214},[204,3721,3722],{"class":221},"このデータにアクセスできません。",[204,3724,624],{"class":214},[204,3726,636],{"class":214},[204,3728,3729,3731,3734,3736,3738,3740,3743],{"class":206,"line":337},[204,3730,3710],{"class":214},[204,3732,3733],{"class":1054},"404",[204,3735,624],{"class":214},[204,3737,215],{"class":214},[204,3739,624],{"class":214},[204,3741,3742],{"class":221},"対応するデータが見つかりません。",[204,3744,695],{"class":214},[204,3746,3747],{"class":206,"line":348},[204,3748,1799],{"class":214},[204,3750,3751],{"class":206,"line":4},[204,3752,785],{"class":214},[204,3754,3755],{"class":206,"line":363},[204,3756,804],{"class":214},[72,3758,3760],{"className":591,"code":3759,"filename":2423,"language":594,"meta":80,"style":80},"{\n    \"auth\":{\n        \"login\":\"Login\",\n        \"logout\":\"Logout\",\n    },\n    \"words\":{\n        \"save\":\"Saving\",\n        \"update\":\"Updating\"\n    },\n    \"exceptions\":{\n        \"401\":\"Please login.\",\n        \"model\":{\n            \"403\":\"You can not access to this data.\",\n            \"404\":\"The data you request is not found.\"\n        }\n    }\n}\n",[61,3761,3762,3766,3776,3795,3814,3818,3828,3847,3864,3868,3878,3897,3907,3926,3943,3947,3951],{"__ignoreMap":80},[204,3763,3764],{"class":206,"line":207},[204,3765,2450],{"class":214},[204,3767,3768,3770,3772,3774],{"class":206,"line":228},[204,3769,617],{"class":214},[204,3771,3567],{"class":620},[204,3773,624],{"class":214},[204,3775,2462],{"class":214},[204,3777,3778,3780,3782,3784,3786,3788,3791,3793],{"class":206,"line":235},[204,3779,1152],{"class":214},[204,3781,2470],{"class":2469},[204,3783,624],{"class":214},[204,3785,215],{"class":214},[204,3787,624],{"class":214},[204,3789,3790],{"class":221},"Login",[204,3792,624],{"class":214},[204,3794,636],{"class":214},[204,3796,3797,3799,3801,3803,3805,3807,3810,3812],{"class":206,"line":244},[204,3798,1152],{"class":214},[204,3800,2490],{"class":2469},[204,3802,624],{"class":214},[204,3804,215],{"class":214},[204,3806,624],{"class":214},[204,3808,3809],{"class":221},"Logout",[204,3811,624],{"class":214},[204,3813,636],{"class":214},[204,3815,3816],{"class":206,"line":249},[204,3817,1969],{"class":214},[204,3819,3820,3822,3824,3826],{"class":206,"line":257},[204,3821,617],{"class":214},[204,3823,2457],{"class":620},[204,3825,624],{"class":214},[204,3827,2462],{"class":214},[204,3829,3830,3832,3834,3836,3838,3840,3843,3845],{"class":206,"line":268},[204,3831,1152],{"class":214},[204,3833,3628],{"class":2469},[204,3835,624],{"class":214},[204,3837,215],{"class":214},[204,3839,624],{"class":214},[204,3841,3842],{"class":221},"Saving",[204,3844,624],{"class":214},[204,3846,636],{"class":214},[204,3848,3849,3851,3853,3855,3857,3859,3862],{"class":206,"line":279},[204,3850,1152],{"class":214},[204,3852,3648],{"class":2469},[204,3854,624],{"class":214},[204,3856,215],{"class":214},[204,3858,624],{"class":214},[204,3860,3861],{"class":221},"Updating",[204,3863,695],{"class":214},[204,3865,3866],{"class":206,"line":287},[204,3867,1969],{"class":214},[204,3869,3870,3872,3874,3876],{"class":206,"line":296},[204,3871,617],{"class":214},[204,3873,3670],{"class":620},[204,3875,624],{"class":214},[204,3877,2462],{"class":214},[204,3879,3880,3882,3884,3886,3888,3890,3893,3895],{"class":206,"line":304},[204,3881,1152],{"class":214},[204,3883,3681],{"class":2469},[204,3885,624],{"class":214},[204,3887,215],{"class":214},[204,3889,624],{"class":214},[204,3891,3892],{"class":221},"Please login.",[204,3894,624],{"class":214},[204,3896,636],{"class":214},[204,3898,3899,3901,3903,3905],{"class":206,"line":315},[204,3900,1152],{"class":214},[204,3902,3701],{"class":2469},[204,3904,624],{"class":214},[204,3906,2462],{"class":214},[204,3908,3909,3911,3913,3915,3917,3919,3922,3924],{"class":206,"line":326},[204,3910,3710],{"class":214},[204,3912,3713],{"class":1054},[204,3914,624],{"class":214},[204,3916,215],{"class":214},[204,3918,624],{"class":214},[204,3920,3921],{"class":221},"You can not access to this data.",[204,3923,624],{"class":214},[204,3925,636],{"class":214},[204,3927,3928,3930,3932,3934,3936,3938,3941],{"class":206,"line":337},[204,3929,3710],{"class":214},[204,3931,3733],{"class":1054},[204,3933,624],{"class":214},[204,3935,215],{"class":214},[204,3937,624],{"class":214},[204,3939,3940],{"class":221},"The data you request is not found.",[204,3942,695],{"class":214},[204,3944,3945],{"class":206,"line":348},[204,3946,1799],{"class":214},[204,3948,3949],{"class":206,"line":4},[204,3950,785],{"class":214},[204,3952,3953],{"class":206,"line":363},[204,3954,804],{"class":214},[13,3956,3957,3958,3961],{},"これでPHPファイルだけでJSONの言語ファイルも作成して、フロントでのVueの言語ファイルなどに提供することができるようになります。動的にこのJSONファイルは生成するので、バージョン管理をするときは",[61,3959,3960],{},".gitignore","で指定しておくといいです。そしてデプロイや更新時にはこのコマンドを打ち忘れないようにしましょう。",[13,3963,3964],{},"ちなみにですが、実際にやっていることは特定のディレクトリのPHPファイルの内容を取得して、それをJSONにして生成したファイルを置いているだけなので素のPHP、pythonやrubyとか他の言語でも行けると思いますよ。",[2310,3966,3967],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sAklC, html code.shiki .sAklC{--shiki-default:#89DDFF}html pre.shiki code .sJ14y, html code.shiki .sJ14y{--shiki-default:#C792EA}html pre.shiki code .s5Dmg, html code.shiki .s5Dmg{--shiki-default:#FFCB6B}html pre.shiki code .sfyAc, html code.shiki .sfyAc{--shiki-default:#C3E88D}html pre.shiki code .sx098, html code.shiki .sx098{--shiki-default:#F78C6C}",{"title":80,"searchDepth":235,"depth":235,"links":3969},[3970,3971,3972,3979],{"id":2430,"depth":228,"text":2431},{"id":2533,"depth":228,"text":2533},{"id":2543,"depth":228,"text":2543,"children":3973},[3974,3975,3976,3977,3978],{"id":2546,"depth":235,"text":2546},{"id":2568,"depth":235,"text":2568},{"id":2795,"depth":235,"text":2795},{"id":3118,"depth":235,"text":3119},{"id":3171,"depth":235,"text":3171},{"id":3203,"depth":228,"text":3203},[3981],"ministack","2022-05-22",{},"\u002Farticles\u002Flaravel-i18n-json-dump",{"title":2346,"description":2346},"articles\u002Flaravel-i18n-json-dump",[541,3988,3989],"laravel","js","_common\u002Flaravel.png","tdUMQGHoIH7SI9TXJlID3MGccNOOjq6udWMjq_HTqC0",{"id":3993,"title":3994,"body":3995,"category":4465,"createdAt":4466,"description":4467,"extension":2334,"index":2335,"meta":4468,"navigation":231,"path":4469,"publish":231,"seo":4470,"series":2335,"seriesTitle":2335,"stem":4471,"tag":4472,"thumbnail":3990,"updatedAt":2335,"__hash__":4473},"articles\u002Farticles\u002Flaravel-custom-faker.md","Laravelでカスタムなフェイカーを作成する。",{"type":10,"value":3996,"toc":4456},[3997,4000,4009,4012,4032,4035,4038,4042,4056,4062,4094,4101,4114,4117,4124,4177,4180,4183,4186,4189,4195,4342,4360,4363,4372,4434,4441,4444,4447,4453],[13,3998,3999],{},"こんにちはjunです。Laravelはフルスタックフレームワークと言われるほど開発者に嬉しい機能が揃っています。その中でFakerと呼ばれるダミーデータを挿入する機能はよく使用します。ページングやいろんな文字を入れてみて、ビュー側やロジックなどが問題ないかを確かめることができるので、効率的な開発には必要不可欠です。",[13,4001,4002,4003,4008],{},"Fakerは標準で英語ですが、設定によって日本語にすることができます。FakerはPHPfakerというDevライブラリを使用しており、",[176,4004,4007],{"href":4005,"rel":4006},"https:\u002F\u002Ffakerphp.github.io\u002Fformatters\u002Fnumbers-and-strings\u002F",[180],"PHP Faker Formatters","で使用できるFakerの一覧を見れます。これらのFakerはおもにFactoryで使用します。",[13,4010,4011],{},"例えば氏名、メールアドレス、文章などは以下の通りです。",[72,4013,4015],{"className":539,"code":4014,"language":541,"meta":80,"style":80},"$this->faker->name();\n$this->faker->email();\n$this->faker->realText();\n",[61,4016,4017,4022,4027],{"__ignoreMap":80},[204,4018,4019],{"class":206,"line":207},[204,4020,4021],{},"$this->faker->name();\n",[204,4023,4024],{"class":206,"line":228},[204,4025,4026],{},"$this->faker->email();\n",[204,4028,4029],{"class":206,"line":235},[204,4030,4031],{},"$this->faker->realText();\n",[13,4033,4034],{},"上記の通りそれっぽいダミーデータを作れるのですが、時たまにアプリで必要なデータ形式のFakerがほしかったり、ランダム・特定条件のマスターのIDを出してほしい、もう少し実装するサービスに即した内容を出してほしいと言った要望がある場合はFakerを自作する必要があります。",[13,4036,4037],{},"今回はそのカスタムFakerの実装を解説したいと思います。",[27,4039,4041],{"id":4040},"カスタムfakerのクラスを作成","カスタムFakerのクラスを作成",[13,4043,4044,4045,4048,4049,4052,4053,4055],{},"最初にカスタムFakerのクラスを作成します。",[61,4046,4047],{},"App\u002FFaker\u002FCustome.php","を作成します。今回は",[61,4050,4051],{},"Custome.php","に実装したいFakerを作成しますが、もしクラスごとにわたい場合は",[61,4054,4051],{},"をFakerごとのクラスに分けてください。",[13,4057,4058,4059,4061],{},"そして",[61,4060,4051],{},"は以下のように記述します。",[72,4063,4065],{"className":539,"code":4064,"filename":4047,"language":541,"meta":80,"style":80},"namespace App\\Faker;\nuse Faker\\Provider\\Base;\n\nclass Custome extends Base{\n\n}\n\n",[61,4066,4067,4072,4077,4081,4086,4090],{"__ignoreMap":80},[204,4068,4069],{"class":206,"line":207},[204,4070,4071],{},"namespace App\\Faker;\n",[204,4073,4074],{"class":206,"line":228},[204,4075,4076],{},"use Faker\\Provider\\Base;\n",[204,4078,4079],{"class":206,"line":235},[204,4080,232],{"emptyLinePlaceholder":231},[204,4082,4083],{"class":206,"line":244},[204,4084,4085],{},"class Custome extends Base{\n",[204,4087,4088],{"class":206,"line":249},[204,4089,232],{"emptyLinePlaceholder":231},[204,4091,4092],{"class":206,"line":257},[204,4093,804],{},[13,4095,4096,4097,4100],{},"ここでは",[61,4098,4099],{},"Faker\\Provider\\Base","を継承させてください。Fakerの元ファイルもFakerメソッドを定義しているクラスで継承しています。",[13,4102,4103,4105,4106,4109,4110,4113],{},[61,4104,4099],{},"からは",[61,4107,4108],{},"randomElements()","といったランダムな配列を取得する、任意範囲の数字を取得する",[61,4111,4112],{},"numberBetween()","といった便利なメソッドを使用できます。",[505,4115,4116],{"id":4116},"フェイカーの書き方",[13,4118,4119,4120,4123],{},"例えば、食品の名前を出してくれる",[61,4121,4122],{},"foodname()","というFakerを作ってみるとします。",[72,4125,4127],{"className":539,"code":4126,"filename":4047,"language":541,"meta":80,"style":80},"namespace App\\Faker;\nuse Faker\\Provider\\Base;\n\nclass Custome extends Base{\n\n     protected static $foodName = ['ラーメン','パスタ','おにぎり','パン','炒飯']\n\n     public function foodname(){\n        return static::randomElement(static::$foodName);\n     }\n}\n\n",[61,4128,4129,4133,4137,4141,4145,4149,4154,4158,4163,4168,4173],{"__ignoreMap":80},[204,4130,4131],{"class":206,"line":207},[204,4132,4071],{},[204,4134,4135],{"class":206,"line":228},[204,4136,4076],{},[204,4138,4139],{"class":206,"line":235},[204,4140,232],{"emptyLinePlaceholder":231},[204,4142,4143],{"class":206,"line":244},[204,4144,4085],{},[204,4146,4147],{"class":206,"line":249},[204,4148,232],{"emptyLinePlaceholder":231},[204,4150,4151],{"class":206,"line":257},[204,4152,4153],{},"     protected static $foodName = ['ラーメン','パスタ','おにぎり','パン','炒飯']\n",[204,4155,4156],{"class":206,"line":268},[204,4157,232],{"emptyLinePlaceholder":231},[204,4159,4160],{"class":206,"line":279},[204,4161,4162],{},"     public function foodname(){\n",[204,4164,4165],{"class":206,"line":287},[204,4166,4167],{},"        return static::randomElement(static::$foodName);\n",[204,4169,4170],{"class":206,"line":296},[204,4171,4172],{},"     }\n",[204,4174,4175],{"class":206,"line":304},[204,4176,804],{},[13,4178,4179],{},"このような感じで、配列に静的なプロパティを作成します。そしてメソッドでそのリストからランダムで呼び出す処理を実装すれば大丈夫です。DB上にあるマスターなどを使用したい場合、DBと接続して取得してもいいと思います。",[13,4181,4182],{},"引数を設定できるので、特定の食品だけ取り出すみたいな処理を加えていいでしょう。まずカスタムFakerメソッドを実装できたので、実際に使えるようにします。",[27,4184,4185],{"id":4185},"サービスプロバイダを作成",[13,4187,4188],{},"Laravelの標準のFakerはサービスプロバイダでシングルトンとして登録されています。それをオーバーライドするような処理を行っています。",[13,4190,4191,4194],{},[61,4192,4193],{},"App\u002FProviders\u002FFakerServiceProvider.php","配下にサービスプロバイダを作成します。",[72,4196,4198],{"className":539,"code":4197,"filename":4193,"language":541,"meta":80,"style":80},"\nnamespace App\\Providers;\nuse Faker\\{Factory, Generator};\nuse Illuminate\\Support\\ServiceProvider;\nuse App\\Faker\\Custome;\n\nclass FakerServiceProvider extends ServiceProvider\n{\n    \u002F**\n     * Register services.\n     *\n     * @return void\n     *\u002F\n    public function register()\n    {\n        $this->app->singleton(Generator::class, function () {\n            $faker = Factory::create(config('app.faker_locale'));\n            $faker->addProvider(new Custome($faker));\n            return $faker;\n        });\n    }\n\n    \u002F**\n     * Bootstrap services.\n     *\n     * @return void\n     *\u002F\n    public function boot()\n    {\n        \u002F\u002F\n    }\n}\n",[61,4199,4200,4204,4209,4214,4219,4224,4228,4233,4237,4241,4246,4250,4254,4258,4263,4267,4272,4277,4282,4287,4291,4295,4299,4303,4308,4312,4316,4320,4325,4329,4334,4338],{"__ignoreMap":80},[204,4201,4202],{"class":206,"line":207},[204,4203,232],{"emptyLinePlaceholder":231},[204,4205,4206],{"class":206,"line":228},[204,4207,4208],{},"namespace App\\Providers;\n",[204,4210,4211],{"class":206,"line":235},[204,4212,4213],{},"use Faker\\{Factory, Generator};\n",[204,4215,4216],{"class":206,"line":244},[204,4217,4218],{},"use Illuminate\\Support\\ServiceProvider;\n",[204,4220,4221],{"class":206,"line":249},[204,4222,4223],{},"use App\\Faker\\Custome;\n",[204,4225,4226],{"class":206,"line":257},[204,4227,232],{"emptyLinePlaceholder":231},[204,4229,4230],{"class":206,"line":268},[204,4231,4232],{},"class FakerServiceProvider extends ServiceProvider\n",[204,4234,4235],{"class":206,"line":279},[204,4236,2450],{},[204,4238,4239],{"class":206,"line":287},[204,4240,2629],{},[204,4242,4243],{"class":206,"line":296},[204,4244,4245],{},"     * Register services.\n",[204,4247,4248],{"class":206,"line":304},[204,4249,2639],{},[204,4251,4252],{"class":206,"line":315},[204,4253,2706],{},[204,4255,4256],{"class":206,"line":326},[204,4257,2649],{},[204,4259,4260],{"class":206,"line":337},[204,4261,4262],{},"    public function register()\n",[204,4264,4265],{"class":206,"line":348},[204,4266,1147],{},[204,4268,4269],{"class":206,"line":4},[204,4270,4271],{},"        $this->app->singleton(Generator::class, function () {\n",[204,4273,4274],{"class":206,"line":363},[204,4275,4276],{},"            $faker = Factory::create(config('app.faker_locale'));\n",[204,4278,4279],{"class":206,"line":368},[204,4280,4281],{},"            $faker->addProvider(new Custome($faker));\n",[204,4283,4284],{"class":206,"line":376},[204,4285,4286],{},"            return $faker;\n",[204,4288,4289],{"class":206,"line":386},[204,4290,3044],{},[204,4292,4293],{"class":206,"line":395},[204,4294,785],{},[204,4296,4297],{"class":206,"line":402},[204,4298,232],{"emptyLinePlaceholder":231},[204,4300,4301],{"class":206,"line":412},[204,4302,2629],{},[204,4304,4305],{"class":206,"line":422},[204,4306,4307],{},"     * Bootstrap services.\n",[204,4309,4310],{"class":206,"line":432},[204,4311,2639],{},[204,4313,4314],{"class":206,"line":447},[204,4315,2706],{},[204,4317,4318],{"class":206,"line":454},[204,4319,2649],{},[204,4321,4322],{"class":206,"line":462},[204,4323,4324],{},"    public function boot()\n",[204,4326,4327],{"class":206,"line":467},[204,4328,1147],{},[204,4330,4331],{"class":206,"line":475},[204,4332,4333],{},"        \u002F\u002F\n",[204,4335,4336],{"class":206,"line":483},[204,4337,785],{},[204,4339,4340],{"class":206,"line":1413},[204,4341,804],{},[13,4343,4344,4347,4348,4351,4352,4355,4356,4359],{},[61,4345,4346],{},"Factory::create(config('app.faker_locale'))","としておくと、",[61,4349,4350],{},"config\u002Fapp.php","の",[61,4353,4354],{},"faker_locale","を用いて日本語化できます。そしてFakerのインスタンスに",[61,4357,4358],{},"addProvider()","を使用して、作成したカスタムフェイカーを追加します。",[505,4361,4362],{"id":4362},"サービスプロバイダの登録",[13,4364,4365,4366,4351,4368,4371],{},"このサービスプロバイダを",[61,4367,4350],{},[61,4369,4370],{},"providers"," に追加します。",[72,4373,4375],{"className":539,"code":4374,"filename":4350,"language":541,"meta":80,"style":80},"'providers' => [\n\u002F*\n* Laravel Framework Service Providers...\n*\u002F\n\u002F\u002F 省略\n\n\u002F*\n* Application Service Providers...\n　追加\n*\u002F\n    App\\Providers\\FakerServiceProvider::class,\n],\n",[61,4376,4377,4382,4387,4392,4397,4402,4406,4410,4415,4420,4424,4429],{"__ignoreMap":80},[204,4378,4379],{"class":206,"line":207},[204,4380,4381],{},"'providers' => [\n",[204,4383,4384],{"class":206,"line":228},[204,4385,4386],{},"\u002F*\n",[204,4388,4389],{"class":206,"line":235},[204,4390,4391],{},"* Laravel Framework Service Providers...\n",[204,4393,4394],{"class":206,"line":244},[204,4395,4396],{},"*\u002F\n",[204,4398,4399],{"class":206,"line":249},[204,4400,4401],{},"\u002F\u002F 省略\n",[204,4403,4404],{"class":206,"line":257},[204,4405,232],{"emptyLinePlaceholder":231},[204,4407,4408],{"class":206,"line":268},[204,4409,4386],{},[204,4411,4412],{"class":206,"line":279},[204,4413,4414],{},"* Application Service Providers...\n",[204,4416,4417],{"class":206,"line":287},[204,4418,4419],{},"　追加\n",[204,4421,4422],{"class":206,"line":296},[204,4423,4396],{},[204,4425,4426],{"class":206,"line":304},[204,4427,4428],{},"    App\\Providers\\FakerServiceProvider::class,\n",[204,4430,4431],{"class":206,"line":315},[204,4432,4433],{},"],\n",[13,4435,4436,4437,4440],{},"そうするとfakerにて",[61,4438,4439],{},"$this->faker->foodname()","でランダムな食品名が出てきます。",[27,4442,4443],{"id":4443},"すぐに検証したい場合の方法",[13,4445,4446],{},"上記のFakerはFactoryなどで使用できますが、Tinkerなどですぐに確かめたいということがあると思います。そんな時はTinkerで以下のようにFakerのインスタンスを生成して、チェックできます。なお上記のサービスプロバイダーを登録している必要があります。",[72,4448,4451],{"className":4449,"code":4450,"language":77},[75],"php artian tinker\n>> $faker = app()->make(Faker\\Generator::class)\n>> $faker->foodname()\n=>'パスタ'\n",[61,4452,4450],{"__ignoreMap":80},[2310,4454,4455],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":80,"searchDepth":235,"depth":235,"links":4457},[4458,4461,4464],{"id":4040,"depth":228,"text":4041,"children":4459},[4460],{"id":4116,"depth":235,"text":4116},{"id":4185,"depth":228,"text":4185,"children":4462},[4463],{"id":4362,"depth":235,"text":4362},{"id":4443,"depth":228,"text":4443},[3981],"2022-04-02","Laravelでのカスタムフェイカーの作り方",{},"\u002Farticles\u002Flaravel-custom-faker",{"title":3994,"description":4467},"articles\u002Flaravel-custom-faker",[541,3988],"J5_Trsafdn5yWViWjr3IsUw74QmcNr3WsF_8_DUEepU",{"id":4475,"title":4476,"body":4477,"category":4941,"createdAt":4942,"description":4943,"extension":2334,"index":2335,"meta":4944,"navigation":231,"path":4945,"publish":231,"seo":4946,"series":2335,"seriesTitle":2335,"stem":4947,"tag":4948,"thumbnail":3990,"updatedAt":2335,"__hash__":4949},"articles\u002Farticles\u002Flaravel-protect-resource.md","Laravelでログインしたユーザーのみ読み取れる画像・アセットを設定する方法",{"type":10,"value":4478,"toc":4934},[4479,4482,4493,4495,4503,4509,4512,4515,4523,4526,4547,4575,4578,4584,4587,4590,4619,4622,4636,4643,4646,4653,4656,4663,4692,4699,4726,4729,4732,4798,4811,4838,4853,4870,4873,4876,4879,4885,4919,4929,4932],[13,4480,4481],{},"こんにちはjunです。Laravel製のシステムにてWebマニュアルを作成していた時、「あれ？マニュルはログインユーザーのみ見れる様にするけど、画像などはどうすればいいんだ？」という事態がありました。",[13,4483,4484,4485,4488,4489,4492],{},"Laravelはルートを定義し、その際に認証を設けることができます。ただし静的な画像（今回の様なあらかじめセットしておくマニュアル画像など）を配置する場合は",[61,4486,4487],{},"public","配下または、",[61,4490,4491],{},"storage\u002Fpublic","配下に置くことが多いと思います。しかしそれらのディレクトリは名の通りいかなるアクセスに対してリクエストを許可しています。",[13,4494,2519],{},[42,4496,4497,4500],{},[45,4498,4499],{},"ログインをしないと見れない画像やアセット",[45,4501,4502],{},"アクセスを制限したい画像やアセット",[13,4504,4505,4506,4508],{},"を実装したい時は単純に",[61,4507,4487],{},"配下に置くことはできません。この場合Laravelではコントローラーを使用して、アセットのリクエストに対して一度認証のロジックをかける必要があります。普段Laravelを使用していると、特定のURLとそのビューに対する認証はルートを定義するだけで簡単に設定できます。しかしビュー以外のアセットファイルの場合はWebサーバーとLaravelの仕組みを少し理解している必要ががります。今回はその様な保護したアセットルートの設定方法を解説しようと思います。",[27,4510,4511],{"id":4511},"概要",[13,4513,4514],{},"Laravelで構築されたURLで指定のビューやファイルをレスポンスとして返す時２通りの処理方法があります。",[108,4516,4517,4520],{},[45,4518,4519],{},"ドキュメントルート配下にリクエストで示されたファイルがある場合、それを返す。（webサーバー）",[45,4521,4522],{},"ドキュメントルートにない場合、Route.phpで定義したルートを照らし合わせ、設定したビューやファイルを返す。（webサーバー+PHP）",[13,4524,4525],{},"「２」の方はよくわかると思います。例えば以下の様なルートを定義した時",[72,4527,4530],{"className":539,"code":4528,"filename":4529,"language":541,"meta":80,"style":80},"Route::get('\u002Ftest', function () {\n    return view('welcome');\n});\n","route.php",[61,4531,4532,4537,4542],{"__ignoreMap":80},[204,4533,4534],{"class":206,"line":207},[204,4535,4536],{},"Route::get('\u002Ftest', function () {\n",[204,4538,4539],{"class":206,"line":228},[204,4540,4541],{},"    return view('welcome');\n",[204,4543,4544],{"class":206,"line":235},[204,4545,4546],{},"});\n",[13,4548,4549,4552,4553,4556,4557,4559,4560,4563,4564,4566,4567,4570,4571,4574],{},[61,4550,4551],{},"https:\u002F\u002Fexample.com\u002Ftest","というURLにアクセスすると",[61,4554,4555],{},"view('welcome')","で定義したHTML（画面）がレスポンスとして表示されます。一方「１」の方はというと例えば",[61,4558,4487],{},"配下に置いた",[61,4561,4562],{},"css",",",[61,4565,3989],{},"など静的なファイルがあげられます。例えば",[61,4568,4569],{},"https:\u002F\u002Fexample.com\u002Fcss\u002Fstyle.css","の場合、webサーバーは",[61,4572,4573],{},"public\u002Fcss\u002Fstyle.css","があればそれをレスポンスとして返します。",[13,4576,4577],{},"両者の違いはwebサーバーだけで完結しているか、PHP（Laravel）も動かしているかです。これはpublic配下の.htaccessを見ると理解できます。",[72,4579,4582],{"className":4580,"code":4581,"filename":535,"language":77,"meta":80},[75],"\u003CIfModule mod_rewrite.c>\n    # Send Requests To Front Controller...\n    RewriteCond %{REQUEST_FILENAME} !-d\n    RewriteCond %{REQUEST_FILENAME} !-f\n    RewriteRule ^ index.php [L]\n\u003C\u002FIfModule>\n",[61,4583,4581],{"__ignoreMap":80},[13,4585,4586],{},"一部省略していますが、重要なのはこの箇所です。これは「もし、リクエストしたディレクトリおよびファイルがない場合、index.phpを実行する」という意味です。つまりLaravelが置かれたwebサーバーでは、まず「リクエストされたファイルが静的に置かれているかをチェック」そしてもしない場合は「index.phpを実行してLaravelが動的にルートに対するレスポンスを作成する」ということが行われています。",[13,4588,4589],{},"Apache側で静的ファイルに対するアクセスを設定していない場合、基本的にリクエストに合致するファイルがある場合はレスポンスしてしまいます。今回の様な保護したファイル、つまりLaravelの認証などを通してファイルを返すためには、独自のルートを定義してレスポンスする必要があります。",[2401,4591,4593,4594,4597,4598,4601,4602,4604,4605,4608,4609],{"className":4592},[2404,2405],"\nLaravelではアップロードされたファイルは",[61,4595,4596],{},"storage\u002Fapp\u002Fpublic"," 配下に配置し、そのstorageファイルへのリクエストは上記のwebサーバーのみで処理できます。storageディレクトリがpublicとは別なのになぜできるのか？それはシンボリックリンクを張っているからです。構築時に",[61,4599,4600],{},"php artisan storage:link","というおまじないを唱えたと思います。これはpublic配下に",[61,4603,4596],{},"に連絡する",[61,4606,4607],{},"storage","というシンボリックリンクを配置する処理を行っています。\n",[13,4610,4611,4612,4615,4616,4618],{},"実際にpublic配下にstorageというものがあり、Vscodeでは矢印マークが加わっているのが分かります。ls -lを実行してみると",[61,4613,4614],{},"storage -> \u002Fvar\u002Fwww\u002Fhtml\u002Fstorage\u002Fapp\u002Fpublic","という風に表示されます（私の環境の場合）。ディレクトリとして離れていても、シンボリックリンクを貼ることで",[61,4617,4596],{},"配下をwebサーバーが走査することができる様にしています。",[13,4620,4621],{},"今回のような保護したアセットファイルルートを設定するために",[108,4623,4624,4627,4630,4633],{},[45,4625,4626],{},"専用のディレクトリを作成",[45,4628,4629],{},"読み取りルートの定義",[45,4631,4632],{},"ファイルの取得処理",[45,4634,4635],{},"レスポンス処理",[13,4637,4638,4639,4642],{},"上記のプログラムを作成します。今回は「ログインしたユーザーが見れるwebマニュアルの画像」ということなので、",[61,4640,4641],{},"resources","配下にファイルを置いておくことにします。一応後でstorageディレクトリに保護ファイルを配置・取得する方法も記述します。",[27,4644,4645],{"id":4645},"ディレクトリの作成",[13,4647,4648,4649,4652],{},"まずは専用のディレクトリを作成します。今回は静的に置いておくので",[61,4650,4651],{},"resources\u002Fprotected","という保護アセットファイルディレクトリを作っておきます。リクエストがあった場合はこのディレクトリからファイルを取得します。",[27,4654,4655],{"id":4655},"ルートを定義する",[13,4657,4658,4659,4662],{},"それではルートを定義します。",[61,4660,4661],{},"routes\u002Fweb.php","にて以下の様なルートを設定。",[72,4664,4666],{"className":539,"code":4665,"filename":4661,"language":541,"meta":80,"style":80},"Route::group(['middleware' => 'auth'], function () {\n    Route::get('\u002Fprotected\u002F{path?}', function (Request $request,$path='') {\n        \u002F\u002F 後で書きます..\n    })->where('path', '.*');\n});\n",[61,4667,4668,4673,4678,4683,4688],{"__ignoreMap":80},[204,4669,4670],{"class":206,"line":207},[204,4671,4672],{},"Route::group(['middleware' => 'auth'], function () {\n",[204,4674,4675],{"class":206,"line":228},[204,4676,4677],{},"    Route::get('\u002Fprotected\u002F{path?}', function (Request $request,$path='') {\n",[204,4679,4680],{"class":206,"line":235},[204,4681,4682],{},"        \u002F\u002F 後で書きます..\n",[204,4684,4685],{"class":206,"line":244},[204,4686,4687],{},"    })->where('path', '.*');\n",[204,4689,4690],{"class":206,"line":249},[204,4691,4546],{},[13,4693,4694,4695,4698],{},"authミドルウェアでグルーピングをして",[61,4696,4697],{},"protected","配下のルートを保護します。",[13,4700,4701,4704,4705,4563,4708,4711,4712,4714,4715,4718,4719,4563,4722,4725],{},[61,4702,4703],{},"{path?}","は任意の記述を意味します。つまり",[61,4706,4707],{},"\u002Fprotected\u002Fsecret.jpg",[61,4709,4710],{},"\u002Fprotected\u002Fmanual\u002Fprivate.png","などのルートにをキャッチすることができます。そして",[61,4713,4703],{},"パラメータはコールバック（コントローラー）に第二引数として使用できます。先程の例のパスの場合、",[61,4716,4717],{},"$path","は",[61,4720,4721],{},"secret.jpg",[61,4723,4724],{},"manual\u002Fprivate.png","となります。この値は後でファイルの取得に使用します。ちなみに今回はルートに処理内容を記述しますが、プロジェクトによっては複雑な認証処理を実装する場合はコントローラーに記述しても大丈夫です。",[27,4727,4728],{"id":4728},"ファイルの取得とレスポンスを行う",[13,4730,4731],{},"ではファイルの取得の処理を記述します。",[72,4733,4735],{"className":539,"code":4734,"filename":4661,"language":541,"meta":80,"style":80},"use Illuminate\\Support\\Facades\\File;\n\nRoute::group(['middleware' => 'auth'], function () {\n    Route::get('\u002Fprotected\u002F{path?}', function (Request $request,$path='') {\n        if($path==='') abort(404);\n\n        $rp = resource_path('protected\u002F'.$path);\n        if(File::exists($rp)){\n            return response()->file($rp);\n        }else{\n            abort(404);\n        }\n    })->where('path', '.*');\n});\n",[61,4736,4737,4741,4745,4749,4753,4758,4762,4767,4772,4777,4781,4786,4790,4794],{"__ignoreMap":80},[204,4738,4739],{"class":206,"line":207},[204,4740,2812],{},[204,4742,4743],{"class":206,"line":228},[204,4744,232],{"emptyLinePlaceholder":231},[204,4746,4747],{"class":206,"line":235},[204,4748,4672],{},[204,4750,4751],{"class":206,"line":244},[204,4752,4677],{},[204,4754,4755],{"class":206,"line":249},[204,4756,4757],{},"        if($path==='') abort(404);\n",[204,4759,4760],{"class":206,"line":257},[204,4761,232],{"emptyLinePlaceholder":231},[204,4763,4764],{"class":206,"line":268},[204,4765,4766],{},"        $rp = resource_path('protected\u002F'.$path);\n",[204,4768,4769],{"class":206,"line":279},[204,4770,4771],{},"        if(File::exists($rp)){\n",[204,4773,4774],{"class":206,"line":287},[204,4775,4776],{},"            return response()->file($rp);\n",[204,4778,4779],{"class":206,"line":296},[204,4780,2941],{},[204,4782,4783],{"class":206,"line":304},[204,4784,4785],{},"            abort(404);\n",[204,4787,4788],{"class":206,"line":315},[204,4789,1799],{},[204,4791,4792],{"class":206,"line":326},[204,4793,4687],{},[204,4795,4796],{"class":206,"line":337},[204,4797,4546],{},[13,4799,4800,4801,4803,4804,4807,4808,4810],{},"最初に",[61,4802,4717],{},"がない場合は404にアボートします。そして何かしらファイルが指定された場合は",[61,4805,4806],{},"resource_path('protected\u002F'.$path)","にて",[61,4809,4651],{},"配下のファイルパスを取得します。",[2401,4812,4815,4816,4819,4820,4823,4824,4827,4828,4831,4832,4718,4834,4837],{"className":4813},[2404,4814],"alert-danger","\nwebサーバーでなくPHP（Laravel）にてユーザーからの入力値（リクエストパス）を用いてファイルの取得をする場合、PHPの",[61,4817,4818],{},"file_get_contents()","は使用せずLaravelの",[61,4821,4822],{},"resource_path()","や",[61,4825,4826],{},"storage_path","を使用し、さらにFileファサード、",[61,4829,4830],{},"file()","メソッドを使用しましょう。",[61,4833,4818],{},[61,4835,4836],{},"..\u002F","といった記述は文字列でなく、パスとして認識してしまい想定しないディレクトリのファイルにがブラウザを通じて取得される可能性があります。このような脆弱性をディレクトリトラバーサルといいます。Laravelのファイル取得系のメソッドはその辺は対策済みなので、基本的にはLaravelのメソッドを使用しましょう。\n",[13,4839,4840,4841,4844,4845,4848,4849,4852],{},"ファイルパスを作成したら",[61,4842,4843],{},"File::exists()"," を使用してファイルが存在するかをチェックします。存在しないファイルをfile()パスで使用すると",[61,4846,4847],{},"FileNotFoundException","が発生してしまいます。例外処理でやってもいいのですが、",[61,4850,4851],{},"response","メソッドを呼んでいるので念のためあらかじめチェックしておきます。",[13,4854,4855,4856,4859,4860,4862,4863,4865,4866,4869],{},"ファイルがある場合は ",[61,4857,4858],{},"response()->file();","を使用して対象の",[61,4861,4651],{},"配下のファイルをレスポンスとして返します。ない場合は404へアボートします。",[61,4864,4830],{},"メソッドを使用することで拡張子から",[61,4867,4868],{},"content-type","を設定してくれるそうでCSVだろうがHTMLでもMP4でも問題なくレスポンスしてくれます。",[13,4871,4872],{},"ルート自体はLaravelのミドルウェアを使用することでファイルを保護し、任意のファイルパスを使用して認証が通ればファイルを取得することができる様になります。",[27,4874,4875],{"id":4875},"storageでやる方法",[13,4877,4878],{},"resourcesは基本的に開発者が静的にファイルを置く場合に使用します。ユーザーが自由にアップロードして、保護しながら呼び出したい時はstorageディレクトリを使用します。ファイルの取得と保護は上記とほぼ同じですが、storageの場合は少しcconfigの設定を行います。",[13,4880,4881,4884],{},[61,4882,4883],{},"config\u002Ffilesystem.php","にて以下の様に保護storageディレクトリを定義します。",[72,4886,4888],{"className":539,"code":4887,"filename":4883,"language":541,"meta":80,"style":80},"'protected' => [\n    'driver' => 'local',\n    'root' => storage_path('app\u002Fprotected'),\n    'url' => env('APP_URL') . '\u002Fstorage',\n    'visibility' => 'private',\n],\n\n",[61,4889,4890,4895,4900,4905,4910,4915],{"__ignoreMap":80},[204,4891,4892],{"class":206,"line":207},[204,4893,4894],{},"'protected' => [\n",[204,4896,4897],{"class":206,"line":228},[204,4898,4899],{},"    'driver' => 'local',\n",[204,4901,4902],{"class":206,"line":235},[204,4903,4904],{},"    'root' => storage_path('app\u002Fprotected'),\n",[204,4906,4907],{"class":206,"line":244},[204,4908,4909],{},"    'url' => env('APP_URL') . '\u002Fstorage',\n",[204,4911,4912],{"class":206,"line":249},[204,4913,4914],{},"    'visibility' => 'private',\n",[204,4916,4917],{"class":206,"line":257},[204,4918,4433],{},[13,4920,4921,4922,4925,4926,4928],{},"こうすることで",[61,4923,4924],{},"Storage::disk('protected')->path()","を用いて対象ファイルパスを取得することができる様になります。ファイルストレージはローカルでなくS3など外部のものを使用することもあるので、この様に設定ファイルで定義しておくといいです。storageにprotectedディレクトリを作成した後、あとはルートを定義して",[61,4927,4924],{},"を用いてリクエストされたファイルパスを取得し、存在チェックをしてレスポンスで返せばOKです。",[13,4930,4931],{},"今回は簡単なauthミドルウェアですが、権限のロジックを組み込むことで所有者のリクエストのみに見せたり、特定の人のみに見せるといった芸当ができそうです。ただしwebサーバーの静的な配信でなく、ファイルの取得にPHPを動かすことになるので大量配信の場合はパフォーマンスはちょっと心配かもしません。",[2310,4933,4455],{},{"title":80,"searchDepth":235,"depth":235,"links":4935},[4936,4937,4938,4939,4940],{"id":4511,"depth":228,"text":4511},{"id":4645,"depth":228,"text":4645},{"id":4655,"depth":228,"text":4655},{"id":4728,"depth":228,"text":4728},{"id":4875,"depth":228,"text":4875},[2332],"2022-03-26","画像、アセットにログインしたユーザーのみリクエストを制限する",{},"\u002Farticles\u002Flaravel-protect-resource",{"title":4476,"description":4943},"articles\u002Flaravel-protect-resource",[3988,541],"f9NQ8_uhPD5HoW5tFBCWKT-Mw4ExKqCgzTqezzlXg_w",{"id":4951,"title":4952,"body":4953,"category":5440,"createdAt":5441,"description":4952,"extension":2334,"index":2335,"meta":5442,"navigation":231,"path":5443,"publish":231,"seo":5444,"series":2335,"seriesTitle":2335,"stem":5445,"tag":5446,"thumbnail":3990,"updatedAt":2335,"__hash__":5447},"articles\u002Farticles\u002Flaravel-validation-unit-test.md","Laravelでカスタムバリデーションのユニットテストをする方法",{"type":10,"value":4954,"toc":5433},[4955,4962,4965,4968,4971,5039,5042,5049,5218,5221,5224,5227,5230,5250,5260,5263,5266,5321,5324,5377,5398,5404,5407,5428,5431],[13,4956,4957,4958,4961],{},"Laravelでは",[61,4959,4960],{},"Illuminate\\Contracts\\Validation\\Rule","を継承したクラスを用いてカスタムなバリデーションを作成することができます。そしてサービスプロバイダに登録することで、FormRequestで文字列で指定することでリクエストのバリデーションを拡張できます。",[13,4963,4964],{},"ただしこの様な独自コードはきちんとユニットテストをすることが大切です。Laravelではこのカスタムバリデーションを簡単にユニットテストをすることができます。",[27,4966,4967],{"id":4967},"サンプルのバリデーション",[13,4969,4970],{},"とりあえず以下の様な郵便番号のバリデーションを作成したとします。７桁のハイフンなしの数字が郵便番号の形式とします。",[72,4972,4974],{"className":539,"code":4973,"language":541,"meta":80,"style":80},"namespace App\\Rules;\nuse Illuminate\\Contracts\\Validation\\Rule;\n\nclass Zipcode implements Rule{\n\n    public function passes($attribute, $value)\n    {\n        return preg_match('\u002F^[0-9]{3}-?[0-9]{4}$\u002F', $value);\n    }\n\n    public function message(){\n        return '郵便番号は7桁の半角数字で入力してください。';\n    }\n}\n",[61,4975,4976,4981,4986,4990,4995,4999,5004,5008,5013,5017,5021,5026,5031,5035],{"__ignoreMap":80},[204,4977,4978],{"class":206,"line":207},[204,4979,4980],{},"namespace App\\Rules;\n",[204,4982,4983],{"class":206,"line":228},[204,4984,4985],{},"use Illuminate\\Contracts\\Validation\\Rule;\n",[204,4987,4988],{"class":206,"line":235},[204,4989,232],{"emptyLinePlaceholder":231},[204,4991,4992],{"class":206,"line":244},[204,4993,4994],{},"class Zipcode implements Rule{\n",[204,4996,4997],{"class":206,"line":249},[204,4998,232],{"emptyLinePlaceholder":231},[204,5000,5001],{"class":206,"line":257},[204,5002,5003],{},"    public function passes($attribute, $value)\n",[204,5005,5006],{"class":206,"line":268},[204,5007,1147],{},[204,5009,5010],{"class":206,"line":279},[204,5011,5012],{},"        return preg_match('\u002F^[0-9]{3}-?[0-9]{4}$\u002F', $value);\n",[204,5014,5015],{"class":206,"line":287},[204,5016,785],{},[204,5018,5019],{"class":206,"line":296},[204,5020,232],{"emptyLinePlaceholder":231},[204,5022,5023],{"class":206,"line":304},[204,5024,5025],{},"    public function message(){\n",[204,5027,5028],{"class":206,"line":315},[204,5029,5030],{},"        return '郵便番号は7桁の半角数字で入力してください。';\n",[204,5032,5033],{"class":206,"line":326},[204,5034,785],{},[204,5036,5037],{"class":206,"line":337},[204,5038,804],{},[27,5040,5041],{"id":5041},"ユニットテストファイルを作成",[13,5043,5044,5045,5048],{},"ひとまず ",[61,5046,5047],{},"tests\u002FUnit\u002FRules.php"," というものを作成します。",[72,5050,5053],{"className":539,"code":5051,"filename":5052,"language":541,"meta":80,"style":80},"namespace Tests\\Unit;\n\nuse App\\Rules\\Zipcode;\nuse Illuminate\\Foundation\\Testing\\TestCase;\nuse Tests\\CreatesApplication;\nuse Illuminate\\Support\\Facades\\Validator;\nuse \\Illuminate\\Validation\\ValidationException;\n\nclass Rules extends TestCase{\n    use CreatesApplication;\n\n    public function test_zipcode_validation(){\n        $tests = [\n            '1234567'=>true,\n            '0012344'=>true,\n            '0012340'=>true,\n            '12345678'=>false,\n            '123456'=>false,\n            '1234 56'=>false,\n            '1234a56'=>false,\n            '1234_56'=>false,\n        ];\n        foreach($tests as $key => $condition){\n            try{\n                Validator::make(['test'=>$key],[\n                    'test'=> new Zipcode()\n                ])->validate();\n                $this->assertTrue($condition===true);\n            }catch(ValidationException $e){\n                $this->assertFalse($condition);\n            }\n        }\n    }\n}\n","Rules.php",[61,5054,5055,5060,5064,5069,5074,5079,5084,5089,5093,5098,5103,5107,5112,5117,5122,5127,5132,5137,5142,5147,5152,5157,5162,5167,5172,5177,5182,5187,5192,5197,5202,5206,5210,5214],{"__ignoreMap":80},[204,5056,5057],{"class":206,"line":207},[204,5058,5059],{},"namespace Tests\\Unit;\n",[204,5061,5062],{"class":206,"line":228},[204,5063,232],{"emptyLinePlaceholder":231},[204,5065,5066],{"class":206,"line":235},[204,5067,5068],{},"use App\\Rules\\Zipcode;\n",[204,5070,5071],{"class":206,"line":244},[204,5072,5073],{},"use Illuminate\\Foundation\\Testing\\TestCase;\n",[204,5075,5076],{"class":206,"line":249},[204,5077,5078],{},"use Tests\\CreatesApplication;\n",[204,5080,5081],{"class":206,"line":257},[204,5082,5083],{},"use Illuminate\\Support\\Facades\\Validator;\n",[204,5085,5086],{"class":206,"line":268},[204,5087,5088],{},"use \\Illuminate\\Validation\\ValidationException;\n",[204,5090,5091],{"class":206,"line":279},[204,5092,232],{"emptyLinePlaceholder":231},[204,5094,5095],{"class":206,"line":287},[204,5096,5097],{},"class Rules extends TestCase{\n",[204,5099,5100],{"class":206,"line":296},[204,5101,5102],{},"    use CreatesApplication;\n",[204,5104,5105],{"class":206,"line":304},[204,5106,232],{"emptyLinePlaceholder":231},[204,5108,5109],{"class":206,"line":315},[204,5110,5111],{},"    public function test_zipcode_validation(){\n",[204,5113,5114],{"class":206,"line":326},[204,5115,5116],{},"        $tests = [\n",[204,5118,5119],{"class":206,"line":337},[204,5120,5121],{},"            '1234567'=>true,\n",[204,5123,5124],{"class":206,"line":348},[204,5125,5126],{},"            '0012344'=>true,\n",[204,5128,5129],{"class":206,"line":4},[204,5130,5131],{},"            '0012340'=>true,\n",[204,5133,5134],{"class":206,"line":363},[204,5135,5136],{},"            '12345678'=>false,\n",[204,5138,5139],{"class":206,"line":368},[204,5140,5141],{},"            '123456'=>false,\n",[204,5143,5144],{"class":206,"line":376},[204,5145,5146],{},"            '1234 56'=>false,\n",[204,5148,5149],{"class":206,"line":386},[204,5150,5151],{},"            '1234a56'=>false,\n",[204,5153,5154],{"class":206,"line":395},[204,5155,5156],{},"            '1234_56'=>false,\n",[204,5158,5159],{"class":206,"line":402},[204,5160,5161],{},"        ];\n",[204,5163,5164],{"class":206,"line":412},[204,5165,5166],{},"        foreach($tests as $key => $condition){\n",[204,5168,5169],{"class":206,"line":422},[204,5170,5171],{},"            try{\n",[204,5173,5174],{"class":206,"line":432},[204,5175,5176],{},"                Validator::make(['test'=>$key],[\n",[204,5178,5179],{"class":206,"line":447},[204,5180,5181],{},"                    'test'=> new Zipcode()\n",[204,5183,5184],{"class":206,"line":454},[204,5185,5186],{},"                ])->validate();\n",[204,5188,5189],{"class":206,"line":462},[204,5190,5191],{},"                $this->assertTrue($condition===true);\n",[204,5193,5194],{"class":206,"line":467},[204,5195,5196],{},"            }catch(ValidationException $e){\n",[204,5198,5199],{"class":206,"line":475},[204,5200,5201],{},"                $this->assertFalse($condition);\n",[204,5203,5204],{"class":206,"line":483},[204,5205,2917],{},[204,5207,5208],{"class":206,"line":1413},[204,5209,1799],{},[204,5211,5212],{"class":206,"line":1419},[204,5213,785],{},[204,5215,5216],{"class":206,"line":1424},[204,5217,804],{},[13,5219,5220],{},"バリデーションテストでは正しい形式は正しいと判断（ポジティブテスト）し、間違っているものは間違っていると判断（ネガティブテスト）できているかをテストします。\nもしも正しいのに間違っていると判断したり、間違っているのに正しいとなったらテストが失敗する様になっています。",[13,5222,5223],{},"ここでバリデーションテストの詳細を解説します。",[505,5225,5226],{"id":5226},"バリデーションのインスタンスを作成",[13,5228,5229],{},"最初にテストしたいバリデーションのインスタンス、そしてバリデーターインスタンスを作成します。",[72,5231,5233],{"className":539,"code":5232,"language":541,"meta":80,"style":80},"Validator::make(['test'=>$key],[\n    'test'=> new Zipcode()\n])->validate();\n",[61,5234,5235,5240,5245],{"__ignoreMap":80},[204,5236,5237],{"class":206,"line":207},[204,5238,5239],{},"Validator::make(['test'=>$key],[\n",[204,5241,5242],{"class":206,"line":228},[204,5243,5244],{},"    'test'=> new Zipcode()\n",[204,5246,5247],{"class":206,"line":235},[204,5248,5249],{},"])->validate();\n",[13,5251,5252,5255,5256,5259],{},[61,5253,5254],{},"Validator","では",[61,5257,5258],{},"make()","を使用して第一引数に、バリデーションをする値とキーを設定します。第二引数にはバリデーションのキーとバリデーションインスタンスを指定します。",[505,5261,5262],{"id":5262},"いろんなパターンをテストする",[13,5264,5265],{},"いろいろなパターンをテストするため以下の様に配列でテストパターンと予期する正誤を設定します。Trueはバリデーション通過で、Falseはバリデーション違反（間違った形式）であることを示します。",[72,5267,5269],{"className":539,"code":5268,"language":541,"meta":80,"style":80},"$tests = [\n    '1234567'=>true,\n    '0012344'=>true,\n    '0012340'=>true,\n    '12345678'=>false,\n    '123456'=>false,\n    '1234 56'=>false,\n    '1234a56'=>false,\n    '1234_56'=>false,\n];\n",[61,5270,5271,5276,5281,5286,5291,5296,5301,5306,5311,5316],{"__ignoreMap":80},[204,5272,5273],{"class":206,"line":207},[204,5274,5275],{},"$tests = [\n",[204,5277,5278],{"class":206,"line":228},[204,5279,5280],{},"    '1234567'=>true,\n",[204,5282,5283],{"class":206,"line":235},[204,5284,5285],{},"    '0012344'=>true,\n",[204,5287,5288],{"class":206,"line":244},[204,5289,5290],{},"    '0012340'=>true,\n",[204,5292,5293],{"class":206,"line":249},[204,5294,5295],{},"    '12345678'=>false,\n",[204,5297,5298],{"class":206,"line":257},[204,5299,5300],{},"    '123456'=>false,\n",[204,5302,5303],{"class":206,"line":268},[204,5304,5305],{},"    '1234 56'=>false,\n",[204,5307,5308],{"class":206,"line":279},[204,5309,5310],{},"    '1234a56'=>false,\n",[204,5312,5313],{"class":206,"line":287},[204,5314,5315],{},"    '1234_56'=>false,\n",[204,5317,5318],{"class":206,"line":296},[204,5319,5320],{},"];\n",[13,5322,5323],{},"そしてforeachでそれぞれチェックします。",[72,5325,5327],{"className":539,"code":5326,"language":541,"meta":80,"style":80},"foreach($tests as $key => $condition){\n    try{\n        Validator::make(['test'=>$key],[\n            'test'=> new Zipcode()\n        ])->validate();\n        $this->assertTrue($condition===true);\n    }catch(ValidationException $e){\n        $this->assertFalse($condition);\n    }\n}\n",[61,5328,5329,5334,5339,5344,5349,5354,5359,5364,5369,5373],{"__ignoreMap":80},[204,5330,5331],{"class":206,"line":207},[204,5332,5333],{},"foreach($tests as $key => $condition){\n",[204,5335,5336],{"class":206,"line":228},[204,5337,5338],{},"    try{\n",[204,5340,5341],{"class":206,"line":235},[204,5342,5343],{},"        Validator::make(['test'=>$key],[\n",[204,5345,5346],{"class":206,"line":244},[204,5347,5348],{},"            'test'=> new Zipcode()\n",[204,5350,5351],{"class":206,"line":249},[204,5352,5353],{},"        ])->validate();\n",[204,5355,5356],{"class":206,"line":257},[204,5357,5358],{},"        $this->assertTrue($condition===true);\n",[204,5360,5361],{"class":206,"line":268},[204,5362,5363],{},"    }catch(ValidationException $e){\n",[204,5365,5366],{"class":206,"line":279},[204,5367,5368],{},"        $this->assertFalse($condition);\n",[204,5370,5371],{"class":206,"line":287},[204,5372,785],{},[204,5374,5375],{"class":206,"line":296},[204,5376,804],{},[13,5378,5379,5382,5383,5386,5387,5389,5390,5393,5394,5397],{},[61,5380,5381],{},"validate()","メソッドは失敗すると",[61,5384,5385],{},"ValidationException"," を投げます。そのため例外処理で",[61,5388,5385],{}," をキャッチします。正しい形式を正しいと判断できれば",[61,5391,5392],{},"$this->assertTrue($condition===true);","となり、まちがった形を間違っていると判断できればキャッチして",[61,5395,5396],{},"$this->assertFalse($condition);","でアサートされます。",[72,5399,5402],{"className":5400,"code":5401,"language":77},[75],"php artisan test \n",[61,5403,5401],{"__ignoreMap":80},[13,5405,5406],{},"にてテストを行い問題なければそのまま通過します。テストは以上の方法でいろんなパターンをテストできます。パターンは思いついたものを配列に書いてもいいですし、プログラム的に大量に配列を生成してもいいかもしれません。まとめると",[42,5408,5409,5412,5417],{},[45,5410,5411],{},"バリデーションインスタンスを作成",[45,5413,5414,5416],{},[61,5415,5385],{},"を用いて間違っている形を識別",[45,5418,5419,5420,5423,5424,5427],{},"バリデーション対象の値が正誤かどうかは",[61,5421,5422],{},"assertTrue","と",[61,5425,5426],{},"assertFalse","で正しく判断できているかをアサート",[13,5429,5430],{},"こんな感じです。私はこのテストで結構救われたので、バリデーションを作った時は必ずユニットテストをしましょう。",[2310,5432,4455],{},{"title":80,"searchDepth":235,"depth":235,"links":5434},[5435,5436],{"id":4967,"depth":228,"text":4967},{"id":5041,"depth":228,"text":5041,"children":5437},[5438,5439],{"id":5226,"depth":235,"text":5226},{"id":5262,"depth":235,"text":5262},[3981],"2022-03-25",{},"\u002Farticles\u002Flaravel-validation-unit-test",{"title":4952,"description":4952},"articles\u002Flaravel-validation-unit-test",[541,3988],"_6kCaD6uDnBdQkj-XY_8G39zixCeVekVE-xS127shV4",{"id":5449,"title":5450,"body":5451,"category":5524,"createdAt":5525,"description":5450,"extension":2334,"index":2335,"meta":5526,"navigation":231,"path":5527,"publish":231,"seo":5528,"series":2335,"seriesTitle":2335,"stem":5529,"tag":5530,"thumbnail":5531,"updatedAt":2335,"__hash__":5532},"articles\u002Farticles\u002Fwordpress-asset-chache.md","WordpressのJS・CSSファイルのキャッシュ対策",{"type":10,"value":5452,"toc":5520},[5453,5456,5460,5470,5476,5483,5489,5492,5495,5498,5501,5508,5514,5517],[13,5454,5455],{},"こんにちはjunです。wordpressのテーマを本番運用してスタイルに修正があり、修正をアップロードしてもクライアントのキャッシュが原因でユーザー側で変更されないことがあります。今回はテーマ内で読み込むjs,cssのキャッシュ対策について忘備録がてら記事として共有したいと思います。",[27,5457,5459],{"id":5458},"wp_enqueue_stylewp_enqueue_scriptに記述","wp_enqueue_style,wp_enqueue_scriptに記述",[13,5461,5462,5463,4563,5466,5469],{},"wordpressのアセット読み込みには",[61,5464,5465],{},"wp_enqueue_style",[61,5467,5468],{},"wp_enqueue_script","を使用します。それらの第４引数にはバージョンの文字列を入力することができます。",[13,5471,5472],{},[176,5473,5468],{"href":5474,"rel":5475},"https:\u002F\u002Fdeveloper.wordpress.org\u002Freference\u002Ffunctions\u002Fwp_enqueue_script\u002F",[180],[13,5477,5478,5479,5482],{},"例えば、",[61,5480,5481],{},"1.3","など入力すれば読み込んだアセットのURLで以下の様に設定されます。",[72,5484,5487],{"className":5485,"code":5486,"language":77},[75],"https:\u002F\u002Fexample.com\u002Fwp-content\u002Fthemes\u002Fminato\u002Fassets\u002Fcss\u002Fstyle.css?ver=1.3\n",[61,5488,5486],{"__ignoreMap":80},[13,5490,5491],{},"GETパラメーターでバージョンの文字列をつけることで、バージョンを識別できる様になります。ブラウザは同じURLのCSS、JS、画像を手元にキャッシュします。普段は通信が早くなるのでありがたいですが、修正が発生した時などは古いコードが残るのでユーザーによって動きに差異が生まれる原因になります。そして大体のユーザーはキャッシュクリアのやり方を知らないですし、スマホは特に強力なキャッシュが効いています。",[13,5493,5494],{},"更新時のファイルをユーザーに届けるためには、上記の様なバージョンのGETパラメーターをつけるなどして、別のURLを指定する必要があります。",[13,5496,5497],{},"そのため納品時には今後の修正を考えてバージョンを変化させる様にした方がいいです。マーケットプレイスならば上記の引数をリリース・修正ごとに変えていればいいです。しかし開発中や毎回バージョンを変えるのが面倒な時、Gitで管理していてバージョンの文字列をあまり変えたくない時はファイルの更新日時でバージョンを変えてあげる方法があります。",[27,5499,5500],{"id":5500},"filemtimeを使用する",[13,5502,5503,5504,5507],{},"PHPには対象ファイルの更新日時を取得する",[61,5505,5506],{},"filemtime()","という関数があります。引数には以下の様にサーバー上でのファイルパスを入力します。",[72,5509,5512],{"className":5510,"code":5511,"language":77},[75],"filemtime(get_theme_file_path('\u002Fcss\u002Feditor-style.css'))\n",[61,5513,5511],{"__ignoreMap":80},[13,5515,5516],{},"返り値はUnixタイムスタンプです。更新時はその時間が変わるので、更新のたびに変化する値を取得して変更したファイルを確実に届けることができます。対象ファイルを編集するだけで自動的にバージョンを示すことができます。",[13,5518,5519],{},"毎回更新日時を取得するのでその分のパフォーマンスがきりなりますが、自分はよくこの方法でwordpressを構築しています。",{"title":80,"searchDepth":235,"depth":235,"links":5521},[5522,5523],{"id":5458,"depth":228,"text":5459},{"id":5500,"depth":228,"text":5500},[3981],"2022-03-20",{},"\u002Farticles\u002Fwordpress-asset-chache",{"title":5450,"description":5450},"articles\u002Fwordpress-asset-chache",[541,2341],"_common\u002Fwordpress.png","S6seqQRftTxMf1j4j6_e1GgS9Q_wBpOnHTPv4lrW-Hc",{"id":5534,"title":5535,"body":5536,"category":5833,"createdAt":5834,"description":5835,"extension":2334,"index":2335,"meta":5836,"navigation":231,"path":5837,"publish":231,"seo":5838,"series":2335,"seriesTitle":2335,"stem":5839,"tag":5840,"thumbnail":2335,"updatedAt":2335,"__hash__":5842},"articles\u002Farticles\u002Flaravel-plain-text-with-html.md","LaravelのMailableでHTMLメールとプレーンテキストメール両方を送信する方法",{"type":10,"value":5537,"toc":5823},[5538,5541,5545,5548,5555,5559,5562,5571,5575,5582,5592,5598,5601,5605,5608,5679,5689,5697,5700,5703,5709,5724,5744,5763,5766,5773,5821],[13,5539,5540],{},"こんにちはjunです。Laravelでメール機能が伴う内容を実装していたときに、HTMLメールだけでなくプレーンテキストメールでも送付してほしいとの用件がありました。Laravelではメールを送信する時は大抵、Mailableを使用しますがその時に両方送る方法が意外と日本語でなかったので忘備録として記事を作りました。プレーンテキストとはなんぞや？というとこから解説するので、対策法をさっさと知りたい方は「Mailabeでのプレーンテキストの設定」を見てください。",[27,5542,5544],{"id":5543},"プレーンテキストメールとhtmlメールの違い","プレーンテキストメールとHTMLメールの違い",[13,5546,5547],{},"HTMLメールは名の通り、HTMLの記法で作成されたメールです。生のデータにはHTMLが書かれており、メールクライアント側でHTMLをレンダリングしてメール内容を表示します。リッチなメールを送付できるというメリットがあります。デメリットとして環境やデバイスによってはメールが全く見れなくなることです。",[13,5549,5550,5551,5554],{},"HTMLメールが見れない環境や昔はプレーンテキストメールといった、メモ帳で書いた様な本当に純粋な文字だけのメールを利用します。メリットはどの端末でも必ず表示はできるので、確実に届けたいメールなどにはプレーンテキストがおすすめです。例えばGithubの二段階認証メールは",[61,5552,5553],{},"Content-Type: text\u002Fplain; charset=UTF-8","とプレーンテキストで必ず送られ、毎週のお知らせメールはその両方が送られています。",[505,5556,5558],{"id":5557},"どうやって確認できるの","どうやって確認できるの？",[13,5560,5561],{},"気になる方は届いたメールのソースを見てみましょう。Gmailであれば「画面右側の点々」をクリックして「メッセージのソースを表示」をクリックしますと、メールのヘッダやボディを確認できます。そのとき",[13,5563,5564,5566,5567,5570],{},[61,5565,5553],{},"があれば、プレーンテキストメール形式で送付され、",[61,5568,5569],{},"Content-Type: text\u002Fhtml; charset=UTF-8","があればHTMLメールです。きちんとHTMLの記述があるのを確認してみてください。",[505,5572,5574],{"id":5573},"なぜ両方ともつけることができるの","なぜ両方ともつけることができるの？",[13,5576,5577,5578,5581],{},"ちなみにメールにはHTMLとプレーン両方ともつけることは可能です。その場合環境に合わせてHTML・プレーンのものが表示されます。その仕組みはソースの中に ",[61,5579,5580],{},"Content-Type: multipart\u002Falternative; boundary=","のような記述をしようすることです。これはメールの内容を複数の形式で送りますよというヘッダ要素です。HTMLの内容とプレーンテキストの内容がソースでどこで分けているかを示しています。",[13,5583,5584,5587,5588,5591],{},[61,5585,5586],{}," boundary=\"...\""," のboundaryの中身にある文字を境界として使用します。Laravelの場合はSwiftを使用しているので ",[61,5589,5590],{},"_=_swift_1646631221_d7511bd8d439c84878b3339aaec563e1_=_","という文字（一部分はランダムです）が境界として使用され、",[72,5593,5596],{"className":5594,"code":5595,"language":77},[75],"Content-Type: multipart\u002Falternative; boundary=\"_=_swift_1646631221_d7511bd8d439c84878b3339aaec563e1_=_\"\n\n--_=_swift_1646631221_d7511bd8d439c84878b3339aaec563e1_=_\nContent-Type: text\u002Fplain; charset=utf-8\nContent-Transfer-Encoding: quoted-printable\n\n（１）HTMLの記述’\n\n--_=_swift_1646631221_d7511bd8d439c84878b3339aaec563e1_=_\nContent-Type: text\u002Fhtml; charset=utf-8\nContent-Transfer-Encoding: quoted-printable\n\n（２）プレーンテキストの記述\n\n--_=_swift_1646631221_d7511bd8d439c84878b3339aaec563e1_=_\n",[61,5597,5595],{"__ignoreMap":80},[13,5599,5600],{},"上記の様な記述があると思います。ここで境界の文字を用いて２つの形式の内容を記述し、メールを送付してクライアントは適宜そのソースを汲み取って表示しています。",[27,5602,5604],{"id":5603},"mailabeでのプレーンテキストの設定","Mailabeでのプレーンテキストの設定",[13,5606,5607],{},"前置きがながくなりましたが、LaravelのMailableでは以下の様に指定します。",[72,5609,5611],{"className":539,"code":5610,"language":541,"meta":80,"style":80},"\u002F**\n    * Build the message.\n    *\n    * @return $this\n*\u002F\npublic function build()\n{\n    return $this->view('mail.registration',[\n        'applay_user_name'=>$name,\n    ])->text('mail.registration_text',[ \u002F\u002F これ！\n        'applay_user_name'=>$name,\n    ])\n    ->subject('登録を受け付けました。');\n}\n",[61,5612,5613,5618,5623,5628,5633,5637,5642,5646,5651,5656,5661,5665,5670,5675],{"__ignoreMap":80},[204,5614,5615],{"class":206,"line":207},[204,5616,5617],{},"\u002F**\n",[204,5619,5620],{"class":206,"line":228},[204,5621,5622],{},"    * Build the message.\n",[204,5624,5625],{"class":206,"line":235},[204,5626,5627],{},"    *\n",[204,5629,5630],{"class":206,"line":244},[204,5631,5632],{},"    * @return $this\n",[204,5634,5635],{"class":206,"line":249},[204,5636,4396],{},[204,5638,5639],{"class":206,"line":257},[204,5640,5641],{},"public function build()\n",[204,5643,5644],{"class":206,"line":268},[204,5645,2450],{},[204,5647,5648],{"class":206,"line":279},[204,5649,5650],{},"    return $this->view('mail.registration',[\n",[204,5652,5653],{"class":206,"line":287},[204,5654,5655],{},"        'applay_user_name'=>$name,\n",[204,5657,5658],{"class":206,"line":296},[204,5659,5660],{},"    ])->text('mail.registration_text',[ \u002F\u002F これ！\n",[204,5662,5663],{"class":206,"line":304},[204,5664,5655],{},[204,5666,5667],{"class":206,"line":315},[204,5668,5669],{},"    ])\n",[204,5671,5672],{"class":206,"line":326},[204,5673,5674],{},"    ->subject('登録を受け付けました。');\n",[204,5676,5677],{"class":206,"line":337},[204,5678,804],{},[13,5680,5681,5684,5685,5688],{},[61,5682,5683],{},"text()","メソッドを使用することで前述の解説の様にプレーンテキスト用のmultipartを入れ込んでくれます。なお、引数は",[61,5686,5687],{},"view()","メソッドと同じでテンプレートファイルとデータを渡すことができます。この記法はLaravel5.3から利用できます。",[13,5690,5691,5692],{},"参考\n",[176,5693,5696],{"href":5694,"rel":5695},"https:\u002F\u002Flaravel.com\u002Fdocs\u002F9.x\u002Fmail#plain-text-emails",[180],"Laravel9 Mail",[505,5698,5699],{"id":5699},"テンプレートの記述とファイル構成のすすめ",[13,5701,5702],{},"私の場合は以下の様な構成とファイル名でメールテンプレートを管理しています。",[72,5704,5707],{"className":5705,"code":5706,"language":77},[75],"views\n│\n├── mail\n    ├── master.blade.php\n    ├── master_text.blade.php\n    ├── register.blade.php\n    └── register_text.blade.php\n",[61,5708,5706],{"__ignoreMap":80},[13,5710,5711,5712,5715,5716,5719,5720,5723],{},"まずviewsでmail用のテンプレートを格納するディレクトリ を作成し、そしてHプレーンテキスト用のテンプレートはTML用のものに",[61,5713,5714],{},"_text","をつけておきます。そして",[61,5717,5718],{},"master.blade.php","はレイアウトやHTMLのスタイルを定義しています。同じようにプレーン用のレイアウトファイルの",[61,5721,5722],{},"master_text.blade.php","を用意しておくといいです。",[72,5725,5728],{"className":539,"code":5726,"filename":5727,"language":541,"meta":80,"style":80},"{{$name}}様\n\u003Cp>いつもご利用いただきありがとうございます。\u003C\u002Fp>\n...\n","register.blade.php",[61,5729,5730,5735,5740],{"__ignoreMap":80},[204,5731,5732],{"class":206,"line":207},[204,5733,5734],{},"{{$name}}様\n",[204,5736,5737],{"class":206,"line":228},[204,5738,5739],{},"\u003Cp>いつもご利用いただきありがとうございます。\u003C\u002Fp>\n",[204,5741,5742],{"class":206,"line":235},[204,5743,964],{},[72,5745,5748],{"className":539,"code":5746,"filename":5747,"language":541,"meta":80,"style":80},"{{$name}}様\nいつもご利用いただきありがとうございます。\n...\n","register_text.blade.php",[61,5749,5750,5754,5759],{"__ignoreMap":80},[204,5751,5752],{"class":206,"line":207},[204,5753,5734],{},[204,5755,5756],{"class":206,"line":228},[204,5757,5758],{},"いつもご利用いただきありがとうございます。\n",[204,5760,5761],{"class":206,"line":235},[204,5762,964],{},[27,5764,5765],{"id":5765},"notifiableの場合",[13,5767,5768,5769,5772],{},"これはLaravel8しか確認していませんが、Notifiableで使用されるMailMessageの場合はプレーンテキストが自動的に作成されていました。以下の様に",[61,5770,5771],{},"toMail()","を作成しておけばHTMLもプレーンも送付されていました。",[72,5774,5776],{"className":539,"code":5775,"language":541,"meta":80,"style":80},"public function toMail($notifiable)\n{\n    return (new MailMessage)\n                ->subject('メールアドレスが変更されました')\n                ->line(\"いつもご利用いただきありがとうございます。\")\n                ->line('連絡用メールアドレスの変更を受け付けました。')\n                ->line('※本メールは送信専用です。ご返信いただいても対応できませんのでご了承ください。')\n                ->line('※本メールにお心当たりがない場合は、恐れ入りますが破棄していただきますようお願いいたします。');\n}\n",[61,5777,5778,5783,5787,5792,5797,5802,5807,5812,5817],{"__ignoreMap":80},[204,5779,5780],{"class":206,"line":207},[204,5781,5782],{},"public function toMail($notifiable)\n",[204,5784,5785],{"class":206,"line":228},[204,5786,2450],{},[204,5788,5789],{"class":206,"line":235},[204,5790,5791],{},"    return (new MailMessage)\n",[204,5793,5794],{"class":206,"line":244},[204,5795,5796],{},"                ->subject('メールアドレスが変更されました')\n",[204,5798,5799],{"class":206,"line":249},[204,5800,5801],{},"                ->line(\"いつもご利用いただきありがとうございます。\")\n",[204,5803,5804],{"class":206,"line":257},[204,5805,5806],{},"                ->line('連絡用メールアドレスの変更を受け付けました。')\n",[204,5808,5809],{"class":206,"line":268},[204,5810,5811],{},"                ->line('※本メールは送信専用です。ご返信いただいても対応できませんのでご了承ください。')\n",[204,5813,5814],{"class":206,"line":279},[204,5815,5816],{},"                ->line('※本メールにお心当たりがない場合は、恐れ入りますが破棄していただきますようお願いいたします。');\n",[204,5818,5819],{"class":206,"line":287},[204,5820,804],{},[2310,5822,4455],{},{"title":80,"searchDepth":235,"depth":235,"links":5824},[5825,5829,5832],{"id":5543,"depth":228,"text":5544,"children":5826},[5827,5828],{"id":5557,"depth":235,"text":5558},{"id":5573,"depth":235,"text":5574},{"id":5603,"depth":228,"text":5604,"children":5830},[5831],{"id":5699,"depth":235,"text":5699},{"id":5765,"depth":228,"text":5765},[3981],"2022-03-07","LaravelのMilableでHTMLメールとプレーンテキストメール両方を送信する方法",{},"\u002Farticles\u002Flaravel-plain-text-with-html",{"title":5535,"description":5835},"articles\u002Flaravel-plain-text-with-html",[3988,541,5841],"network","Ru_0w8TnRhwaXPjcr0gSRvxAArbPnEm7l-VCgPS-v6w",{"id":5844,"title":5845,"body":5846,"category":8158,"createdAt":8159,"description":8160,"extension":2334,"index":207,"meta":8161,"navigation":231,"path":8162,"publish":231,"seo":8163,"series":8164,"seriesTitle":8165,"stem":8166,"tag":8167,"thumbnail":8168,"updatedAt":2335,"__hash__":8169},"series\u002Fseries\u002Fvue-laravel-app-1.md","Vue SPA x Laravelでつくる実務パチモンアプリその１：アプリの概要と環境構築",{"type":10,"value":5847,"toc":8134},[5848,5851,5868,5879,5882,5902,5905,5908,5911,5914,5917,5921,5952,5955,5991,5994,6005,6008,6011,6014,6017,6020,6029,6043,6050,6113,6127,6132,6136,6139,6192,6195,6198,6201,6219,6276,6283,6287,6290,6598,6615,6624,6682,6685,6691,6705,6714,6720,6734,6737,6740,6744,6751,6757,6760,6764,6767,6771,6777,6797,6803,6935,6946,6949,6972,6976,6982,6986,6993,6999,7002,7061,7064,7200,7207,7210,7334,7417,7420,7751,7758,7765,7768,7786,7960,7969,7981,8018,8021,8060,8066,8079,8090,8093,8106,8109,8118,8121,8124,8128,8131],[13,5849,5850],{},"こんにちはjunです。仕事で中規模プロジェクトのディレクターとしてアサインされて、半年ほどブログを書けませんでした。ようやく最近時間が出てきたので新しいシリーズ記事を作成しようと思いました。とにかく最近１年はシステムばっかり作っていました。それぞのれプロジェクトでは",[42,5852,5853,5856,5859,5862,5865],{},[45,5854,5855],{},"CRUDなAPI",[45,5857,5858],{},"管理画面の構築",[45,5860,5861],{},"ユーザー権限によるアクセス制限",[45,5863,5864],{},"ログイン・ログアウト",[45,5866,5867],{},"ジョブ・キューを用いた長い処理",[13,5869,5870,5871,5874,5875,5878],{},"など使用するシステムの構築を多く行い、いろいろ学ぶことできました。その中で「CRUDなAPI」を考えた「管理画面」の構築をすることが多くありました。バックエンドは",[61,5872,5873],{},"Laravel","を、フロントエンドは",[61,5876,5877],{},"Vue.js","を使用していました。Vue.jsのおかげで高速にリッチなUIを構築し、システムを開発することができました。",[13,5880,5881],{},"この知見を共有し、まとめるためにも今回は",[42,5883,5884,5887,5890,5893,5896,5899],{},[45,5885,5886],{},"管理画面はVue.js SPA",[45,5888,5889],{},"web APIによるCRUD操作",[45,5891,5892],{},"ルーティング設計",[45,5894,5895],{},"バリデーション",[45,5897,5898],{},"MVCによるアプリケーション設計",[45,5900,5901],{},"laravel\u002Fpermissionを用いたユーザー権限",[13,5903,5904],{},"以上の様な機能を持ったデモアプリを作ろうと思います。Laravelを用いたシステム開発と、Vue.jsによる管理画面フロントエンド開発を解説していきます。",[27,5906,5907],{"id":5907},"作るアプリの概要",[13,5909,5910],{},"今回のアプリはn○teみたいなブログサービスを作ります。CRUDや管理画面の構築をメインに行いたいのでシンプルにいきます。",[505,5912,5913],{"id":5913},"機能概要",[13,5915,5916],{},"ざっくりとした機能概要は以下のとおりです。",[5918,5919,5920],"h4",{"id":5920},"ユーザー系の特徴",[42,5922,5923,5926,5929,5932,5935,5938],{},[45,5924,5925],{},"利用する際にはユーザーとしてアカウントを作る（今回は全てゲストとする）。",[45,5927,5928],{},"ログインはアドレスとパスワードを使用する。",[45,5930,5931],{},"アプリを利用する一般ユーザーと管理を行う管理ユーザーが存在する。",[45,5933,5934],{},"管理ユーザーは特定の管理用ルートから入る。",[45,5936,5937],{},"退会可能。退会時は関連するデータは削除される。",[45,5939,5940,5941],{},"プロフィールでは以下の項目を持つ\n",[42,5942,5943,5946,5949],{},[45,5944,5945],{},"ユーザー名（仮名）",[45,5947,5948],{},"プロフィール文",[45,5950,5951],{},"アバター写真",[5918,5953,5954],{"id":5954},"ブログ機能",[42,5956,5957,5960,5976,5979,5982,5985,5988],{},[45,5958,5959],{},"ブログを作成することができ、公開することができる。",[45,5961,5962,5963],{},"ブログは以下の項目を持つ\n",[42,5964,5965,5967,5970,5973],{},[45,5966,1942],{},[45,5968,5969],{},"内容（リッチテキスト）",[45,5971,5972],{},"タグ（任意）",[45,5974,5975],{},"公開日時",[45,5977,5978],{},"下書きとして保存することができる。更新時も下書き可能。",[45,5980,5981],{},"任意数のタグを添付できる。タグはユーザーが新しく作成できる。",[45,5983,5984],{},"削除可能",[45,5986,5987],{},"画像の添付が可能。",[45,5989,5990],{},"アップロードした画像は管理できる。",[5918,5992,5993],{"id":5993},"一覧検索機能",[42,5995,5996,5999,6002],{},[45,5997,5998],{},"サイトトップは投稿された記事が新着順に並ぶ。",[45,6000,6001],{},"任意のタグを選んで一覧表示することが可能。",[45,6003,6004],{},"部分一致検索も一応可能とする。",[505,6006,6007],{"id":6007},"アプリケーションアーキテクチャー",[13,6009,6010],{},"バックエンドにはLaravel9、管理画面はVue.js(2系)を使用して構築します。なおVue.jsはSPA構成とします。記事の一覧や詳細画面などの公開側はLaravelのレンダリングで表示します。",[522,6012],{":src":6013,":center":526},"'vue_laravel_app\u002Farchitecture.jpg'",[13,6015,6016],{},"デザインに関してはセンスが皆無なので管理画面・公開側ともにBootstrapを使用します。",[27,6018,6019],{"id":6019},"環境構築",[13,6021,6022,6023,6028],{},"ではまずは環境構築を行っていきましょう。環境構築にはDockerを使用してLAMP環境を作ります。",[176,6024,6027],{"href":6025,"rel":6026},"https:\u002F\u002Fwww.twilio.com\u002Fblog\u002Fget-started-docker-laravel-jp",[180],"こちらの記事","を参考にし、",[42,6030,6031,6034,6037,6040],{},[45,6032,6033],{},"mysql5.7",[45,6035,6036],{},"phpmyadmin",[45,6038,6039],{},"apache2.4",[45,6041,6042],{},"php8.1",[13,6044,6045,6046,6049],{},"以上の構成を作成したいと思います。ディレクトリを作成して",[61,6047,6048],{},"docker-compose.yaml","を作成します。",[72,6051,6055],{"className":6052,"code":6053,"language":6054,"meta":80,"style":80},"language-bash shiki shiki-themes material-theme-ocean","mkdir ~\u002Flaravel_vue\ncd  ~\u002Flaravel_vue\n\ntouch docker-compose.yaml\nmkdir html\nmkdir web_1\ncd web_1\ntouch Dockerfile\n","bash",[61,6056,6057,6065,6074,6078,6086,6093,6100,6106],{"__ignoreMap":80},[204,6058,6059,6062],{"class":206,"line":207},[204,6060,6061],{"class":2469},"mkdir",[204,6063,6064],{"class":221}," ~\u002Flaravel_vue\n",[204,6066,6067,6071],{"class":206,"line":228},[204,6068,6070],{"class":6069},"sdLwU","cd",[204,6072,6073],{"class":221},"  ~\u002Flaravel_vue\n",[204,6075,6076],{"class":206,"line":235},[204,6077,232],{"emptyLinePlaceholder":231},[204,6079,6080,6083],{"class":206,"line":244},[204,6081,6082],{"class":2469},"touch",[204,6084,6085],{"class":221}," docker-compose.yaml\n",[204,6087,6088,6090],{"class":206,"line":249},[204,6089,6061],{"class":2469},[204,6091,6092],{"class":221}," html\n",[204,6094,6095,6097],{"class":206,"line":257},[204,6096,6061],{"class":2469},[204,6098,6099],{"class":221}," web_1\n",[204,6101,6102,6104],{"class":206,"line":268},[204,6103,6070],{"class":6069},[204,6105,6099],{"class":221},[204,6107,6108,6110],{"class":206,"line":279},[204,6109,6082],{"class":2469},[204,6111,6112],{"class":221}," Dockerfile\n",[13,6114,6115,6118,6119,6122,6123,6126],{},[61,6116,6117],{},"html\u002F","という名前で作成したディレクトリにLaravelのソースが作成され、Dockerコンテナにマウントされる様にします。",[61,6120,6121],{},"web_1\u002F","ディレクトリにはphpとapacheが構築できる",[61,6124,6125],{},"Dockerfile","を作成してビルドします。",[13,6128,6129,6131],{},[61,6130,6048],{},"でDBとapache+phpと連結したいと思います。",[505,6133,6135],{"id":6134},"dockerfile設定","Dockerfile設定",[13,6137,6138],{},"まずはapacheとphpのイメージをDockerfileを作成します。",[72,6140,6145],{"className":6141,"code":6142,"filename":6143,"language":6144,"meta":80,"style":80},"language-dockerfie shiki shiki-themes material-theme-ocean","FROM php:8.1-apache\nRUN apt update \\\n        && apt install -y g++ libicu-dev libpq-dev libzip-dev zip zlib1g-dev \\\n        && mv \u002Fetc\u002Fapache2\u002Fmods-available\u002Frewrite.load \u002Fetc\u002Fapache2\u002Fmods-enabled\nRUN docker-php-ext-install pdo pdo_mysql\nWORKDIR \u002Fvar\u002Fwww\u002Fhtml\nRUN curl -sS https:\u002F\u002Fgetcomposer.org\u002Finstaller | php -- --install-dir=\u002Fusr\u002Flocal\u002Fbin --filename=composer\nRUN curl -fsSL https:\u002F\u002Fdeb.nodesource.com\u002Fsetup_lts.x | bash - \\\n    && apt-get install -y nodejs\n","web_1\u002FDockerfile","dockerfie",[61,6146,6147,6152,6157,6162,6167,6172,6177,6182,6187],{"__ignoreMap":80},[204,6148,6149],{"class":206,"line":207},[204,6150,6151],{},"FROM php:8.1-apache\n",[204,6153,6154],{"class":206,"line":228},[204,6155,6156],{},"RUN apt update \\\n",[204,6158,6159],{"class":206,"line":235},[204,6160,6161],{},"        && apt install -y g++ libicu-dev libpq-dev libzip-dev zip zlib1g-dev \\\n",[204,6163,6164],{"class":206,"line":244},[204,6165,6166],{},"        && mv \u002Fetc\u002Fapache2\u002Fmods-available\u002Frewrite.load \u002Fetc\u002Fapache2\u002Fmods-enabled\n",[204,6168,6169],{"class":206,"line":249},[204,6170,6171],{},"RUN docker-php-ext-install pdo pdo_mysql\n",[204,6173,6174],{"class":206,"line":257},[204,6175,6176],{},"WORKDIR \u002Fvar\u002Fwww\u002Fhtml\n",[204,6178,6179],{"class":206,"line":268},[204,6180,6181],{},"RUN curl -sS https:\u002F\u002Fgetcomposer.org\u002Finstaller | php -- --install-dir=\u002Fusr\u002Flocal\u002Fbin --filename=composer\n",[204,6183,6184],{"class":206,"line":279},[204,6185,6186],{},"RUN curl -fsSL https:\u002F\u002Fdeb.nodesource.com\u002Fsetup_lts.x | bash - \\\n",[204,6188,6189],{"class":206,"line":287},[204,6190,6191],{},"    && apt-get install -y nodejs\n",[13,6193,6194],{},"このファイルではphpとapacheが入った環境にてLaravelの依存PHPモジュールのインストールとcomposerをインストールする内容を記述しています。そしてLaravelのアセットビルドにはnode.jsを使用するのでそのインストールも記述しています。",[505,6196,6197],{"id":6197},"apacheの設定ファイルを作成",[13,6199,6200],{},"Laravelのドキュメントルートを設定するためにapacheの設定ファイル作成して、マウントできる様にします。",[72,6202,6204],{"className":6052,"code":6203,"language":6054,"meta":80,"style":80},"cd web_1\ntouch default.conf\n",[61,6205,6206,6212],{"__ignoreMap":80},[204,6207,6208,6210],{"class":206,"line":207},[204,6209,6070],{"class":6069},[204,6211,6099],{"class":221},[204,6213,6214,6216],{"class":206,"line":228},[204,6215,6082],{"class":2469},[204,6217,6218],{"class":221}," default.conf\n",[72,6220,6225],{"className":6221,"code":6222,"filename":6223,"language":6224,"meta":80,"style":80},"language-conf shiki shiki-themes material-theme-ocean","\u003CVirtualHost *:80>\n    ServerName laravel_docker\n    DocumentRoot \u002Fvar\u002Fwww\u002Fhtml\u002Fpublic\n\n    \u003CDirectory \"\u002Fvar\u002Fwww\u002Fhtml\u002Fpublic\">\n        AllowOverride All\n    \u003C\u002FDirectory>\n    ErrorLog ${APACHE_LOG_DIR}\u002Ferror.log\n    CustomLog ${APACHE_LOG_DIR}\u002Faccess.log combined\n\u003C\u002FVirtualHost>\n","default.conf","conf",[61,6226,6227,6232,6237,6242,6246,6251,6256,6261,6266,6271],{"__ignoreMap":80},[204,6228,6229],{"class":206,"line":207},[204,6230,6231],{},"\u003CVirtualHost *:80>\n",[204,6233,6234],{"class":206,"line":228},[204,6235,6236],{},"    ServerName laravel_docker\n",[204,6238,6239],{"class":206,"line":235},[204,6240,6241],{},"    DocumentRoot \u002Fvar\u002Fwww\u002Fhtml\u002Fpublic\n",[204,6243,6244],{"class":206,"line":244},[204,6245,232],{"emptyLinePlaceholder":231},[204,6247,6248],{"class":206,"line":249},[204,6249,6250],{},"    \u003CDirectory \"\u002Fvar\u002Fwww\u002Fhtml\u002Fpublic\">\n",[204,6252,6253],{"class":206,"line":257},[204,6254,6255],{},"        AllowOverride All\n",[204,6257,6258],{"class":206,"line":268},[204,6259,6260],{},"    \u003C\u002FDirectory>\n",[204,6262,6263],{"class":206,"line":279},[204,6264,6265],{},"    ErrorLog ${APACHE_LOG_DIR}\u002Ferror.log\n",[204,6267,6268],{"class":206,"line":287},[204,6269,6270],{},"    CustomLog ${APACHE_LOG_DIR}\u002Faccess.log combined\n",[204,6272,6273],{"class":206,"line":296},[204,6274,6275],{},"\u003C\u002FVirtualHost>\n",[13,6277,6278,6279,6282],{},"このファイルはコンテナの",[61,6280,6281],{},"\u002Fetc\u002Fapache2\u002Fsites-enabled\u002F000-default.conf","にマウントされます。",[505,6284,6286],{"id":6285},"docker-composeyamlを作成","docker-compose.yamlを作成",[13,6288,6289],{},"次にdocker-comose.yamlを作成してDBとphpmyadmin",[72,6291,6296],{"className":6292,"code":6293,"filename":6294,"language":6295,"meta":80,"style":80},"language-yaml shiki shiki-themes material-theme-ocean","version: '3'\nservices: \n  web_1:\n    build: .\u002Fweb_1\n    depends_on: \n      - db\n    volumes: \n      - .\u002Fhtml\u002F:\u002Fvar\u002Fwww\u002Fhtml\u002F\n      - .\u002Fweb_1\u002Fdefault.conf:\u002Fetc\u002Fapache2\u002Fsites-enabled\u002F000-default.conf\n    ports: \n      - \"8005:80\"\n  phpmyadmin:\n    image: phpmyadmin\n    restart: always\n    ports:\n      - \"8080:80\"\n    environment:\n     - PMA_ARBITRARY=1\n     - PMA_HOST=db:3306\n     - PMA_USER=root\n     - PMA_PASSWORD=rootroot\n  db:\n    image: mysql:5.7\n    command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci\n    environment:\n      MYSQL_DATABASE: preform\n      MYSQL_USER: test\n      MYSQL_PASSWORD: testtest\n      MYSQL_ROOT_PASSWORD: rootroot\n      TZ: Asia\u002FTokyo\n    ports: \n      - \"3306:3306\"\n    volumes: \n      - vue_laravel:\u002Fvar\u002Flib\u002Fmysql\nvolumes: \n  vue_laravel: {}\n","docker-comose.yaml","yaml",[61,6297,6298,6310,6319,6326,6336,6345,6351,6359,6366,6373,6381,6393,6400,6409,6417,6423,6434,6440,6448,6455,6462,6469,6475,6483,6493,6499,6508,6517,6526,6536,6546,6554,6565,6573,6580,6588],{"__ignoreMap":80},[204,6299,6300,6302,6304,6306,6308],{"class":206,"line":207},[204,6301,211],{"class":210},[204,6303,215],{"class":214},[204,6305,218],{"class":214},[204,6307,1164],{"class":221},[204,6309,225],{"class":214},[204,6311,6312,6314,6316],{"class":206,"line":228},[204,6313,238],{"class":210},[204,6315,215],{"class":214},[204,6317,6318],{"class":606}," \n",[204,6320,6321,6324],{"class":206,"line":235},[204,6322,6323],{"class":210},"  web_1",[204,6325,241],{"class":214},[204,6327,6328,6331,6333],{"class":206,"line":244},[204,6329,6330],{"class":210},"    build",[204,6332,215],{"class":214},[204,6334,6335],{"class":221}," .\u002Fweb_1\n",[204,6337,6338,6341,6343],{"class":206,"line":249},[204,6339,6340],{"class":210},"    depends_on",[204,6342,215],{"class":214},[204,6344,6318],{"class":606},[204,6346,6347,6349],{"class":206,"line":257},[204,6348,290],{"class":214},[204,6350,312],{"class":221},[204,6352,6353,6355,6357],{"class":206,"line":268},[204,6354,351],{"class":210},[204,6356,215],{"class":214},[204,6358,6318],{"class":606},[204,6360,6361,6363],{"class":206,"line":279},[204,6362,290],{"class":214},[204,6364,6365],{"class":221}," .\u002Fhtml\u002F:\u002Fvar\u002Fwww\u002Fhtml\u002F\n",[204,6367,6368,6370],{"class":206,"line":287},[204,6369,290],{"class":214},[204,6371,6372],{"class":221}," .\u002Fweb_1\u002Fdefault.conf:\u002Fetc\u002Fapache2\u002Fsites-enabled\u002F000-default.conf\n",[204,6374,6375,6377,6379],{"class":206,"line":296},[204,6376,282],{"class":210},[204,6378,215],{"class":214},[204,6380,6318],{"class":606},[204,6382,6383,6385,6388,6391],{"class":206,"line":304},[204,6384,290],{"class":214},[204,6386,6387],{"class":214}," \"",[204,6389,6390],{"class":221},"8005:80",[204,6392,695],{"class":214},[204,6394,6395,6398],{"class":206,"line":315},[204,6396,6397],{"class":210},"  phpmyadmin",[204,6399,241],{"class":214},[204,6401,6402,6404,6406],{"class":206,"line":326},[204,6403,260],{"class":210},[204,6405,215],{"class":214},[204,6407,6408],{"class":221}," phpmyadmin\n",[204,6410,6411,6413,6415],{"class":206,"line":337},[204,6412,271],{"class":210},[204,6414,215],{"class":214},[204,6416,276],{"class":221},[204,6418,6419,6421],{"class":206,"line":348},[204,6420,282],{"class":210},[204,6422,241],{"class":214},[204,6424,6425,6427,6429,6432],{"class":206,"line":4},[204,6426,290],{"class":214},[204,6428,6387],{"class":214},[204,6430,6431],{"class":221},"8080:80",[204,6433,695],{"class":214},[204,6435,6436,6438],{"class":206,"line":363},[204,6437,299],{"class":210},[204,6439,241],{"class":214},[204,6441,6442,6445],{"class":206,"line":368},[204,6443,6444],{"class":214},"     -",[204,6446,6447],{"class":221}," PMA_ARBITRARY=1\n",[204,6449,6450,6452],{"class":206,"line":376},[204,6451,6444],{"class":214},[204,6453,6454],{"class":221}," PMA_HOST=db:3306\n",[204,6456,6457,6459],{"class":206,"line":386},[204,6458,6444],{"class":214},[204,6460,6461],{"class":221}," PMA_USER=root\n",[204,6463,6464,6466],{"class":206,"line":395},[204,6465,6444],{"class":214},[204,6467,6468],{"class":221}," PMA_PASSWORD=rootroot\n",[204,6470,6471,6473],{"class":206,"line":402},[204,6472,371],{"class":210},[204,6474,241],{"class":214},[204,6476,6477,6479,6481],{"class":206,"line":412},[204,6478,260],{"class":210},[204,6480,215],{"class":214},[204,6482,383],{"class":221},[204,6484,6485,6488,6490],{"class":206,"line":422},[204,6486,6487],{"class":210},"    command",[204,6489,215],{"class":214},[204,6491,6492],{"class":221}," mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci\n",[204,6494,6495,6497],{"class":206,"line":432},[204,6496,299],{"class":210},[204,6498,241],{"class":214},[204,6500,6501,6503,6505],{"class":206,"line":447},[204,6502,405],{"class":210},[204,6504,215],{"class":214},[204,6506,6507],{"class":221}," preform\n",[204,6509,6510,6512,6514],{"class":206,"line":454},[204,6511,415],{"class":210},[204,6513,215],{"class":214},[204,6515,6516],{"class":221}," test\n",[204,6518,6519,6521,6523],{"class":206,"line":462},[204,6520,425],{"class":210},[204,6522,215],{"class":214},[204,6524,6525],{"class":221}," testtest\n",[204,6527,6528,6531,6533],{"class":206,"line":467},[204,6529,6530],{"class":210},"      MYSQL_ROOT_PASSWORD",[204,6532,215],{"class":214},[204,6534,6535],{"class":221}," rootroot\n",[204,6537,6538,6541,6543],{"class":206,"line":475},[204,6539,6540],{"class":210},"      TZ",[204,6542,215],{"class":214},[204,6544,6545],{"class":221}," Asia\u002FTokyo\n",[204,6547,6548,6550,6552],{"class":206,"line":483},[204,6549,282],{"class":210},[204,6551,215],{"class":214},[204,6553,6318],{"class":606},[204,6555,6556,6558,6560,6563],{"class":206,"line":1413},[204,6557,290],{"class":214},[204,6559,6387],{"class":214},[204,6561,6562],{"class":221},"3306:3306",[204,6564,695],{"class":214},[204,6566,6567,6569,6571],{"class":206,"line":1419},[204,6568,351],{"class":210},[204,6570,215],{"class":214},[204,6572,6318],{"class":606},[204,6574,6575,6577],{"class":206,"line":1424},[204,6576,290],{"class":214},[204,6578,6579],{"class":221}," vue_laravel:\u002Fvar\u002Flib\u002Fmysql\n",[204,6581,6582,6584,6586],{"class":206,"line":1429},[204,6583,470],{"class":210},[204,6585,215],{"class":214},[204,6587,6318],{"class":606},[204,6589,6590,6593,6595],{"class":206,"line":1434},[204,6591,6592],{"class":210},"  vue_laravel",[204,6594,215],{"class":214},[204,6596,6597],{"class":214}," {}\n",[13,6599,6600,6603,6604,6606,6607,6610,6611,6614],{},[61,6601,6602],{},"web_1","サービスでapacheとphpの環境をビルドします。そして",[61,6605,6117],{},"をコンテナ内の",[61,6608,6609],{},"\u002Fvar\u002Fwww\u002Fhtml\u002F","にマウントされます。ubuntuのapacheは",[61,6612,6613],{},"\u002Fetc\u002Fapache2\u002Fsites-enabled\u002F","配下にconfファイルを設置することで設定を追加したりオーバーライドできます。",[13,6616,6617,6619,6620,6623],{},[61,6618,6036],{},"サービスでphpmyadminを起動してDBの内容を編集しやすくします。",[61,6621,6622],{},"db","サービスではmysqlの環境を作成します。ボリュームの設定をして永続化を行います。以上の設定を行ったらコンテナを立ち上げます。",[72,6625,6627],{"className":6052,"code":6626,"language":6054,"meta":80,"style":80},"docker-compose up -d\n\nCreating laravel_vue_phpmyadmin_1 ... done\nCreating laravel_vue_db_1         ... done\nCreating laravel_vue_web_1_1      ... done\n",[61,6628,6629,6640,6644,6658,6670],{"__ignoreMap":80},[204,6630,6631,6634,6637],{"class":206,"line":207},[204,6632,6633],{"class":2469},"docker-compose",[204,6635,6636],{"class":221}," up",[204,6638,6639],{"class":221}," -d\n",[204,6641,6642],{"class":206,"line":228},[204,6643,232],{"emptyLinePlaceholder":231},[204,6645,6646,6649,6652,6655],{"class":206,"line":235},[204,6647,6648],{"class":2469},"Creating",[204,6650,6651],{"class":221}," laravel_vue_phpmyadmin_1",[204,6653,6654],{"class":221}," ...",[204,6656,6657],{"class":221}," done\n",[204,6659,6660,6662,6665,6668],{"class":206,"line":244},[204,6661,6648],{"class":2469},[204,6663,6664],{"class":221}," laravel_vue_db_1",[204,6666,6667],{"class":221},"         ...",[204,6669,6657],{"class":221},[204,6671,6672,6674,6677,6680],{"class":206,"line":249},[204,6673,6648],{"class":2469},[204,6675,6676],{"class":221}," laravel_vue_web_1_1",[204,6678,6679],{"class":221},"      ...",[204,6681,6657],{"class":221},[13,6683,6684],{},"そしてコンテナ内に入ってLaravelをインストールしていきます。",[72,6686,6689],{"className":6687,"code":6688,"language":77},[75],"docker exec -it laravel_vue_web_1_1 \u002Fbin\u002Fbash\n\nroot@a790844c74d6:\u002Fvar\u002Fwww\u002Fhtml# composer \nroot@a790844c74d6:\u002Fvar\u002Fwww\u002Fhtml# composer create-project laravel\u002Flaravel .\u002F\n",[61,6690,6688],{"__ignoreMap":80},[13,6692,6693,6694,6697,6698,6701,6702,6704],{},"これで",[61,6695,6696],{},"\u002Fvar\u002Fwww\u002Fhtml","配下にLaravelプロジェクトが入ります。コンテナの",[61,6699,6700],{}," \u002Fvar\u002Fwww\u002Fhtml","はホストマシンの",[61,6703,6117],{},"に同期されます。",[13,6706,6707,6709,6710,6713],{},[61,6708,6117],{},"配下にソースが生成されたのを確認しましたら、",[61,6711,6712],{},".env","ファイルにDBの接続設定を記述します。",[72,6715,6718],{"className":6716,"code":6717,"filename":6712,"language":77,"meta":80},[75],"DB_CONNECTION=mysql\nDB_HOST=db\nDB_PORT=3306\nDB_DATABASE=laravel\nDB_USERNAME=root\nDB_PASSWORD=rootroot\n",[61,6719,6717],{"__ignoreMap":80},[13,6721,6722,6723,6725,6726,6729,6730,6733],{},"phpmyadminとかで",[61,6724,3988],{},"データベースは作成しておいてください。",[61,6727,6728],{},"DB_HOST","はdockerのDBのサービス名を入力します。設定したらブラウザで",[61,6731,6732],{},"localhost:8005","にアクセスします。以下の様なウェルカムページが表示されれば成功です。",[522,6735],{":src":6736,":center":526},"'vue_laravel_app\u002Flaravel_welcome.png'",[13,6738,6739],{},"（特に断りがない限り、以降ではDockerコンテナ内の操作とします。）",[27,6741,6743],{"id":6742},"laravelとvueセットアップ","LaravelとVueセットアップ",[13,6745,6746,6747,6750],{},"それではアセットと認証機能のセットアップをしましょう。今回は",[61,6748,6749],{},"laravel\u002Fbreeze","をスターターキットとして利用します。",[72,6752,6755],{"className":6753,"code":6754,"language":77},[75],"composer require laravel\u002Fbreeze --dev\n\nphp artisan breeze:install\nBreeze scaffolding installed successfully.\nPlease execute the \"npm install && npm run dev\" command to build your assets.\n\nnpm install\nnpm run dev\nphp artisan migrate\nMigration table created successfully.\nMigrating: 2014_10_12_000000_create_users_table\nMigrated:  2014_10_12_000000_create_users_table (23.69ms)\nMigrating: 2014_10_12_100000_create_password_resets_table\nMigrated:  2014_10_12_100000_create_password_resets_table (10.45ms)\nMigrating: 2019_08_19_000000_create_failed_jobs_table\nMigrated:  2019_08_19_000000_create_failed_jobs_table (10.60ms)\nMigrating: 2019_12_14_000001_create_personal_access_tokens_table\nMigrated:  2019_12_14_000001_create_personal_access_tokens_table (14.54ms)\n",[61,6756,6754],{"__ignoreMap":80},[13,6758,6759],{},"マイグレーションが完了したので登録などができる様になっているはずです。ひとまずLaravelのセットアップは以上となります。",[27,6761,6763],{"id":6762},"とりあえずspa構成ができるかチェック","とりあえずSPA構成ができるかチェック",[13,6765,6766],{},"次は管理画面のフロントエンドのセットアップをしていきます。管理画面はvue.jsで作成し、公開側コンテンツはLaravelのサーバーサイドレンダリングをします。ここではSPA構成のセットアップをします。",[505,6768,6770],{"id":6769},"spaのビューとルートの設定","SPAのビューとルートの設定",[13,6772,6773,6776],{},[61,6774,6775],{},"\u002Fsystem","配下を管理画面として構築していきます。以下の様なビューテンプレートとルートを作成します。",[72,6778,6780],{"className":539,"code":6779,"filename":4661,"language":541,"meta":80,"style":80},"Route::get('\u002Fsystem\u002F{path?}', function () {\n    return view('admin');\n})->where('path', '.*')->name('admin');\n",[61,6781,6782,6787,6792],{"__ignoreMap":80},[204,6783,6784],{"class":206,"line":207},[204,6785,6786],{},"Route::get('\u002Fsystem\u002F{path?}', function () {\n",[204,6788,6789],{"class":206,"line":228},[204,6790,6791],{},"    return view('admin');\n",[204,6793,6794],{"class":206,"line":235},[204,6795,6796],{},"})->where('path', '.*')->name('admin');\n",[13,6798,6799,6800,6802],{},"このルートは",[61,6801,6775],{},"配下すべてのルートに対してadminビューを返すことを示しています。",[72,6804,6807],{"className":539,"code":6805,"filename":6806,"language":541,"meta":80,"style":80},"\u003C!DOCTYPE html>\n\u003Chtml lang=\"{{ str_replace('_', '-', app()->getLocale()) }}\">\n    \u003Chead>\n        \u003Cmeta charset=\"utf-8\">\n        \u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n        \u003Cmeta name=\"csrf-token\" content=\"{{ csrf_token() }}\">\n\n        \u003Ctitle>{{ config('app.name', 'Laravel') }}\u003C\u002Ftitle>\n\n        \u003C!-- Fonts -->\n        \u003Clink rel=\"stylesheet\" href=\"https:\u002F\u002Ffonts.googleapis.com\u002Fcss2?family=Nunito:wght@400;600;700&display=swap\">\n\n        \u003C!-- Styles -->\n        \u003Clink rel=\"stylesheet\" href=\"{{ asset('css\u002Fapp.css') }}\">\n\n    \u003C\u002Fhead>\n    \u003Cbody>\n        \u003Cdiv class=\"font-sans text-gray-900 antialiased\">\n            \u003Cdiv id=\"app\">\n                \u003Crouter-view\u002F>\n            \u003C\u002Fdiv>\n        \u003C\u002Fdiv>\n    \u003C\u002Fbody>\n    \u003C!-- Scripts -->\n    \u003Cscript src=\"{{ asset('js\u002Fadmin\u002Findex.js') }}\" defer>\u003C\u002Fscript>\n\u003C\u002Fhtml>\n","resources\u002Fview\u002Fadmin.blade.php",[61,6808,6809,6814,6819,6824,6829,6834,6839,6843,6848,6852,6857,6862,6866,6871,6876,6880,6885,6890,6895,6900,6905,6910,6915,6920,6925,6930],{"__ignoreMap":80},[204,6810,6811],{"class":206,"line":207},[204,6812,6813],{},"\u003C!DOCTYPE html>\n",[204,6815,6816],{"class":206,"line":228},[204,6817,6818],{},"\u003Chtml lang=\"{{ str_replace('_', '-', app()->getLocale()) }}\">\n",[204,6820,6821],{"class":206,"line":235},[204,6822,6823],{},"    \u003Chead>\n",[204,6825,6826],{"class":206,"line":244},[204,6827,6828],{},"        \u003Cmeta charset=\"utf-8\">\n",[204,6830,6831],{"class":206,"line":249},[204,6832,6833],{},"        \u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n",[204,6835,6836],{"class":206,"line":257},[204,6837,6838],{},"        \u003Cmeta name=\"csrf-token\" content=\"{{ csrf_token() }}\">\n",[204,6840,6841],{"class":206,"line":268},[204,6842,232],{"emptyLinePlaceholder":231},[204,6844,6845],{"class":206,"line":279},[204,6846,6847],{},"        \u003Ctitle>{{ config('app.name', 'Laravel') }}\u003C\u002Ftitle>\n",[204,6849,6850],{"class":206,"line":287},[204,6851,232],{"emptyLinePlaceholder":231},[204,6853,6854],{"class":206,"line":296},[204,6855,6856],{},"        \u003C!-- Fonts -->\n",[204,6858,6859],{"class":206,"line":304},[204,6860,6861],{},"        \u003Clink rel=\"stylesheet\" href=\"https:\u002F\u002Ffonts.googleapis.com\u002Fcss2?family=Nunito:wght@400;600;700&display=swap\">\n",[204,6863,6864],{"class":206,"line":315},[204,6865,232],{"emptyLinePlaceholder":231},[204,6867,6868],{"class":206,"line":326},[204,6869,6870],{},"        \u003C!-- Styles -->\n",[204,6872,6873],{"class":206,"line":337},[204,6874,6875],{},"        \u003Clink rel=\"stylesheet\" href=\"{{ asset('css\u002Fapp.css') }}\">\n",[204,6877,6878],{"class":206,"line":348},[204,6879,232],{"emptyLinePlaceholder":231},[204,6881,6882],{"class":206,"line":4},[204,6883,6884],{},"    \u003C\u002Fhead>\n",[204,6886,6887],{"class":206,"line":363},[204,6888,6889],{},"    \u003Cbody>\n",[204,6891,6892],{"class":206,"line":368},[204,6893,6894],{},"        \u003Cdiv class=\"font-sans text-gray-900 antialiased\">\n",[204,6896,6897],{"class":206,"line":376},[204,6898,6899],{},"            \u003Cdiv id=\"app\">\n",[204,6901,6902],{"class":206,"line":386},[204,6903,6904],{},"                \u003Crouter-view\u002F>\n",[204,6906,6907],{"class":206,"line":395},[204,6908,6909],{},"            \u003C\u002Fdiv>\n",[204,6911,6912],{"class":206,"line":402},[204,6913,6914],{},"        \u003C\u002Fdiv>\n",[204,6916,6917],{"class":206,"line":412},[204,6918,6919],{},"    \u003C\u002Fbody>\n",[204,6921,6922],{"class":206,"line":422},[204,6923,6924],{},"    \u003C!-- Scripts -->\n",[204,6926,6927],{"class":206,"line":432},[204,6928,6929],{},"    \u003Cscript src=\"{{ asset('js\u002Fadmin\u002Findex.js') }}\" defer>\u003C\u002Fscript>\n",[204,6931,6932],{"class":206,"line":447},[204,6933,6934],{},"\u003C\u002Fhtml>\n",[13,6936,6937,6938,6941,6942,6945],{},"テンプレートは上記の様にして",[61,6939,6940],{},"\u003Cdiv id=\"app\">\u003Crouter-view\u002F>\u003C\u002Fdiv>","を設置して、vueのエントリーポイントと、vueのビルドファイルを読み込む様にします。実際はヘッダーなどを分けたほうがいいですが、今はとりあえずこんな感じでOKです。\n次に",[61,6943,6944],{},"'js\u002Fadmin\u002Findex.js","で読み込ませるjsファイルを作成していきます。",[505,6947,6948],{"id":6948},"必要なライブラリをインストール",[13,6950,6951,6952,6955,6956,5423,6959,6962,6963,4718,6965,5423,6968,6971],{},"一昔前の",[61,6953,6954],{},"laravel\u002Fui","の時は",[61,6957,6958],{},"vue",[61,6960,6961],{},"bootstrap","の設定がインストール時に自動設定されますが、今回の",[61,6964,6749],{},[61,6966,6967],{},"alpinejs",[61,6969,6970],{},"tailwindcss","が入っているので、vue一式を自分でインストールする必要があります。まずはVue・Vuex・Vue-Routerをインストールしましょう。",[2401,6973,6975],{"className":6974},[2404,4814],"\nこの記事を作成した2021年3月現在、Vue3がリリースされておりバージョンを指定しないと、それぞれ最新版がインストールされる可能性があります。この記事はVue2を用いて解説しますのでバージョンを指定してインストールしています。\n",[72,6977,6980],{"className":6978,"code":6979,"language":77},[75],"npm install vue@2 vue-loader@15 vue-template-compiler@2 --save-dev\nnpm install vuex@3 vue-router@3\n",[61,6981,6979],{"__ignoreMap":80},[505,6983,6985],{"id":6984},"spa用のjsファイルを作成する","SPA用のJSファイルを作成する",[13,6987,6988,6989,6992],{},"それでは",[61,6990,6991],{},"resources\u002Fjs","配下にSPAのファイルを作成を行います。以下の様にファイルとディレクトリを作成してください。",[72,6994,6997],{"className":6995,"code":6996,"language":77},[75],"js\n├── admin\n│   ├── index.js\n│   ├── router.js\n│   └── store.js\n└── vue\n    └── page\n        ├── Home.vue\n        └── Profile.vue\n",[61,6998,6996],{"__ignoreMap":80},[13,7000,7001],{},"store.jsとrouter.jsは以下の様にします。",[72,7003,7007],{"className":7004,"code":7005,"filename":7006,"language":3989,"meta":80,"style":80},"language-js shiki shiki-themes material-theme-ocean","export default {\n    state:{},\n    getters: {},\n    mutations: {},\n    actions: {}\n}\n","store.js",[61,7008,7009,7021,7029,7039,7048,7057],{"__ignoreMap":80},[204,7010,7011,7015,7018],{"class":206,"line":207},[204,7012,7014],{"class":7013},"s6cf3","export",[204,7016,7017],{"class":7013}," default",[204,7019,7020],{"class":214}," {\n",[204,7022,7023,7026],{"class":206,"line":228},[204,7024,7025],{"class":210},"    state",[204,7027,7028],{"class":214},":{},\n",[204,7030,7031,7034,7036],{"class":206,"line":235},[204,7032,7033],{"class":210},"    getters",[204,7035,215],{"class":214},[204,7037,7038],{"class":214}," {},\n",[204,7040,7041,7044,7046],{"class":206,"line":244},[204,7042,7043],{"class":210},"    mutations",[204,7045,215],{"class":214},[204,7047,7038],{"class":214},[204,7049,7050,7053,7055],{"class":206,"line":249},[204,7051,7052],{"class":210},"    actions",[204,7054,215],{"class":214},[204,7056,6597],{"class":214},[204,7058,7059],{"class":206,"line":257},[204,7060,804],{"class":214},[13,7062,7063],{},"storeはひとまず必要な型だけ準備します。",[72,7065,7068],{"className":7004,"code":7066,"filename":7067,"language":3989,"meta":80,"style":80},"import Home from '~js\u002Fvue\u002Fpage\u002FHome.vue';\nimport Profile from '~js\u002Fvue\u002Fpage\u002FProfile.vue';\n\nconst routes = [\n    { path: '\u002Fsystem', component: Home },\n    { path: '\u002Fsystem\u002Fprofile', component: Profile },\n]\n\nexport default routes;\n","router.js",[61,7069,7070,7092,7110,7114,7128,7156,7181,7185,7189],{"__ignoreMap":80},[204,7071,7072,7075,7078,7081,7083,7086,7089],{"class":206,"line":207},[204,7073,7074],{"class":7013},"import",[204,7076,7077],{"class":606}," Home ",[204,7079,7080],{"class":7013},"from",[204,7082,218],{"class":214},[204,7084,7085],{"class":221},"~js\u002Fvue\u002Fpage\u002FHome.vue",[204,7087,7088],{"class":214},"'",[204,7090,7091],{"class":214},";\n",[204,7093,7094,7096,7099,7101,7103,7106,7108],{"class":206,"line":228},[204,7095,7074],{"class":7013},[204,7097,7098],{"class":606}," Profile ",[204,7100,7080],{"class":7013},[204,7102,218],{"class":214},[204,7104,7105],{"class":221},"~js\u002Fvue\u002Fpage\u002FProfile.vue",[204,7107,7088],{"class":214},[204,7109,7091],{"class":214},[204,7111,7112],{"class":206,"line":235},[204,7113,232],{"emptyLinePlaceholder":231},[204,7115,7116,7119,7122,7125],{"class":206,"line":244},[204,7117,7118],{"class":620},"const",[204,7120,7121],{"class":606}," routes ",[204,7123,7124],{"class":214},"=",[204,7126,7127],{"class":606}," [\n",[204,7129,7130,7133,7136,7138,7140,7142,7144,7146,7149,7151,7153],{"class":206,"line":249},[204,7131,7132],{"class":214},"    {",[204,7134,7135],{"class":210}," path",[204,7137,215],{"class":214},[204,7139,218],{"class":214},[204,7141,6775],{"class":221},[204,7143,7088],{"class":214},[204,7145,4563],{"class":214},[204,7147,7148],{"class":210}," component",[204,7150,215],{"class":214},[204,7152,7077],{"class":606},[204,7154,7155],{"class":214},"},\n",[204,7157,7158,7160,7162,7164,7166,7169,7171,7173,7175,7177,7179],{"class":206,"line":257},[204,7159,7132],{"class":214},[204,7161,7135],{"class":210},[204,7163,215],{"class":214},[204,7165,218],{"class":214},[204,7167,7168],{"class":221},"\u002Fsystem\u002Fprofile",[204,7170,7088],{"class":214},[204,7172,4563],{"class":214},[204,7174,7148],{"class":210},[204,7176,215],{"class":214},[204,7178,7098],{"class":606},[204,7180,7155],{"class":214},[204,7182,7183],{"class":206,"line":268},[204,7184,709],{"class":606},[204,7186,7187],{"class":206,"line":279},[204,7188,232],{"emptyLinePlaceholder":231},[204,7190,7191,7193,7195,7198],{"class":206,"line":287},[204,7192,7014],{"class":7013},[204,7194,7017],{"class":7013},[204,7196,7197],{"class":606}," routes",[204,7199,7091],{"class":214},[13,7201,7202,7203,7206],{},"router.jsでは指定のパスに対してどのコンポーネントを出すかを定義します。",[61,7204,7205],{},"~js","という記述は後で解説しますので、ひとまず記述してください。",[13,7208,7209],{},"router.jsで参照しているコンポーネントは以下の様にしておきます。",[72,7211,7215],{"className":7212,"code":7213,"filename":7214,"language":6958,"meta":80,"style":80},"language-vue shiki shiki-themes material-theme-ocean","\u003Ctemplate>\n    \u003Cdiv>\n        home\n        \u003Crouter-link to=\"\u002Fsystem\u002Fprofile\">profile\u003C\u002Frouter-link>\n    \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\u003Cscript>\nexport default {\n    name:'home'\n}\n\u003C\u002Fscript>\n","Home.vue",[61,7216,7217,7228,7237,7242,7274,7283,7291,7300,7308,7322,7326],{"__ignoreMap":80},[204,7218,7219,7222,7225],{"class":206,"line":207},[204,7220,7221],{"class":214},"\u003C",[204,7223,7224],{"class":210},"template",[204,7226,7227],{"class":214},">\n",[204,7229,7230,7233,7235],{"class":206,"line":228},[204,7231,7232],{"class":214},"    \u003C",[204,7234,2401],{"class":210},[204,7236,7227],{"class":214},[204,7238,7239],{"class":206,"line":235},[204,7240,7241],{"class":606},"        home\n",[204,7243,7244,7247,7250,7253,7255,7257,7259,7261,7264,7267,7270,7272],{"class":206,"line":244},[204,7245,7246],{"class":214},"        \u003C",[204,7248,7249],{"class":210},"router-link",[204,7251,7252],{"class":620}," to",[204,7254,7124],{"class":214},[204,7256,624],{"class":214},[204,7258,7168],{"class":221},[204,7260,624],{"class":214},[204,7262,7263],{"class":214},">",[204,7265,7266],{"class":606},"profile",[204,7268,7269],{"class":214},"\u003C\u002F",[204,7271,7249],{"class":210},[204,7273,7227],{"class":214},[204,7275,7276,7279,7281],{"class":206,"line":249},[204,7277,7278],{"class":214},"    \u003C\u002F",[204,7280,2401],{"class":210},[204,7282,7227],{"class":214},[204,7284,7285,7287,7289],{"class":206,"line":257},[204,7286,7269],{"class":214},[204,7288,7224],{"class":210},[204,7290,7227],{"class":214},[204,7292,7293,7295,7298],{"class":206,"line":268},[204,7294,7221],{"class":214},[204,7296,7297],{"class":210},"script",[204,7299,7227],{"class":214},[204,7301,7302,7304,7306],{"class":206,"line":279},[204,7303,7014],{"class":7013},[204,7305,7017],{"class":7013},[204,7307,7020],{"class":214},[204,7309,7310,7313,7315,7317,7320],{"class":206,"line":287},[204,7311,7312],{"class":210},"    name",[204,7314,215],{"class":214},[204,7316,7088],{"class":214},[204,7318,7319],{"class":221},"home",[204,7321,225],{"class":214},[204,7323,7324],{"class":206,"line":296},[204,7325,804],{"class":214},[204,7327,7328,7330,7332],{"class":206,"line":304},[204,7329,7269],{"class":214},[204,7331,7297],{"class":210},[204,7333,7227],{"class":214},[72,7335,7338],{"className":7212,"code":7336,"filename":7337,"language":6958,"meta":80,"style":80},"\u003Ctemplate>\n    \u003Cdiv>\n        profile\n    \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\u003Cscript>\nexport default {\n    name:'profile'\n}\n\u003C\u002Fscript>\n","Profile.vue",[61,7339,7340,7348,7356,7361,7369,7377,7385,7393,7405,7409],{"__ignoreMap":80},[204,7341,7342,7344,7346],{"class":206,"line":207},[204,7343,7221],{"class":214},[204,7345,7224],{"class":210},[204,7347,7227],{"class":214},[204,7349,7350,7352,7354],{"class":206,"line":228},[204,7351,7232],{"class":214},[204,7353,2401],{"class":210},[204,7355,7227],{"class":214},[204,7357,7358],{"class":206,"line":235},[204,7359,7360],{"class":606},"        profile\n",[204,7362,7363,7365,7367],{"class":206,"line":244},[204,7364,7278],{"class":214},[204,7366,2401],{"class":210},[204,7368,7227],{"class":214},[204,7370,7371,7373,7375],{"class":206,"line":249},[204,7372,7269],{"class":214},[204,7374,7224],{"class":210},[204,7376,7227],{"class":214},[204,7378,7379,7381,7383],{"class":206,"line":257},[204,7380,7221],{"class":214},[204,7382,7297],{"class":210},[204,7384,7227],{"class":214},[204,7386,7387,7389,7391],{"class":206,"line":268},[204,7388,7014],{"class":7013},[204,7390,7017],{"class":7013},[204,7392,7020],{"class":214},[204,7394,7395,7397,7399,7401,7403],{"class":206,"line":279},[204,7396,7312],{"class":210},[204,7398,215],{"class":214},[204,7400,7088],{"class":214},[204,7402,7266],{"class":221},[204,7404,225],{"class":214},[204,7406,7407],{"class":206,"line":287},[204,7408,804],{"class":214},[204,7410,7411,7413,7415],{"class":206,"line":296},[204,7412,7269],{"class":214},[204,7414,7297],{"class":210},[204,7416,7227],{"class":214},[13,7418,7419],{},"index.jsは以下の様に記述します。",[72,7421,7424],{"className":7004,"code":7422,"filename":7423,"language":3989,"meta":80,"style":80},"window.axios = require('axios');\nwindow.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';\n\nimport Vue from 'vue';\n\nimport Vuex from 'vuex';\nimport index from '.\u002Fstore';\nVue.use(Vuex);\nconst store = new Vuex.Store(index);\n\nimport VueRouter from 'vue-router'\nimport routes from '.\u002Frouter';\nVue.use(VueRouter)\nconst router =  new VueRouter({mode: 'history',routes})\n\nconst app = new Vue({\n    store,\n    router,\n}).$mount(\"#app\");\n","index.js",[61,7425,7426,7456,7500,7504,7521,7525,7543,7561,7576,7601,7605,7621,7638,7649,7691,7695,7713,7720,7727],{"__ignoreMap":80},[204,7427,7428,7431,7433,7436,7438,7441,7444,7446,7449,7451,7454],{"class":206,"line":207},[204,7429,7430],{"class":606},"window",[204,7432,3110],{"class":214},[204,7434,7435],{"class":606},"axios ",[204,7437,7124],{"class":214},[204,7439,7440],{"class":6069}," require",[204,7442,7443],{"class":606},"(",[204,7445,7088],{"class":214},[204,7447,7448],{"class":221},"axios",[204,7450,7088],{"class":214},[204,7452,7453],{"class":606},")",[204,7455,7091],{"class":214},[204,7457,7458,7460,7462,7464,7466,7469,7471,7474,7476,7479,7481,7484,7486,7489,7491,7493,7496,7498],{"class":206,"line":228},[204,7459,7430],{"class":606},[204,7461,3110],{"class":214},[204,7463,7448],{"class":606},[204,7465,3110],{"class":214},[204,7467,7468],{"class":606},"defaults",[204,7470,3110],{"class":214},[204,7472,7473],{"class":606},"headers",[204,7475,3110],{"class":214},[204,7477,7478],{"class":606},"common[",[204,7480,7088],{"class":214},[204,7482,7483],{"class":221},"X-Requested-With",[204,7485,7088],{"class":214},[204,7487,7488],{"class":606},"] ",[204,7490,7124],{"class":214},[204,7492,218],{"class":214},[204,7494,7495],{"class":221},"XMLHttpRequest",[204,7497,7088],{"class":214},[204,7499,7091],{"class":214},[204,7501,7502],{"class":206,"line":235},[204,7503,232],{"emptyLinePlaceholder":231},[204,7505,7506,7508,7511,7513,7515,7517,7519],{"class":206,"line":244},[204,7507,7074],{"class":7013},[204,7509,7510],{"class":606}," Vue ",[204,7512,7080],{"class":7013},[204,7514,218],{"class":214},[204,7516,6958],{"class":221},[204,7518,7088],{"class":214},[204,7520,7091],{"class":214},[204,7522,7523],{"class":206,"line":249},[204,7524,232],{"emptyLinePlaceholder":231},[204,7526,7527,7529,7532,7534,7536,7539,7541],{"class":206,"line":257},[204,7528,7074],{"class":7013},[204,7530,7531],{"class":606}," Vuex ",[204,7533,7080],{"class":7013},[204,7535,218],{"class":214},[204,7537,7538],{"class":221},"vuex",[204,7540,7088],{"class":214},[204,7542,7091],{"class":214},[204,7544,7545,7547,7550,7552,7554,7557,7559],{"class":206,"line":268},[204,7546,7074],{"class":7013},[204,7548,7549],{"class":606}," index ",[204,7551,7080],{"class":7013},[204,7553,218],{"class":214},[204,7555,7556],{"class":221},".\u002Fstore",[204,7558,7088],{"class":214},[204,7560,7091],{"class":214},[204,7562,7563,7566,7568,7571,7574],{"class":206,"line":279},[204,7564,7565],{"class":606},"Vue",[204,7567,3110],{"class":214},[204,7569,7570],{"class":6069},"use",[204,7572,7573],{"class":606},"(Vuex)",[204,7575,7091],{"class":214},[204,7577,7578,7580,7583,7585,7588,7591,7593,7596,7599],{"class":206,"line":287},[204,7579,7118],{"class":620},[204,7581,7582],{"class":606}," store ",[204,7584,7124],{"class":214},[204,7586,7587],{"class":214}," new",[204,7589,7590],{"class":606}," Vuex",[204,7592,3110],{"class":214},[204,7594,7595],{"class":6069},"Store",[204,7597,7598],{"class":606},"(index)",[204,7600,7091],{"class":214},[204,7602,7603],{"class":206,"line":296},[204,7604,232],{"emptyLinePlaceholder":231},[204,7606,7607,7609,7612,7614,7616,7619],{"class":206,"line":304},[204,7608,7074],{"class":7013},[204,7610,7611],{"class":606}," VueRouter ",[204,7613,7080],{"class":7013},[204,7615,218],{"class":214},[204,7617,7618],{"class":221},"vue-router",[204,7620,225],{"class":214},[204,7622,7623,7625,7627,7629,7631,7634,7636],{"class":206,"line":315},[204,7624,7074],{"class":7013},[204,7626,7121],{"class":606},[204,7628,7080],{"class":7013},[204,7630,218],{"class":214},[204,7632,7633],{"class":221},".\u002Frouter",[204,7635,7088],{"class":214},[204,7637,7091],{"class":214},[204,7639,7640,7642,7644,7646],{"class":206,"line":326},[204,7641,7565],{"class":606},[204,7643,3110],{"class":214},[204,7645,7570],{"class":6069},[204,7647,7648],{"class":606},"(VueRouter)\n",[204,7650,7651,7653,7656,7658,7661,7664,7666,7669,7672,7674,7676,7679,7681,7683,7686,7689],{"class":206,"line":337},[204,7652,7118],{"class":620},[204,7654,7655],{"class":606}," router ",[204,7657,7124],{"class":214},[204,7659,7660],{"class":214},"  new",[204,7662,7663],{"class":6069}," VueRouter",[204,7665,7443],{"class":606},[204,7667,7668],{"class":214},"{",[204,7670,7671],{"class":210},"mode",[204,7673,215],{"class":214},[204,7675,218],{"class":214},[204,7677,7678],{"class":221},"history",[204,7680,7088],{"class":214},[204,7682,4563],{"class":214},[204,7684,7685],{"class":606},"routes",[204,7687,7688],{"class":214},"}",[204,7690,868],{"class":606},[204,7692,7693],{"class":206,"line":348},[204,7694,232],{"emptyLinePlaceholder":231},[204,7696,7697,7699,7702,7704,7706,7709,7711],{"class":206,"line":4},[204,7698,7118],{"class":620},[204,7700,7701],{"class":606}," app ",[204,7703,7124],{"class":214},[204,7705,7587],{"class":214},[204,7707,7708],{"class":6069}," Vue",[204,7710,7443],{"class":606},[204,7712,2450],{"class":214},[204,7714,7715,7718],{"class":206,"line":363},[204,7716,7717],{"class":606},"    store",[204,7719,636],{"class":214},[204,7721,7722,7725],{"class":206,"line":368},[204,7723,7724],{"class":606},"    router",[204,7726,636],{"class":214},[204,7728,7729,7731,7733,7735,7738,7740,7742,7745,7747,7749],{"class":206,"line":376},[204,7730,7688],{"class":214},[204,7732,7453],{"class":606},[204,7734,3110],{"class":214},[204,7736,7737],{"class":6069},"$mount",[204,7739,7443],{"class":606},[204,7741,624],{"class":214},[204,7743,7744],{"class":221},"#app",[204,7746,624],{"class":214},[204,7748,7453],{"class":606},[204,7750,7091],{"class":214},[13,7752,7753,7754,7757],{},"axiosはLaravelインストール時に入っているのでそのまま利用しています。このファイルではVuexとVue-routerの連携を行い、最終的に",[61,7755,7756],{},"\u003Cdiv id=\"app\">\u003C\u002Fdiv>","にレンダリングされる様にします。",[13,7759,7760,7761,7764],{},"上記のファイルを作成した後、",[61,7762,7763],{},"webpac.mix.js","を修正します。",[505,7766,7767],{"id":7767},"mixファイルの作成",[13,7769,7770,7771,7773,7774,7777,7778,7781,7782,7785],{},"このようなVueのプロジェクトwebpackを使用しますがLaravelは簡単な記述で",[61,7772,4641],{},"配下を簡単に",[61,7775,7776],{},"public\u002F","配下に出力してくれます。またvueやsassのコンパイル設定を",[61,7779,7780],{},"webpack.config.js","の様に書く必要がありません。どのファイルをコンパイル対象にするかなどは",[61,7783,7784],{},"webpack.mix.js","に記載します。以下の様に修正します。",[72,7787,7789],{"className":7004,"code":7788,"filename":7780,"language":3989,"meta":80,"style":80},"const mix = require('laravel-mix');\nconst path = require('path');\n\nmix.webpackConfig({\n    resolve: {\n      alias: {\n        '~js': path.resolve('.\u002Fresources\u002Fjs'),\n      }\n    }\n});\nmix.js('resources\u002Fjs\u002Fadmin\u002Findex.js', 'public\u002Fjs\u002Fadmin').vue();\n",[61,7790,7791,7815,7839,7843,7857,7866,7875,7906,7911,7915,7923],{"__ignoreMap":80},[204,7792,7793,7795,7798,7800,7802,7804,7806,7809,7811,7813],{"class":206,"line":207},[204,7794,7118],{"class":620},[204,7796,7797],{"class":606}," mix ",[204,7799,7124],{"class":214},[204,7801,7440],{"class":6069},[204,7803,7443],{"class":606},[204,7805,7088],{"class":214},[204,7807,7808],{"class":221},"laravel-mix",[204,7810,7088],{"class":214},[204,7812,7453],{"class":606},[204,7814,7091],{"class":214},[204,7816,7817,7819,7822,7824,7826,7828,7830,7833,7835,7837],{"class":206,"line":228},[204,7818,7118],{"class":620},[204,7820,7821],{"class":606}," path ",[204,7823,7124],{"class":214},[204,7825,7440],{"class":6069},[204,7827,7443],{"class":606},[204,7829,7088],{"class":214},[204,7831,7832],{"class":221},"path",[204,7834,7088],{"class":214},[204,7836,7453],{"class":606},[204,7838,7091],{"class":214},[204,7840,7841],{"class":206,"line":235},[204,7842,232],{"emptyLinePlaceholder":231},[204,7844,7845,7848,7850,7853,7855],{"class":206,"line":244},[204,7846,7847],{"class":606},"mix",[204,7849,3110],{"class":214},[204,7851,7852],{"class":6069},"webpackConfig",[204,7854,7443],{"class":606},[204,7856,2450],{"class":214},[204,7858,7859,7862,7864],{"class":206,"line":249},[204,7860,7861],{"class":210},"    resolve",[204,7863,215],{"class":214},[204,7865,7020],{"class":214},[204,7867,7868,7871,7873],{"class":206,"line":257},[204,7869,7870],{"class":210},"      alias",[204,7872,215],{"class":214},[204,7874,7020],{"class":214},[204,7876,7877,7880,7882,7884,7886,7888,7890,7893,7895,7897,7900,7902,7904],{"class":206,"line":268},[204,7878,7879],{"class":214},"        '",[204,7881,7205],{"class":210},[204,7883,7088],{"class":214},[204,7885,215],{"class":214},[204,7887,7135],{"class":606},[204,7889,3110],{"class":214},[204,7891,7892],{"class":6069},"resolve",[204,7894,7443],{"class":606},[204,7896,7088],{"class":214},[204,7898,7899],{"class":221},".\u002Fresources\u002Fjs",[204,7901,7088],{"class":214},[204,7903,7453],{"class":606},[204,7905,636],{"class":214},[204,7907,7908],{"class":206,"line":279},[204,7909,7910],{"class":214},"      }\n",[204,7912,7913],{"class":206,"line":287},[204,7914,785],{"class":214},[204,7916,7917,7919,7921],{"class":206,"line":296},[204,7918,7688],{"class":214},[204,7920,7453],{"class":606},[204,7922,7091],{"class":214},[204,7924,7925,7927,7929,7931,7933,7935,7938,7940,7942,7944,7947,7949,7951,7953,7955,7958],{"class":206,"line":304},[204,7926,7847],{"class":606},[204,7928,3110],{"class":214},[204,7930,3989],{"class":6069},[204,7932,7443],{"class":606},[204,7934,7088],{"class":214},[204,7936,7937],{"class":221},"resources\u002Fjs\u002Fadmin\u002Findex.js",[204,7939,7088],{"class":214},[204,7941,4563],{"class":214},[204,7943,218],{"class":214},[204,7945,7946],{"class":221},"public\u002Fjs\u002Fadmin",[204,7948,7088],{"class":214},[204,7950,7453],{"class":606},[204,7952,3110],{"class":214},[204,7954,6958],{"class":6069},[204,7956,7957],{"class":606},"()",[204,7959,7091],{"class":214},[13,7961,7962,7965,7966,7968],{},[61,7963,7964],{},"mix.webpackConfig","はのように",[61,7967,7780],{},"に書く様な他の細かいwebpackの設定を定義でき、ここではエイリアスを定義しています。",[13,7970,7971,7974,7975,7977,7978,7980],{},[61,7972,7973],{},"'~js': path.resolve('.\u002Fresources\u002Fjs')","という記述は",[61,7976,7205],{},"というパスの記載があった場合は「",[61,7979,6991],{},"までの絶対ルート」であるとwebpackに支持しています。この様にエイリアスを定義することで相対パスによるファイルの参照がなくなります。router.jsで以下の様に定義していましたが、",[72,7982,7984],{"className":7004,"code":7983,"filename":7067,"language":3989,"meta":80,"style":80},"import Home from '~js\u002Fvue\u002Fpage\u002FHome.vue';\nimport Profile from '~js\u002Fvue\u002Fpage\u002FProfile.vue';\n",[61,7985,7986,8002],{"__ignoreMap":80},[204,7987,7988,7990,7992,7994,7996,7998,8000],{"class":206,"line":207},[204,7989,7074],{"class":7013},[204,7991,7077],{"class":606},[204,7993,7080],{"class":7013},[204,7995,218],{"class":214},[204,7997,7085],{"class":221},[204,7999,7088],{"class":214},[204,8001,7091],{"class":214},[204,8003,8004,8006,8008,8010,8012,8014,8016],{"class":206,"line":228},[204,8005,7074],{"class":7013},[204,8007,7098],{"class":606},[204,8009,7080],{"class":7013},[204,8011,218],{"class":214},[204,8013,7105],{"class":221},[204,8015,7088],{"class":214},[204,8017,7091],{"class":214},[13,8019,8020],{},"もしエイリアスが使えない場合は",[72,8022,8024],{"className":7004,"code":8023,"filename":7067,"language":3989,"meta":80,"style":80},"import Home from '..\u002Fvue\u002Fpage\u002FHome.vue';\nimport Profile from '..\u002Fvue\u002Fpage\u002FProfile.vue';\n",[61,8025,8026,8043],{"__ignoreMap":80},[204,8027,8028,8030,8032,8034,8036,8039,8041],{"class":206,"line":207},[204,8029,7074],{"class":7013},[204,8031,7077],{"class":606},[204,8033,7080],{"class":7013},[204,8035,218],{"class":214},[204,8037,8038],{"class":221},"..\u002Fvue\u002Fpage\u002FHome.vue",[204,8040,7088],{"class":214},[204,8042,7091],{"class":214},[204,8044,8045,8047,8049,8051,8053,8056,8058],{"class":206,"line":228},[204,8046,7074],{"class":7013},[204,8048,7098],{"class":606},[204,8050,7080],{"class":7013},[204,8052,218],{"class":214},[204,8054,8055],{"class":221},"..\u002Fvue\u002Fpage\u002FProfile.vue",[204,8057,7088],{"class":214},[204,8059,7091],{"class":214},[13,8061,8062,8063,8065],{},"このように相対パスになってしまい、もし構成を変えようとした時に相対関係の修正が必要となります。その対策としてエイリアスを定めておくと今後の管理がしやすいです。",[61,8064,7973],{},"はjsディレクトリを参照する様にしています。",[13,8067,8068,8071,8072,8074,8075,8078],{},[61,8069,8070],{},"mix.js('resources\u002Fjs\u002Fadmin\u002Findex.js', 'public\u002Fjs\u002Fadmin').vue();"," にて",[61,8073,7423],{},"をソースとして",[61,8076,8077],{},"'public\u002Fjs\u002Fadmin'","配下に出力し、vueコンパイルを行う様に定義してます。",[13,8080,8081,8082,8085,8086,8089],{},"問題なければ",[61,8083,8084],{},"npm run prod","をプロジェクト ルートでコマンドを叩いてコンパイルを行います。完了後に",[61,8087,8088],{},"public\u002Fjs\u002Fadmin\u002Findex.js","というものが出力されていることを確認してください。",[505,8091,8092],{"id":8092},"反映の確認",[13,8094,8095,8096,8099,8100,8102,8103,8105],{},"jsファイルが出力されましたら",[61,8097,8098],{},"http:\u002F\u002Flocalhost:8005\u002Fsystem","にアクセスします。以下の様に表示され、",[61,8101,7266],{},"というリンクをクリックした際にURLが変わり、",[61,8104,7337],{},"に書かれた内容が表示されればSPAはできています。",[522,8107],{":src":8108,":center":526},"'vue_laravel_app\u002Fhomevue.png'",[13,8110,8111,8113,8114,8117],{},[61,8112,7266],{},"というリンクをクリック後 or ",[61,8115,8116],{},"http:\u002F\u002Flocalhost:8005\u002Fsystem\u002Fprofile","にアクセスしますと以下の様になります。",[522,8119],{":src":8120,":center":526},"'vue_laravel_app\u002Fprofilevue.png'",[13,8122,8123],{},"これでSPAが設定できたことを確認できました。",[27,8125,8127],{"id":8126},"次回は","次回は..",[13,8129,8130],{},"今回は環境構築とLaravel、Vue SPAのセットアップまでとなります。来週はweb apiを通じたSPAでの認証と簡単なプロフィールの更新機能を作成してみます。",[2310,8132,8133],{},"html pre.shiki code .s5Dmg, html code.shiki .s5Dmg{--shiki-default:#FFCB6B}html pre.shiki code .sfyAc, html code.shiki .sfyAc{--shiki-default:#C3E88D}html pre.shiki code .sdLwU, html code.shiki .sdLwU{--shiki-default:#82AAFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .s-wAU, html code.shiki .s-wAU{--shiki-default:#F07178}html pre.shiki code .sAklC, html code.shiki .sAklC{--shiki-default:#89DDFF}html pre.shiki code .s0W1g, html code.shiki .s0W1g{--shiki-default:#BABED8}html pre.shiki code .s6cf3, html code.shiki .s6cf3{--shiki-default:#89DDFF;--shiki-default-font-style:italic}html pre.shiki code .sJ14y, html code.shiki .sJ14y{--shiki-default:#C792EA}",{"title":80,"searchDepth":235,"depth":235,"links":8135},[8136,8144,8149,8150,8157],{"id":5907,"depth":228,"text":5907,"children":8137},[8138,8143],{"id":5913,"depth":235,"text":5913,"children":8139},[8140,8141,8142],{"id":5920,"depth":244,"text":5920},{"id":5954,"depth":244,"text":5954},{"id":5993,"depth":244,"text":5993},{"id":6007,"depth":235,"text":6007},{"id":6019,"depth":228,"text":6019,"children":8145},[8146,8147,8148],{"id":6134,"depth":235,"text":6135},{"id":6197,"depth":235,"text":6197},{"id":6285,"depth":235,"text":6286},{"id":6742,"depth":228,"text":6743},{"id":6762,"depth":228,"text":6763,"children":8151},[8152,8153,8154,8155,8156],{"id":6769,"depth":235,"text":6770},{"id":6948,"depth":235,"text":6948},{"id":6984,"depth":235,"text":6985},{"id":7767,"depth":235,"text":7767},{"id":8092,"depth":235,"text":8092},{"id":8126,"depth":228,"text":8127},[2332],"2022-03-02","アプリの解説とシリーズの概要",{},"\u002Fseries\u002Fvue-laravel-app-1",{"title":5845,"description":8160},"vue_laravel_app","Vue SPA x Laravelでつくる実務パチモンアプリ","series\u002Fvue-laravel-app-1",[3988,541,3989,6958],"vue_laravel_app\u002Fseries.png","QS_7_9yqTbYmh1Gt9XAVtEnkXBXd1wzlct80a3xdm_U",{"id":8171,"title":8172,"body":8173,"category":8636,"createdAt":8637,"description":8638,"extension":2334,"index":2335,"meta":8639,"navigation":231,"path":8640,"publish":231,"seo":8641,"series":2335,"seriesTitle":2335,"stem":8642,"tag":8643,"thumbnail":2335,"updatedAt":2335,"__hash__":8644},"articles\u002Farticles\u002Fphpspredsheet-rich-text.md","Phpspreadsheetで書式があるとgetValue()で文字を取得できない",{"type":10,"value":8174,"toc":8634},[8175,8184,8232,8246,8249,8556,8564,8577,8583,8596,8629,8632],[13,8176,8177,8178,8183],{},"こんにちはjunです。PHPでエクセルをアップロードして、データを取得するという機能を実装する際に",[176,8179,8182],{"href":8180,"rel":8181},"https:\u002F\u002Fphpspreadsheet.readthedocs.io\u002Fen\u002Flatest\u002F",[180],"Phpspreadsheet","を使用することが多いと思います。アップロードされたエクセルを以下の様にしてセルの値を取得することができます。",[72,8185,8187],{"className":539,"code":8186,"language":541,"meta":80,"style":80},"use \\PhpOffice\\PhpSpreadsheet\\IOFactory;\nuse \\PhpOffice\\PhpSpreadsheet\\Shared\\Date;\n\n\u002F\u002F $fileはエクセルファイル\n$spreadsheet = IOFactory::load($file);\n$worksheet = $spreadsheet->getSheetByName('Sheet1');\n\n\u002F\u002F A1のセルのデータを取得する\n$workSheet->getCell(\"A1\")->getValue();\n",[61,8188,8189,8194,8199,8203,8208,8213,8218,8222,8227],{"__ignoreMap":80},[204,8190,8191],{"class":206,"line":207},[204,8192,8193],{},"use \\PhpOffice\\PhpSpreadsheet\\IOFactory;\n",[204,8195,8196],{"class":206,"line":228},[204,8197,8198],{},"use \\PhpOffice\\PhpSpreadsheet\\Shared\\Date;\n",[204,8200,8201],{"class":206,"line":235},[204,8202,232],{"emptyLinePlaceholder":231},[204,8204,8205],{"class":206,"line":244},[204,8206,8207],{},"\u002F\u002F $fileはエクセルファイル\n",[204,8209,8210],{"class":206,"line":249},[204,8211,8212],{},"$spreadsheet = IOFactory::load($file);\n",[204,8214,8215],{"class":206,"line":257},[204,8216,8217],{},"$worksheet = $spreadsheet->getSheetByName('Sheet1');\n",[204,8219,8220],{"class":206,"line":268},[204,8221,232],{"emptyLinePlaceholder":231},[204,8223,8224],{"class":206,"line":279},[204,8225,8226],{},"\u002F\u002F A1のセルのデータを取得する\n",[204,8228,8229],{"class":206,"line":287},[204,8230,8231],{},"$workSheet->getCell(\"A1\")->getValue();\n",[13,8233,8234,8237,8238,8241,8242,8245],{},[61,8235,8236],{},"getCell()","の引数を指定して再帰的に全ての列の値を取得して配列にするみたいなこともできるので、エクセルを用いてwebアプリへのデータ入力ができます。最初は",[61,8239,8240],{},"getValue()","を用いて問題なくセルの文字を取得できていました。しかしある日、お客さんから「このエクセルを読み取らせると変な文字が表示されます」という問い合わせをいただきました。サーバーにアップしたエクセルの内容を上記の様に取得してブラウザに表示するということをやっていたのですが、確かに",[61,8243,8244],{},"{}","という謎の文字がありました。",[13,8247,8248],{},"デバッグログを見てみると下記の様に一部の値がインスタンスとなっていました。",[72,8250,8252],{"className":539,"code":8251,"language":541,"meta":80,"style":80},"array(\n'name' => \n  PhpOffice\\PhpSpreadsheet\\RichText\\RichText::__set_state(array(\n     'richTextElements' => \n    array (\n      0 => \n      PhpOffice\\PhpSpreadsheet\\RichText\\TextElement::__set_state(array(\n         'text' => 'テスト',\n      )),\n      1 => \n      PhpOffice\\PhpSpreadsheet\\RichText\\Run::__set_state(array(\n         'font' => \n        PhpOffice\\PhpSpreadsheet\\Style\\Font::__set_state(array(\n           'name' => '游ゴシック',\n           'size' => 12.0,\n           'bold' => false,\n           'italic' => false,\n           'superscript' => false,\n           'subscript' => false,\n           'underline' => 'none',\n           'strikethrough' => false,\n           'color' => \n          PhpOffice\\PhpSpreadsheet\\Style\\Color::__set_state(array(\n             'argb' => 'FF000000',\n             'hasChanged' => false,\n             'isSupervisor' => false,\n             'parent' => NULL,\n             'parentPropertyName' => NULL,\n          )),\n           'colorIndex' => NULL,\n           'isSupervisor' => false,\n           'parent' => NULL,\n           'parentPropertyName' => NULL,\n        )),\n         'text' => 'テス',\n      )),\n      2 => \n      PhpOffice\\PhpSpreadsheet\\RichText\\Run::__set_state(array(\n         'font' => \n        PhpOffice\\PhpSpreadsheet\\Style\\Font::__set_state(array(\n           'name' => '游ゴシック',\n           'size' => 12.0,\n           'bold' => false,\n           'italic' => false,\n           'superscript' => false,\n           'subscript' => false,\n           'underline' => 'none',\n           'strikethrough' => false,\n           'color' => \n          PhpOffice\\PhpSpreadsheet\\Style\\Color::__set_state(array(\n             'argb' => 'FF000000',\n             'hasChanged' => false,\n             'isSupervisor' => false,\n             'parent' => NULL,\n             'parentPropertyName' => NULL,\n          )),\n           'colorIndex' => NULL,\n           'isSupervisor' => false,\n           'parent' => NULL,\n           'parentPropertyName' => NULL,\n        )),\n         'text' => 'テスト',\n      )),\n    ),\n  )),\n)\n",[61,8253,8254,8259,8264,8269,8274,8279,8284,8289,8294,8299,8304,8309,8314,8319,8324,8329,8334,8339,8344,8349,8354,8359,8364,8369,8374,8379,8384,8389,8394,8399,8404,8409,8414,8419,8424,8429,8433,8438,8442,8446,8450,8454,8458,8462,8466,8470,8474,8478,8482,8486,8490,8494,8498,8502,8506,8510,8514,8518,8522,8526,8530,8534,8538,8542,8547,8552],{"__ignoreMap":80},[204,8255,8256],{"class":206,"line":207},[204,8257,8258],{},"array(\n",[204,8260,8261],{"class":206,"line":228},[204,8262,8263],{},"'name' => \n",[204,8265,8266],{"class":206,"line":235},[204,8267,8268],{},"  PhpOffice\\PhpSpreadsheet\\RichText\\RichText::__set_state(array(\n",[204,8270,8271],{"class":206,"line":244},[204,8272,8273],{},"     'richTextElements' => \n",[204,8275,8276],{"class":206,"line":249},[204,8277,8278],{},"    array (\n",[204,8280,8281],{"class":206,"line":257},[204,8282,8283],{},"      0 => \n",[204,8285,8286],{"class":206,"line":268},[204,8287,8288],{},"      PhpOffice\\PhpSpreadsheet\\RichText\\TextElement::__set_state(array(\n",[204,8290,8291],{"class":206,"line":279},[204,8292,8293],{},"         'text' => 'テスト',\n",[204,8295,8296],{"class":206,"line":287},[204,8297,8298],{},"      )),\n",[204,8300,8301],{"class":206,"line":296},[204,8302,8303],{},"      1 => \n",[204,8305,8306],{"class":206,"line":304},[204,8307,8308],{},"      PhpOffice\\PhpSpreadsheet\\RichText\\Run::__set_state(array(\n",[204,8310,8311],{"class":206,"line":315},[204,8312,8313],{},"         'font' => \n",[204,8315,8316],{"class":206,"line":326},[204,8317,8318],{},"        PhpOffice\\PhpSpreadsheet\\Style\\Font::__set_state(array(\n",[204,8320,8321],{"class":206,"line":337},[204,8322,8323],{},"           'name' => '游ゴシック',\n",[204,8325,8326],{"class":206,"line":348},[204,8327,8328],{},"           'size' => 12.0,\n",[204,8330,8331],{"class":206,"line":4},[204,8332,8333],{},"           'bold' => false,\n",[204,8335,8336],{"class":206,"line":363},[204,8337,8338],{},"           'italic' => false,\n",[204,8340,8341],{"class":206,"line":368},[204,8342,8343],{},"           'superscript' => false,\n",[204,8345,8346],{"class":206,"line":376},[204,8347,8348],{},"           'subscript' => false,\n",[204,8350,8351],{"class":206,"line":386},[204,8352,8353],{},"           'underline' => 'none',\n",[204,8355,8356],{"class":206,"line":395},[204,8357,8358],{},"           'strikethrough' => false,\n",[204,8360,8361],{"class":206,"line":402},[204,8362,8363],{},"           'color' => \n",[204,8365,8366],{"class":206,"line":412},[204,8367,8368],{},"          PhpOffice\\PhpSpreadsheet\\Style\\Color::__set_state(array(\n",[204,8370,8371],{"class":206,"line":422},[204,8372,8373],{},"             'argb' => 'FF000000',\n",[204,8375,8376],{"class":206,"line":432},[204,8377,8378],{},"             'hasChanged' => false,\n",[204,8380,8381],{"class":206,"line":447},[204,8382,8383],{},"             'isSupervisor' => false,\n",[204,8385,8386],{"class":206,"line":454},[204,8387,8388],{},"             'parent' => NULL,\n",[204,8390,8391],{"class":206,"line":462},[204,8392,8393],{},"             'parentPropertyName' => NULL,\n",[204,8395,8396],{"class":206,"line":467},[204,8397,8398],{},"          )),\n",[204,8400,8401],{"class":206,"line":475},[204,8402,8403],{},"           'colorIndex' => NULL,\n",[204,8405,8406],{"class":206,"line":483},[204,8407,8408],{},"           'isSupervisor' => false,\n",[204,8410,8411],{"class":206,"line":1413},[204,8412,8413],{},"           'parent' => NULL,\n",[204,8415,8416],{"class":206,"line":1419},[204,8417,8418],{},"           'parentPropertyName' => NULL,\n",[204,8420,8421],{"class":206,"line":1424},[204,8422,8423],{},"        )),\n",[204,8425,8426],{"class":206,"line":1429},[204,8427,8428],{},"         'text' => 'テス',\n",[204,8430,8431],{"class":206,"line":1434},[204,8432,8298],{},[204,8434,8435],{"class":206,"line":1440},[204,8436,8437],{},"      2 => \n",[204,8439,8440],{"class":206,"line":1445},[204,8441,8308],{},[204,8443,8444],{"class":206,"line":1450},[204,8445,8313],{},[204,8447,8448],{"class":206,"line":1455},[204,8449,8318],{},[204,8451,8452],{"class":206,"line":1460},[204,8453,8323],{},[204,8455,8456],{"class":206,"line":1466},[204,8457,8328],{},[204,8459,8460],{"class":206,"line":1472},[204,8461,8333],{},[204,8463,8464],{"class":206,"line":1478},[204,8465,8338],{},[204,8467,8468],{"class":206,"line":1484},[204,8469,8343],{},[204,8471,8472],{"class":206,"line":1490},[204,8473,8348],{},[204,8475,8476],{"class":206,"line":1496},[204,8477,8353],{},[204,8479,8480],{"class":206,"line":1502},[204,8481,8358],{},[204,8483,8484],{"class":206,"line":1508},[204,8485,8363],{},[204,8487,8488],{"class":206,"line":1513},[204,8489,8368],{},[204,8491,8492],{"class":206,"line":1518},[204,8493,8373],{},[204,8495,8496],{"class":206,"line":1524},[204,8497,8378],{},[204,8499,8500],{"class":206,"line":1529},[204,8501,8383],{},[204,8503,8504],{"class":206,"line":1534},[204,8505,8388],{},[204,8507,8508],{"class":206,"line":1539},[204,8509,8393],{},[204,8511,8512],{"class":206,"line":1544},[204,8513,8398],{},[204,8515,8516],{"class":206,"line":1549},[204,8517,8403],{},[204,8519,8520],{"class":206,"line":3455},[204,8521,8408],{},[204,8523,8524],{"class":206,"line":3461},[204,8525,8413],{},[204,8527,8528],{"class":206,"line":3467},[204,8529,8418],{},[204,8531,8532],{"class":206,"line":3472},[204,8533,8423],{},[204,8535,8536],{"class":206,"line":3478},[204,8537,8293],{},[204,8539,8540],{"class":206,"line":3484},[204,8541,8298],{},[204,8543,8544],{"class":206,"line":3490},[204,8545,8546],{},"    ),\n",[204,8548,8549],{"class":206,"line":3496},[204,8550,8551],{},"  )),\n",[204,8553,8554],{"class":206,"line":3501},[204,8555,868],{},[13,8557,8558,8559],{},"対象のエクセルのセルを見てみると文字が赤色になっていたりと装飾が当てられていました。原因がわかったので対処を行います。調査の結果似た様なissueがありました。",[176,8560,8563],{"href":8561,"rel":8562},"https:\u002F\u002Fgithub.com\u002FPHPOffice\u002FPhpSpreadsheet\u002Fissues\u002F442",[180],"RichText can not get the correct value, when one part is plain text and the other part is rich text.",[13,8565,8566,8567,8569,8570,8573,8574,8576],{},"方法としては",[61,8568,8240],{},"に加えて",[61,8571,8572],{},"getPlainText()","というメソッドを通すことで装飾されたセルの値（リッチテキスト）を取得することができます。しかし",[61,8575,8572],{},"には問題があり、なんとリッチテキストでない値に適用すると以下の様エラーが発生します。",[72,8578,8581],{"className":8579,"code":8580,"language":77},[75],"Call to a member function getPlainText() on string.\n",[61,8582,8580],{"__ignoreMap":80},[13,8584,8585,8586,8588,8589,8591,8592,8595],{},"つまり、",[61,8587,8240],{},"した時の値がリッチテキストインスタンスであるかをチェックして",[61,8590,8572],{},"を適用するかを分岐させる必要があります。リッチテキストは",[61,8593,8594],{},"\\PhpOffice\\PhpSpreadsheet\\RichText\\RichText","というクラスなので以下の様な関数を作成すれば問題ありません。",[72,8597,8599],{"className":539,"code":8598,"language":541,"meta":80,"style":80},"use \\PhpOffice\\PhpSpreadsheet\\RichText\\RichText;\n\nfunction getCellValue($workSheet,$cell){\n    $val = $workSheet->getCell($cell)->getValue();\n    return ($val instanceof RichText)?$val->getPlainText():$val;\n}\n",[61,8600,8601,8606,8610,8615,8620,8625],{"__ignoreMap":80},[204,8602,8603],{"class":206,"line":207},[204,8604,8605],{},"use \\PhpOffice\\PhpSpreadsheet\\RichText\\RichText;\n",[204,8607,8608],{"class":206,"line":228},[204,8609,232],{"emptyLinePlaceholder":231},[204,8611,8612],{"class":206,"line":235},[204,8613,8614],{},"function getCellValue($workSheet,$cell){\n",[204,8616,8617],{"class":206,"line":244},[204,8618,8619],{},"    $val = $workSheet->getCell($cell)->getValue();\n",[204,8621,8622],{"class":206,"line":249},[204,8623,8624],{},"    return ($val instanceof RichText)?$val->getPlainText():$val;\n",[204,8626,8627],{"class":206,"line":257},[204,8628,804],{},[13,8630,8631],{},"リッチテキストのインスタンスかをチェックして値を取得することでリッチテキストもプレーンテキストも対応することができました。こう考えるとやっぱりエクセルインポートなんてやめてCSVインポートにしておけばよかったなーと思っています。",[2310,8633,4455],{},{"title":80,"searchDepth":235,"depth":235,"links":8635},[],[3981],"2022-02-28","書式を当てたセルから文字・値を取得する方法",{},"\u002Farticles\u002Fphpspredsheet-rich-text",{"title":8172,"description":8638},"articles\u002Fphpspredsheet-rich-text",[541],"vZGoVH1w7p4Zs6BmYkw-9jf7nQBYtDotePnJnccXEz4",{"id":8646,"title":8647,"body":8648,"category":10049,"createdAt":10050,"description":8160,"extension":2334,"index":207,"meta":10051,"navigation":231,"path":10052,"publish":231,"seo":10053,"series":10054,"seriesTitle":10055,"stem":10056,"tag":10057,"thumbnail":10059,"updatedAt":2335,"__hash__":10060},"series\u002Fseries\u002Flaravel-to-django-1.md","Laravel使いがDjangoでwebアプリを作るよその１：アプリの概要と環境構築",{"type":10,"value":8649,"toc":10029},[8650,8653,8656,8659,8670,8673,8676,8679,8699,8702,8705,8724,8726,8732,8773,8780,8783,8786,8827,8830,8832,8837,9109,9114,9123,9137,9144,9161,9184,9189,9195,9210,9214,9217,9220,9222,9244,9246,9275,9277,9285,9288,9291,9295,9298,9301,9312,9373,9376,9432,9445,9448,9451,9526,9542,9545,9557,9820,9823,9840,9843,9850,9856,9863,9869,9879,9886,9896,9915,9921,9930,9933,9984,9991,10020,10023,10026],[13,8651,8652],{},"こんにちはjunです。最近Laravelでシステムを作る機会が何回かあったので、Laravelによる構築がかなり得意になってきました。しかし会社の方で「pythonを使用した機械学習や統計の機能をwebアプリとして使用できるアプリを開発したい」という企画がでてきました。",[13,8654,8655],{},"pythonで機械学習や統計の処理（モデル）を作成し、UIやロジックの部分をwebフレームワークで作成します。できたらwebフレームワークの処理部分にて統計ロジックをimportして、データの引き渡しを行いたいと思いました。しかしLaravelはPHPなので連携が難しいです。",[13,8657,8658],{},"そのため「Python製のwebフレームワークであるdjangoを利用できないか？」と思い、Djangoの勉強をスタートしました。チュートリアルはひとまず終えましたが、Laravelをやっていたおかげで",[42,8660,8661,8664,8667],{},[45,8662,8663],{},"どんな機能があると便利か？",[45,8665,8666],{},"Laravelのこの機能がDjangoのこの記述にあたるのか？",[45,8668,8669],{},"実務上必要となるこの機能を実装するには何を使えばいいのか？",[13,8671,8672],{},"という視点を用いて学習することができました。このシリーズでは「Laravelを使っていたPHP開発者がDjangoとPythonを使用してアプリを作る」ことを通じてDjangoの学習をしたいと思っています。記事内ではDjangoにおける実装とLaravelにおける実装をリンクしながら解説していきたいと思います。",[13,8674,8675],{},"利用するDjangoのバージョンは3.2で、Laravelは8とします。第１回のこの記事は環境構築とアプリの概要、Djangoの開発コンセプトやディレクトリ説明がメインとなります。",[13,8677,8678],{},"なおシリーズでは以下の内容を押さえていることを前提としています",[42,8680,8681,8684,8687,8690,8693,8696],{},[45,8682,8683],{},"Laravelの実務開発経験があるまたは、完成したアプリを作ったことがある。",[45,8685,8686],{},"Laravelのコンセプトやディレクトリ構成を知っている。",[45,8688,8689],{},"PythonがPCにインストールされ、基本的な記述を理解している。",[45,8691,8692],{},"PHPを触ったことが多いが、Pythonはそれほどない。",[45,8694,8695],{},"Djangoのチュートリアルは行っており、必須の内容は知っている。",[45,8697,8698],{},"MVCの概念を知っている、使える。",[13,8700,8701],{},"それでは早速始めていきましょう。",[13,8703,8704],{},"参考資料",[42,8706,8707,8714,8717],{},[45,8708,8709],{},[176,8710,8713],{"href":8711,"rel":8712},"https:\u002F\u002Fdocs.djangoproject.com\u002Fja\u002F3.2",[180],"Django ドキュメント3.2",[45,8715,8716],{},"書籍『現場で使える Django の教科書』",[45,8718,8719],{},[176,8720,8723],{"href":8721,"rel":8722},"https:\u002F\u002Fdocs.docker.jp\u002Fcompose\u002Fdjango.html",[180],"クィックスタート: Compose と Django - Docker ドキュメント",[27,8725,6019],{"id":6019},[13,8727,8728,8729,8731],{},"今回の環境構築はdockerを用いて作成しようと思います。適当なディレクトリ を作成し、",[61,8730,6048],{},"とビルドファイルを作成します。",[72,8733,8735],{"className":6052,"code":8734,"language":6054,"meta":80,"style":80},"mkdir laravel_django\ncd laravel_django\n\ntouch Dockerfile\ntouch docker-compose.yaml\nmkdir source\n",[61,8736,8737,8744,8750,8754,8760,8766],{"__ignoreMap":80},[204,8738,8739,8741],{"class":206,"line":207},[204,8740,6061],{"class":2469},[204,8742,8743],{"class":221}," laravel_django\n",[204,8745,8746,8748],{"class":206,"line":228},[204,8747,6070],{"class":6069},[204,8749,8743],{"class":221},[204,8751,8752],{"class":206,"line":235},[204,8753,232],{"emptyLinePlaceholder":231},[204,8755,8756,8758],{"class":206,"line":244},[204,8757,6082],{"class":2469},[204,8759,6112],{"class":221},[204,8761,8762,8764],{"class":206,"line":249},[204,8763,6082],{"class":2469},[204,8765,6085],{"class":221},[204,8767,8768,8770],{"class":206,"line":257},[204,8769,6061],{"class":2469},[204,8771,8772],{"class":221}," source\n",[13,8774,8775,8776,8779],{},"まずはDockerfileでDjangoが稼働するpythonの環境を作成します。なお、この環境ではApacheやnginxといったwebサーバーとpythonとの連携設定は行わないものとします。コンテナ内で",[61,8777,8778],{},"run serve","を行います。",[505,8781,6125],{"id":8782},"dockerfile",[13,8784,8785],{},"Dockerfileを以下の様に記述します。",[72,8787,8790],{"className":8788,"code":8789,"language":6125,"meta":80,"style":80},"language-Dockerfile shiki shiki-themes material-theme-ocean","FROM python:3\nENV PYTHONUNBUFFERED 1\nRUN mkdir \u002Fcode\nWORKDIR \u002Fcode\nADD requirements.txt \u002Fcode\u002F\nRUN pip install -r requirements.txt\nADD . \u002Fcode\u002F\n",[61,8791,8792,8797,8802,8807,8812,8817,8822],{"__ignoreMap":80},[204,8793,8794],{"class":206,"line":207},[204,8795,8796],{},"FROM python:3\n",[204,8798,8799],{"class":206,"line":228},[204,8800,8801],{},"ENV PYTHONUNBUFFERED 1\n",[204,8803,8804],{"class":206,"line":235},[204,8805,8806],{},"RUN mkdir \u002Fcode\n",[204,8808,8809],{"class":206,"line":244},[204,8810,8811],{},"WORKDIR \u002Fcode\n",[204,8813,8814],{"class":206,"line":249},[204,8815,8816],{},"ADD requirements.txt \u002Fcode\u002F\n",[204,8818,8819],{"class":206,"line":257},[204,8820,8821],{},"RUN pip install -r requirements.txt\n",[204,8823,8824],{"class":206,"line":268},[204,8825,8826],{},"ADD . \u002Fcode\u002F\n",[13,8828,8829],{},"python3 イメージをもとにpythonが動く環境を用意します。",[505,8831,6633],{"id":6633},[13,8833,8834,8836],{},[61,8835,6048],{},"は以下の様に記述します。",[72,8838,8840],{"className":6292,"code":8839,"filename":6048,"language":6295,"meta":80,"style":80},"version: '3'\n\nservices:\n  db:\n    image: mysql:5.7\n    command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci\n    environment:\n      MYSQL_DATABASE: preform\n      MYSQL_USER: test\n      MYSQL_PASSWORD: testtest\n      MYSQL_ROOT_PASSWORD: rootroot\n      TZ: Asia\u002FTokyo\n    ports: \n      - \"3306:3306\"\n    volumes: \n      - djangodemo:\u002Fvar\u002Flib\u002Fmysql\n  phpmyadmin:\n    image: phpmyadmin\n    ports:\n      - \"8080:80\"\n    environment:\n     - PMA_ARBITRARY=1\n     - PMA_HOST=db:3306\n     - PMA_USER=root\n     - PMA_PASSWORD=rootroot\n  web:\n    build: .\n    command: python3 manage.py runserver 0.0.0.0:8000\n    volumes:\n      - .\u002Fsource:\u002Fcode\n    ports:\n      - \"8000:8000\"\n    depends_on:\n      - db\nvolumes: \n  djangodemo: {}\n",[61,8841,8842,8854,8858,8864,8870,8878,8886,8892,8900,8908,8916,8924,8932,8940,8950,8958,8965,8971,8979,8985,8995,9001,9007,9013,9019,9025,9032,9041,9050,9056,9063,9069,9080,9086,9092,9100],{"__ignoreMap":80},[204,8843,8844,8846,8848,8850,8852],{"class":206,"line":207},[204,8845,211],{"class":210},[204,8847,215],{"class":214},[204,8849,218],{"class":214},[204,8851,1164],{"class":221},[204,8853,225],{"class":214},[204,8855,8856],{"class":206,"line":228},[204,8857,232],{"emptyLinePlaceholder":231},[204,8859,8860,8862],{"class":206,"line":235},[204,8861,238],{"class":210},[204,8863,241],{"class":214},[204,8865,8866,8868],{"class":206,"line":244},[204,8867,371],{"class":210},[204,8869,241],{"class":214},[204,8871,8872,8874,8876],{"class":206,"line":249},[204,8873,260],{"class":210},[204,8875,215],{"class":214},[204,8877,383],{"class":221},[204,8879,8880,8882,8884],{"class":206,"line":257},[204,8881,6487],{"class":210},[204,8883,215],{"class":214},[204,8885,6492],{"class":221},[204,8887,8888,8890],{"class":206,"line":268},[204,8889,299],{"class":210},[204,8891,241],{"class":214},[204,8893,8894,8896,8898],{"class":206,"line":279},[204,8895,405],{"class":210},[204,8897,215],{"class":214},[204,8899,6507],{"class":221},[204,8901,8902,8904,8906],{"class":206,"line":287},[204,8903,415],{"class":210},[204,8905,215],{"class":214},[204,8907,6516],{"class":221},[204,8909,8910,8912,8914],{"class":206,"line":296},[204,8911,425],{"class":210},[204,8913,215],{"class":214},[204,8915,6525],{"class":221},[204,8917,8918,8920,8922],{"class":206,"line":304},[204,8919,6530],{"class":210},[204,8921,215],{"class":214},[204,8923,6535],{"class":221},[204,8925,8926,8928,8930],{"class":206,"line":315},[204,8927,6540],{"class":210},[204,8929,215],{"class":214},[204,8931,6545],{"class":221},[204,8933,8934,8936,8938],{"class":206,"line":326},[204,8935,282],{"class":210},[204,8937,215],{"class":214},[204,8939,6318],{"class":606},[204,8941,8942,8944,8946,8948],{"class":206,"line":337},[204,8943,290],{"class":214},[204,8945,6387],{"class":214},[204,8947,6562],{"class":221},[204,8949,695],{"class":214},[204,8951,8952,8954,8956],{"class":206,"line":348},[204,8953,351],{"class":210},[204,8955,215],{"class":214},[204,8957,6318],{"class":606},[204,8959,8960,8962],{"class":206,"line":4},[204,8961,290],{"class":214},[204,8963,8964],{"class":221}," djangodemo:\u002Fvar\u002Flib\u002Fmysql\n",[204,8966,8967,8969],{"class":206,"line":363},[204,8968,6397],{"class":210},[204,8970,241],{"class":214},[204,8972,8973,8975,8977],{"class":206,"line":368},[204,8974,260],{"class":210},[204,8976,215],{"class":214},[204,8978,6408],{"class":221},[204,8980,8981,8983],{"class":206,"line":376},[204,8982,282],{"class":210},[204,8984,241],{"class":214},[204,8986,8987,8989,8991,8993],{"class":206,"line":386},[204,8988,290],{"class":214},[204,8990,6387],{"class":214},[204,8992,6431],{"class":221},[204,8994,695],{"class":214},[204,8996,8997,8999],{"class":206,"line":395},[204,8998,299],{"class":210},[204,9000,241],{"class":214},[204,9002,9003,9005],{"class":206,"line":402},[204,9004,6444],{"class":214},[204,9006,6447],{"class":221},[204,9008,9009,9011],{"class":206,"line":412},[204,9010,6444],{"class":214},[204,9012,6454],{"class":221},[204,9014,9015,9017],{"class":206,"line":422},[204,9016,6444],{"class":214},[204,9018,6461],{"class":221},[204,9020,9021,9023],{"class":206,"line":432},[204,9022,6444],{"class":214},[204,9024,6468],{"class":221},[204,9026,9027,9030],{"class":206,"line":447},[204,9028,9029],{"class":210},"  web",[204,9031,241],{"class":214},[204,9033,9034,9036,9038],{"class":206,"line":454},[204,9035,6330],{"class":210},[204,9037,215],{"class":214},[204,9039,9040],{"class":1054}," .\n",[204,9042,9043,9045,9047],{"class":206,"line":462},[204,9044,6487],{"class":210},[204,9046,215],{"class":214},[204,9048,9049],{"class":221}," python3 manage.py runserver 0.0.0.0:8000\n",[204,9051,9052,9054],{"class":206,"line":467},[204,9053,351],{"class":210},[204,9055,241],{"class":214},[204,9057,9058,9060],{"class":206,"line":475},[204,9059,290],{"class":214},[204,9061,9062],{"class":221}," .\u002Fsource:\u002Fcode\n",[204,9064,9065,9067],{"class":206,"line":483},[204,9066,282],{"class":210},[204,9068,241],{"class":214},[204,9070,9071,9073,9075,9078],{"class":206,"line":1413},[204,9072,290],{"class":214},[204,9074,6387],{"class":214},[204,9076,9077],{"class":221},"8000:8000",[204,9079,695],{"class":214},[204,9081,9082,9084],{"class":206,"line":1419},[204,9083,6340],{"class":210},[204,9085,241],{"class":214},[204,9087,9088,9090],{"class":206,"line":1424},[204,9089,290],{"class":214},[204,9091,312],{"class":221},[204,9093,9094,9096,9098],{"class":206,"line":1429},[204,9095,470],{"class":210},[204,9097,215],{"class":214},[204,9099,6318],{"class":606},[204,9101,9102,9105,9107],{"class":206,"line":1434},[204,9103,9104],{"class":210},"  djangodemo",[204,9106,215],{"class":214},[204,9108,6597],{"class":214},[13,9110,9111,9113],{},[61,9112,6622],{},"ではデータベースを定義しています。DjangoはSQLiteを使用した簡易的なDBが用意されています。ただし実務ではmysqlなどのDBサーバーを使用することが多いので、今回はmysqlを使用できる様にします。",[2401,9115,9117,9118],{"className":9116},[2404,2405],"\nDjangoでmysqlを使用するためにはpythonモジュールのmysqlclientが必要です。この環境にはデフォルトでないので注意してください。他のDBサーバーを使用する場合は適宜ドライバをインストールしてください。\n",[176,9119,9122],{"target":9120,"href":9121},"_blank","https:\u002F\u002Fdocs.djangoproject.com\u002Fja\u002F3.2\u002Ftopics\u002Finstall\u002F#get-your-database-running","本家ドキュメントを参照",[13,9124,9125,9126,9129,9130,9132,9133,9136],{},"DB内部をすぐに確認できる様にphpmyadminを入れておきます。（好みなのでなくてもいいです。）\nそして",[61,9127,9128],{},"web","では同階層にある",[61,9131,6125],{},"をビルドして実行できる様になります。立ち上げ時に",[61,9134,9135],{},"python3 manage.py runserver 0.0.0.0:8000","をしてwebサーバを立ち上げる設定にしています。",[13,9138,9139,9140,9143],{},"このコンテナを起動する前に ",[61,9141,9142],{},"requirements.txt"," をDockerfileと同じ階層に生成しておき、以下の内容を記述します。",[72,9145,9149],{"className":9146,"code":9147,"filename":9142,"language":9148,"meta":80,"style":80},"language-txt shiki shiki-themes material-theme-ocean","Django==3.2\nmysqlclient==2.1.0\n","txt",[61,9150,9151,9156],{"__ignoreMap":80},[204,9152,9153],{"class":206,"line":207},[204,9154,9155],{},"Django==3.2\n",[204,9157,9158],{"class":206,"line":228},[204,9159,9160],{},"mysqlclient==2.1.0\n",[13,9162,9163,9165,9166,9169,9170,9173,9174,9176,9177,9179,9180,9183],{},[61,9164,9142],{}," はpythonライブラリ管理ツールpipの設定ファイルです。npmの",[61,9167,9168],{},"package.json","やcomposerの",[61,9171,9172],{},"composer.json","といったものと同じです。pythonのモジュール管理はpipを使用することが多く、",[61,9175,9142],{}," は環境ないの依存するモジュールとそのバージョンを記述して配置しておきます。そして",[61,9178,9142],{}," がある階層で ",[61,9181,9182],{},"pip install","をすることで自動的に依存関係をインストールしてくれます。Django3.2とmysqlclientを記述しておきます。",[13,9185,9186,9188],{},[61,9187,9142],{},"を記述したら以下のコマンドを実行してDjangoのソースを作成します。",[72,9190,9193],{"className":9191,"code":9192,"language":77},[75],"docker-compose run web django-admin.py startproject djangodemo .\n",[61,9194,9192],{"__ignoreMap":80},[13,9196,9197,9198,9201,9202,9205,9206,9209],{},"すると",[61,9199,9200],{},"source\u002F","配下にDjangoのソースが生成されるはずです。\nそして準備が整ったら",[61,9203,9204],{},"docker-compose up -d --build"," を行って構築を行います。問題なくいった場合はブラウザにて ",[61,9207,9208],{},"localhost:8000"," を閲覧すると、Djangoのウェルカムページが表示されるはずです。",[522,9211],{":src":9212,":width":9213},"'laralve_to_django\u002Fwelcome.png'","'100%'",[27,9215,9216],{"id":9216},"アプリ概要",[13,9218,9219],{},"今回作成するアプリはn○teみたいなブログサービスを作成しようと思います。",[5918,9221,5920],{"id":5920},[42,9223,9224,9226,9228,9230,9232,9234],{},[45,9225,5925],{},[45,9227,5928],{},[45,9229,5931],{},[45,9231,5934],{},[45,9233,5937],{},[45,9235,5940,9236],{},[42,9237,9238,9240,9242],{},[45,9239,5945],{},[45,9241,5948],{},[45,9243,5951],{},[5918,9245,5954],{"id":5954},[42,9247,9248,9250,9262,9264,9266,9268,9270,9272],{},[45,9249,5959],{},[45,9251,5962,9252],{},[42,9253,9254,9256,9258,9260],{},[45,9255,1942],{},[45,9257,5969],{},[45,9259,5972],{},[45,9261,5975],{},[45,9263,5978],{},[45,9265,5981],{},[45,9267,5984],{},[45,9269,5987],{},[45,9271,5990],{},[45,9273,9274],{},"公開側からコメント可能",[5918,9276,5993],{"id":5993},[42,9278,9279,9281,9283],{},[45,9280,5998],{},[45,9282,6001],{},[45,9284,6004],{},[13,9286,9287],{},"一通りユーザーエンティティから、記事のCURD、リレーションの練習はできると思います。すべてのビューはpython側でレンダリングを行い、Vue.jsなどは今回使用しません。違う機会にrest apiを使用した構成を作ってみたいとおおいます。",[13,9289,9290],{},"デザインに関してはいつものbootstrapを使用します。",[27,9292,9294],{"id":9293},"djangoの大まかな解説","Djangoの大まかな解説",[13,9296,9297],{},"詳細なロジックなどは次の記事で解説したいと思います。今回はDjangoのディレクトリ構成やLaravelと似ているとこやリンクしている中核的な機能を解説したいと思います。",[505,9299,9300],{"id":9300},"コマンドによる制御",[13,9302,9303,9304,9307,9308,9311],{},"Laravelでは ",[61,9305,9306],{},"php artisan"," を使用することでコントローラーを作ったり、マイグレーションを実行したりできます。Djangoにも似た様なものがあります。使用する時はソーストップの ",[61,9309,9310],{},"manage.py"," がある箇所で実行します。例えば以下の様な機能があります。",[72,9313,9315],{"className":6052,"code":9314,"language":6054,"meta":80,"style":80},"# 簡易webサーバーを起動\npython3 manage.py runserve\n\n# マイグレーションを実行\npython3 manage.py migrate\n\n# アプリのテンプレートを作成\npython3 manage.py startapp APP_NAME\n\n",[61,9316,9317,9323,9334,9338,9343,9352,9356,9361],{"__ignoreMap":80},[204,9318,9319],{"class":206,"line":207},[204,9320,9322],{"class":9321},"sC9rS","# 簡易webサーバーを起動\n",[204,9324,9325,9328,9331],{"class":206,"line":228},[204,9326,9327],{"class":2469},"python3",[204,9329,9330],{"class":221}," manage.py",[204,9332,9333],{"class":221}," runserve\n",[204,9335,9336],{"class":206,"line":235},[204,9337,232],{"emptyLinePlaceholder":231},[204,9339,9340],{"class":206,"line":244},[204,9341,9342],{"class":9321},"# マイグレーションを実行\n",[204,9344,9345,9347,9349],{"class":206,"line":249},[204,9346,9327],{"class":2469},[204,9348,9330],{"class":221},[204,9350,9351],{"class":221}," migrate\n",[204,9353,9354],{"class":206,"line":257},[204,9355,232],{"emptyLinePlaceholder":231},[204,9357,9358],{"class":206,"line":268},[204,9359,9360],{"class":9321},"# アプリのテンプレートを作成\n",[204,9362,9363,9365,9367,9370],{"class":206,"line":279},[204,9364,9327],{"class":2469},[204,9366,9330],{"class":221},[204,9368,9369],{"class":221}," startapp",[204,9371,9372],{"class":221}," APP_NAME\n",[13,9374,9375],{},"Laravelで例えると",[72,9377,9379],{"className":6052,"code":9378,"language":6054,"meta":80,"style":80},"# 簡易webサーバーを起動\nphp artisan serve\n\n# マイグレーションを実行\nphp artisan migrate\n\n# コントローラーのテンプレートを作成\nphp artisan make:controller CONTROLLER_NAME\n\n",[61,9380,9381,9385,9395,9399,9403,9411,9415,9420],{"__ignoreMap":80},[204,9382,9383],{"class":206,"line":207},[204,9384,9322],{"class":9321},[204,9386,9387,9389,9392],{"class":206,"line":228},[204,9388,541],{"class":2469},[204,9390,9391],{"class":221}," artisan",[204,9393,9394],{"class":221}," serve\n",[204,9396,9397],{"class":206,"line":235},[204,9398,232],{"emptyLinePlaceholder":231},[204,9400,9401],{"class":206,"line":244},[204,9402,9342],{"class":9321},[204,9404,9405,9407,9409],{"class":206,"line":249},[204,9406,541],{"class":2469},[204,9408,9391],{"class":221},[204,9410,9351],{"class":221},[204,9412,9413],{"class":206,"line":257},[204,9414,232],{"emptyLinePlaceholder":231},[204,9416,9417],{"class":206,"line":268},[204,9418,9419],{"class":9321},"# コントローラーのテンプレートを作成\n",[204,9421,9422,9424,9426,9429],{"class":206,"line":279},[204,9423,541],{"class":2469},[204,9425,9391],{"class":221},[204,9427,9428],{"class":221}," make:controller",[204,9430,9431],{"class":221}," CONTROLLER_NAME\n",[13,9433,9434,9435,9438,9439,9444],{},"以上の様になります。この ",[61,9436,9437],{},"python3 manage.py","で実行できる内容は",[176,9440,9443],{"href":9441,"rel":9442},"https:\u002F\u002Fdocs.djangoproject.com\u002Fja\u002F3.2\u002Fref\u002Fdjango-admin\u002F",[180],"こちらのドキュメント","で知ることができます。",[505,9446,9447],{"id":9447},"ディレクトリとファイルの構成",[13,9449,9450],{},"初期のファイル構成は以下の様になっています。",[72,9452,9454],{"className":6052,"code":9453,"language":6054,"meta":80,"style":80},"├── db.sqlite3\n├── djangodemo\n│   ├── __init__.py\n│   ├── asgi.py\n│   ├── settings.py\n│   ├── urls.py\n│   └── wsgi.py\n├── manage.py\n",[61,9455,9456,9464,9471,9482,9491,9500,9509,9519],{"__ignoreMap":80},[204,9457,9458,9461],{"class":206,"line":207},[204,9459,9460],{"class":2469},"├──",[204,9462,9463],{"class":221}," db.sqlite3\n",[204,9465,9466,9468],{"class":206,"line":228},[204,9467,9460],{"class":2469},[204,9469,9470],{"class":221}," djangodemo\n",[204,9472,9473,9476,9479],{"class":206,"line":235},[204,9474,9475],{"class":2469},"│",[204,9477,9478],{"class":221},"   ├──",[204,9480,9481],{"class":221}," __init__.py\n",[204,9483,9484,9486,9488],{"class":206,"line":244},[204,9485,9475],{"class":2469},[204,9487,9478],{"class":221},[204,9489,9490],{"class":221}," asgi.py\n",[204,9492,9493,9495,9497],{"class":206,"line":249},[204,9494,9475],{"class":2469},[204,9496,9478],{"class":221},[204,9498,9499],{"class":221}," settings.py\n",[204,9501,9502,9504,9506],{"class":206,"line":257},[204,9503,9475],{"class":2469},[204,9505,9478],{"class":221},[204,9507,9508],{"class":221}," urls.py\n",[204,9510,9511,9513,9516],{"class":206,"line":268},[204,9512,9475],{"class":2469},[204,9514,9515],{"class":221},"   └──",[204,9517,9518],{"class":221}," wsgi.py\n",[204,9520,9521,9523],{"class":206,"line":279},[204,9522,9460],{"class":2469},[204,9524,9525],{"class":221}," manage.py\n",[13,9527,9528,9531,9532,9534,9535,5423,9538,9541],{},[61,9529,9530],{},"djangodemo\u002F"," はDjangoのアプリを最初に作成した時に生成されるディレクトリです。",[61,9533,9530],{}," では特に ",[61,9536,9537],{},"settings.py",[61,9539,9540],{},"urls.py","が重要です。",[5918,9543,9544],{"id":9544},"設定ファイル",[13,9546,9547,9549,9550,9553,9554,9556],{},[61,9548,9537],{}," はLaravelでいう",[61,9551,9552],{},"config\u002F","ディレクトリ配下のファイルたちを一つにまとめた様なものになっています。特に",[61,9555,4350],{},"の記述が近いかもしれません。一部抜粋して解説します。",[72,9558,9562],{"className":9559,"code":9560,"filename":9537,"language":9561,"meta":80,"style":80},"language-python shiki shiki-themes material-theme-ocean","BASE_DIR = Path(__file__).resolve().parent.parent\n\nSECRET_KEY = 'hogehoge'\n\n# SECURITY WARNING: don't run with debug turned on in production!\n# Laravelのapp.debugと同じ\nDEBUG = True\n\n# Application definition\n# Djangoが利用可能なアプリ（モジュール）Laravelにはないかも。\nINSTALLED_APPS = [\n    'django.contrib.admin',\n    'django.contrib.auth',\n    'django.contrib.contenttypes',\n    'django.contrib.sessions',\n    'django.contrib.messages',\n    'django.contrib.staticfiles',\n]\n\n# laravel の app\u002FHttp\u002FKernel.php のmiddlewareみたいなもの\nMIDDLEWARE = [\n    'django.middleware.security.SecurityMiddleware',\n    'django.contrib.sessions.middleware.SessionMiddleware',\n    'django.middleware.common.CommonMiddleware',\n    'django.middleware.csrf.CsrfViewMiddleware',\n    'django.contrib.auth.middleware.AuthenticationMiddleware',\n    'django.contrib.messages.middleware.MessageMiddleware',\n    'django.middleware.clickjacking.XFrameOptionsMiddleware',\n]\n\n# Laravelのroutesみたいなもの。\nROOT_URLCONF = 'djangodemo.urls'\n\n# Laravelのビューテンプレートが resource\u002Fviewであることを決めている様なことと同じ。DjangoないのTemplateの読み込み先を定義している\nTEMPLATES = [\n    # ...\n]\n\nWSGI_APPLICATION = 'djangodemo.wsgi.application'\n\n# Laravelのconfig\u002Fdatabase.phpと同じ。使用するDBドライバやアクセス情報を記述。\nDATABASES = {\n    'default': {\n        'ENGINE': 'django.db.backends.mysql',\n        'NAME': 'preform',\n        'USER': 'root',\n        'PASSWORD': 'rootroot',\n        'HOST': 'db',\n        'PORT': '3306',\n    }\n}\n\n# 静的ファイルを配置する箇所。Laravelのstorageディレクトリ を定義している様なもの。\nSTATIC_URL = '\u002Fstatic\u002F'\n\n","python",[61,9563,9564,9569,9573,9578,9582,9587,9592,9597,9601,9606,9611,9616,9621,9626,9631,9636,9641,9646,9650,9654,9659,9664,9669,9674,9679,9684,9689,9694,9699,9703,9707,9712,9717,9721,9726,9731,9736,9740,9744,9749,9753,9758,9763,9768,9773,9778,9783,9788,9793,9798,9802,9806,9810,9815],{"__ignoreMap":80},[204,9565,9566],{"class":206,"line":207},[204,9567,9568],{},"BASE_DIR = Path(__file__).resolve().parent.parent\n",[204,9570,9571],{"class":206,"line":228},[204,9572,232],{"emptyLinePlaceholder":231},[204,9574,9575],{"class":206,"line":235},[204,9576,9577],{},"SECRET_KEY = 'hogehoge'\n",[204,9579,9580],{"class":206,"line":244},[204,9581,232],{"emptyLinePlaceholder":231},[204,9583,9584],{"class":206,"line":249},[204,9585,9586],{},"# SECURITY WARNING: don't run with debug turned on in production!\n",[204,9588,9589],{"class":206,"line":257},[204,9590,9591],{},"# Laravelのapp.debugと同じ\n",[204,9593,9594],{"class":206,"line":268},[204,9595,9596],{},"DEBUG = True\n",[204,9598,9599],{"class":206,"line":279},[204,9600,232],{"emptyLinePlaceholder":231},[204,9602,9603],{"class":206,"line":287},[204,9604,9605],{},"# Application definition\n",[204,9607,9608],{"class":206,"line":296},[204,9609,9610],{},"# Djangoが利用可能なアプリ（モジュール）Laravelにはないかも。\n",[204,9612,9613],{"class":206,"line":304},[204,9614,9615],{},"INSTALLED_APPS = [\n",[204,9617,9618],{"class":206,"line":315},[204,9619,9620],{},"    'django.contrib.admin',\n",[204,9622,9623],{"class":206,"line":326},[204,9624,9625],{},"    'django.contrib.auth',\n",[204,9627,9628],{"class":206,"line":337},[204,9629,9630],{},"    'django.contrib.contenttypes',\n",[204,9632,9633],{"class":206,"line":348},[204,9634,9635],{},"    'django.contrib.sessions',\n",[204,9637,9638],{"class":206,"line":4},[204,9639,9640],{},"    'django.contrib.messages',\n",[204,9642,9643],{"class":206,"line":363},[204,9644,9645],{},"    'django.contrib.staticfiles',\n",[204,9647,9648],{"class":206,"line":368},[204,9649,709],{},[204,9651,9652],{"class":206,"line":376},[204,9653,232],{"emptyLinePlaceholder":231},[204,9655,9656],{"class":206,"line":386},[204,9657,9658],{},"# laravel の app\u002FHttp\u002FKernel.php のmiddlewareみたいなもの\n",[204,9660,9661],{"class":206,"line":395},[204,9662,9663],{},"MIDDLEWARE = [\n",[204,9665,9666],{"class":206,"line":402},[204,9667,9668],{},"    'django.middleware.security.SecurityMiddleware',\n",[204,9670,9671],{"class":206,"line":412},[204,9672,9673],{},"    'django.contrib.sessions.middleware.SessionMiddleware',\n",[204,9675,9676],{"class":206,"line":422},[204,9677,9678],{},"    'django.middleware.common.CommonMiddleware',\n",[204,9680,9681],{"class":206,"line":432},[204,9682,9683],{},"    'django.middleware.csrf.CsrfViewMiddleware',\n",[204,9685,9686],{"class":206,"line":447},[204,9687,9688],{},"    'django.contrib.auth.middleware.AuthenticationMiddleware',\n",[204,9690,9691],{"class":206,"line":454},[204,9692,9693],{},"    'django.contrib.messages.middleware.MessageMiddleware',\n",[204,9695,9696],{"class":206,"line":462},[204,9697,9698],{},"    'django.middleware.clickjacking.XFrameOptionsMiddleware',\n",[204,9700,9701],{"class":206,"line":467},[204,9702,709],{},[204,9704,9705],{"class":206,"line":475},[204,9706,232],{"emptyLinePlaceholder":231},[204,9708,9709],{"class":206,"line":483},[204,9710,9711],{},"# Laravelのroutesみたいなもの。\n",[204,9713,9714],{"class":206,"line":1413},[204,9715,9716],{},"ROOT_URLCONF = 'djangodemo.urls'\n",[204,9718,9719],{"class":206,"line":1419},[204,9720,232],{"emptyLinePlaceholder":231},[204,9722,9723],{"class":206,"line":1424},[204,9724,9725],{},"# Laravelのビューテンプレートが resource\u002Fviewであることを決めている様なことと同じ。DjangoないのTemplateの読み込み先を定義している\n",[204,9727,9728],{"class":206,"line":1429},[204,9729,9730],{},"TEMPLATES = [\n",[204,9732,9733],{"class":206,"line":1434},[204,9734,9735],{},"    # ...\n",[204,9737,9738],{"class":206,"line":1440},[204,9739,709],{},[204,9741,9742],{"class":206,"line":1445},[204,9743,232],{"emptyLinePlaceholder":231},[204,9745,9746],{"class":206,"line":1450},[204,9747,9748],{},"WSGI_APPLICATION = 'djangodemo.wsgi.application'\n",[204,9750,9751],{"class":206,"line":1455},[204,9752,232],{"emptyLinePlaceholder":231},[204,9754,9755],{"class":206,"line":1460},[204,9756,9757],{},"# Laravelのconfig\u002Fdatabase.phpと同じ。使用するDBドライバやアクセス情報を記述。\n",[204,9759,9760],{"class":206,"line":1466},[204,9761,9762],{},"DATABASES = {\n",[204,9764,9765],{"class":206,"line":1472},[204,9766,9767],{},"    'default': {\n",[204,9769,9770],{"class":206,"line":1478},[204,9771,9772],{},"        'ENGINE': 'django.db.backends.mysql',\n",[204,9774,9775],{"class":206,"line":1484},[204,9776,9777],{},"        'NAME': 'preform',\n",[204,9779,9780],{"class":206,"line":1490},[204,9781,9782],{},"        'USER': 'root',\n",[204,9784,9785],{"class":206,"line":1496},[204,9786,9787],{},"        'PASSWORD': 'rootroot',\n",[204,9789,9790],{"class":206,"line":1502},[204,9791,9792],{},"        'HOST': 'db',\n",[204,9794,9795],{"class":206,"line":1508},[204,9796,9797],{},"        'PORT': '3306',\n",[204,9799,9800],{"class":206,"line":1513},[204,9801,785],{},[204,9803,9804],{"class":206,"line":1518},[204,9805,804],{},[204,9807,9808],{"class":206,"line":1524},[204,9809,232],{"emptyLinePlaceholder":231},[204,9811,9812],{"class":206,"line":1529},[204,9813,9814],{},"# 静的ファイルを配置する箇所。Laravelのstorageディレクトリ を定義している様なもの。\n",[204,9816,9817],{"class":206,"line":1534},[204,9818,9819],{},"STATIC_URL = '\u002Fstatic\u002F'\n",[5918,9821,9822],{"id":9822},"ルーティング",[13,9824,9825,9826,9828,9829,9832,9833,4823,9836,9839],{},"次は",[61,9827,9540],{},"です。これはLaravelでいうところの ",[61,9830,9831],{},"routes\u002F","と内容的には一緒です。ここでHTTPリクエストに対する処理を結びつけています。Urlディスパッチャと言われています。Laravelでは",[61,9834,9835],{},"web.php",[61,9837,9838],{},"api.php","などいくらか分かれていますが、Djangoではデフォルトで一つです。詳細な記述は次回説明します。",[5918,9841,9842],{"id":9842},"アプリ側のディレクトリ",[13,9844,9845,9846,9849],{},"次にアプリを作成します。Djangoは機能ごとにディレクトリを分割しアプリ(app)と呼んでいます。例としてユーザーのアカウント設定や認証を行う機能を作成するために、",[61,9847,9848],{},"account","というアプリを作成します。",[72,9851,9854],{"className":9852,"code":9853,"language":77},[75],"python3 manage.py startapp account\n",[61,9855,9853],{"__ignoreMap":80},[13,9857,9858,9859,9862],{},"ディレクトリ が一つ増えて、",[61,9860,9861],{},"account\u002F","というのが作成されたはずです。アプリ内には初期では以下の通りテンプレートが作成されます。",[72,9864,9867],{"className":9865,"code":9866,"language":77},[75],"├── account\n│   ├── __init__.py\n│   ├── admin.py\n│   ├── apps.py\n│   ├── migrations\n│   │   └── __init__.py\n│   ├── models.py\n│   ├── tests.py\n│   └── views.py\n├── db.sqlite3\n├── djangodemo\n├── manage.py\n",[61,9868,9866],{"__ignoreMap":80},[13,9870,9871,9872,9875,9876,9878],{},"Laravelは",[61,9873,9874],{},"app\u002F","配下にコントローラーやモデル、ジョブなどのクラスファイルを定義します。独立した機能であっても",[61,9877,9874],{},"配下に配置してクラス名などで判別する感じです。",[13,9880,9881,9882,9885],{},"しかしDjangoは",[20,9883,9884],{},"機能ごとにディレクトリ を作成し、その機能に関連するモデル、ビュー、テストの設定を記述","します。ここがLaravelとすこし違うところです。",[13,9887,9888,9889,4351,9892,9895],{},"アプリを",[61,9890,9891],{},"setting.py",[61,9893,9894],{},"INSTALLED_APPS","に記述することで、",[72,9897,9899],{"className":9559,"code":9898,"language":9561,"meta":80,"style":80},"from account.model import Account\n\n# account\u002Fmodel.py のAccountクラスを使用する\n",[61,9900,9901,9906,9910],{"__ignoreMap":80},[204,9902,9903],{"class":206,"line":207},[204,9904,9905],{},"from account.model import Account\n",[204,9907,9908],{"class":206,"line":228},[204,9909,232],{"emptyLinePlaceholder":231},[204,9911,9912],{"class":206,"line":235},[204,9913,9914],{},"# account\u002Fmodel.py のAccountクラスを使用する\n",[13,9916,9917,9918,9920],{},"などそのアプリ内機能をモジュールとして使用できます。Laravel（PHP）風に解説すると、適切な名前空間を定義して任意のファイルで",[61,9919,7570],{},"する感じです。",[72,9922,9924],{"className":539,"code":9923,"language":541,"meta":80,"style":80},"use App\\Models\\Account\n",[61,9925,9926],{"__ignoreMap":80},[204,9927,9928],{"class":206,"line":207},[204,9929,9923],{},[13,9931,9932],{},"それぞれのファイルを説明します。",[42,9934,9935,9941,9950,9959,9968,9974],{},[45,9936,9937,9940],{},[61,9938,9939],{},"admin.py",":Djangoが提供する管理者画面でモデルの内容をどう表記するかを定義します。",[45,9942,9943,9946,9947,9949],{},[61,9944,9945],{},"apps.py",":アプリの設定ファイル。アプリの名前やデフォルトのプライマリーキーなどを設定でき、",[61,9948,9894],{},"に必要",[45,9951,9952,9955,9956],{},[61,9953,9954],{},"models.py",": アプリのモデルファイル。テーブルや使用するフィールドの定義などを行う。Laravelでいう",[61,9957,9958],{},"app\u002Fmodels",[45,9960,9961,9964,9965],{},[61,9962,9963],{},"tets.py",": アプリのテストファイル。テストコードを記述する。Laravelでいう",[61,9966,9967],{},"tests\u002F",[45,9969,9970,9973],{},[61,9971,9972],{},"views.py",": アプリのビューファイル。DjangoではビューはLaravelでいうControllerみたいな働きをする。",[45,9975,9976,9979,9980,9983],{},[61,9977,9978],{},"migrations\u002F",": DBに対する変更、マイグレーションファイルが格納されます。Laravelでいう、",[61,9981,9982],{},"database\u002Fmigrations","です。",[13,9985,9986,9987,9990],{},"のこっている ",[61,9988,9989],{},"__init__.py","ですがこれはpythonがディレクトリ をパッケージとして認識して、importできる様にするために必要なファイルです。python2ではこのファイルがないとimportが動作しません。python3からは不要ですが、後方互換性のために作っておいて損はありません。",[72,9992,9994],{"className":9559,"code":9993,"language":9561,"meta":80,"style":80},"from account.models\n\"\"\"\naccountディレクトリ（パッケージ）のmodels.py（モジュール）を読み込むということが,\n__init__.pyがあるとできる。\n\"\"\"\n",[61,9995,9996,10001,10006,10011,10016],{"__ignoreMap":80},[204,9997,9998],{"class":206,"line":207},[204,9999,10000],{},"from account.models\n",[204,10002,10003],{"class":206,"line":228},[204,10004,10005],{},"\"\"\"\n",[204,10007,10008],{"class":206,"line":235},[204,10009,10010],{},"accountディレクトリ（パッケージ）のmodels.py（モジュール）を読み込むということが,\n",[204,10012,10013],{"class":206,"line":244},[204,10014,10015],{},"__init__.pyがあるとできる。\n",[204,10017,10018],{"class":206,"line":249},[204,10019,10005],{},[27,10021,10022],{"id":10022},"今回はとりあえずここまで",[13,10024,10025],{},"ひとまずこの回ではディレクトリやファイルの説明までとしておきます。次回はユーザーモデルを使用した認証の実装とLaravelと比較したCRUDの一部（記事の作成）を実装します。",[2310,10027,10028],{},"html pre.shiki code .s5Dmg, html code.shiki .s5Dmg{--shiki-default:#FFCB6B}html pre.shiki code .sfyAc, html code.shiki .sfyAc{--shiki-default:#C3E88D}html pre.shiki code .sdLwU, html code.shiki .sdLwU{--shiki-default:#82AAFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .s-wAU, html code.shiki .s-wAU{--shiki-default:#F07178}html pre.shiki code .sAklC, html code.shiki .sAklC{--shiki-default:#89DDFF}html pre.shiki code .s0W1g, html code.shiki .s0W1g{--shiki-default:#BABED8}html pre.shiki code .sx098, html code.shiki .sx098{--shiki-default:#F78C6C}html pre.shiki code .sC9rS, html code.shiki .sC9rS{--shiki-default:#464B5D;--shiki-default-font-style:italic}",{"title":80,"searchDepth":235,"depth":235,"links":10030},[10031,10035,10040,10048],{"id":6019,"depth":228,"text":6019,"children":10032},[10033,10034],{"id":8782,"depth":235,"text":6125},{"id":6633,"depth":235,"text":6633},{"id":9216,"depth":228,"text":9216,"children":10036},[10037,10038,10039],{"id":5920,"depth":244,"text":5920},{"id":5954,"depth":244,"text":5954},{"id":5993,"depth":244,"text":5993},{"id":9293,"depth":228,"text":9294,"children":10041},[10042,10043],{"id":9300,"depth":235,"text":9300},{"id":9447,"depth":235,"text":9447,"children":10044},[10045,10046,10047],{"id":9544,"depth":244,"text":9544},{"id":9822,"depth":244,"text":9822},{"id":9842,"depth":244,"text":9842},{"id":10022,"depth":228,"text":10022},[2332],"2022-02-12",{},"\u002Fseries\u002Flaravel-to-django-1",{"title":8647,"description":8160},"laravel_to_django","Laravel使いがDjangoでwebアプリを作るよ","series\u002Flaravel-to-django-1",[10058,9561,3988,541],"django","laralve_to_django\u002Fthumbnail.png","k4TYlI4xXfHM5YMh4HtklCzEfD1cyqgLf2FCDeypoBY",1780987146399]