[{"data":1,"prerenderedAt":1991},["ShallowReactive",2],{"articles-page-7":3},{"count":4,"content":5},63,[6,911,1294],{"id":7,"title":8,"body":9,"category":896,"createdAt":898,"description":8,"extension":899,"index":900,"meta":901,"navigation":902,"path":903,"publish":902,"seo":904,"series":900,"seriesTitle":900,"stem":905,"tag":906,"thumbnail":909,"updatedAt":900,"__hash__":910},"articles\u002Farticles\u002Flalavel7-deplay-on-heroku.md","HerokuにLaravel 7.0で作成したアプリケーションをデプロイする。",{"type":10,"value":11,"toc":865},"minimark",[12,16,24,29,32,43,50,55,58,74,77,80,84,90,93,111,114,117,121,124,129,136,139,172,176,180,191,194,197,204,207,211,218,221,226,232,235,238,241,244,247,254,257,260,264,267,270,277,280,285,288,292,298,301,304,307,310,318,323,326,329,332,335,339,351,355,358,362,365,553,556,570,573,582,585,593,596,603,611,614,618,624,627,630,634,637,640,643,648,651,654,658,661,667,674,677,680,684,692,695,698,706,709,715,721,728,734,737,743,746,750,756,759,776,779,783,786,800,803,807,810,813,817,820,826,829,832,835,838,841,848,851,854,858,861],[13,14,15],"p",{},"こんにちはじゅんです。会社で試験的に作成したLaravelアプリケーションをHerokuにデプロイする機会があり、いくらかハマりポイントなどがあったのでメモがてら残そうと思います。どんなLaravel アプリなのかなど、ある程度具体的な要件の定義から書くので、",[13,17,18,19,23],{},"「さっさとデプロイの説明しろや！」という人は目次から ",[20,21,22],"strong",{},"「Herokuへのデプロイ作業を開始」"," をクリックして飛んでください。",[25,26,28],"h2",{"id":27},"デプロイの前に作成したlaravel-アプリについて","デプロイの前に、作成したLaravel アプリについて",[13,30,31],{},"会社でRestfulなWebAPIとそのコンテンツを提供するサービスを開発しようという動きがあり、Laravelやvue、webAPIは少しわかる私に試験的な開発をまかされLaravelで作りました。",[13,33,34,35,38,39,42],{},"最近、巷で有名な ",[20,36,37],{},"「Headless CMS」と似たような動きをします。"," 細かい要件は書くことはできないですが、 ",[20,40,41],{},"LaravelでAPIサーバー兼コンテンツ管理画面を作成して置きます。"," お客さんはアプリケーションにログインしてコンテンツの編集などを行えます。そしてフロントエンドは別のサーバーに配置するか、既存のアプリを使用します。",[13,44,45,46,49],{},"Ajaxなどを通して認証付きAPIをLaravelが置いてあるサーバーに投げれば、 ",[20,47,48],{},"データ（掲載コンテンツの内容）をJSON・XMLで手に入れることができます。"," 欲しい情報は適宜、非同期でAPIで呼び出して取得するという形式を取っています。モダンなやり方ですね。",[51,52,54],"h3",{"id":53},"laravel-でapiサーバーを構築した理由","Laravel でAPIサーバーを構築した理由",[13,56,57],{},"APIサーバーを構築するフレームワークは他にもありますが以下のような理由により、Laravelを選びました。",[59,60,61,65,68,71],"ul",{},[62,63,64],"li",{},"私が扱いやすい。学習しやすい。",[62,66,67],{},"Laravel Passport でOAuth2.0 に準拠したAPI認証機能をさくっと構築可能",[62,69,70],{},"Laravel Mixのおかげで管理画面（お客さんが触るGUI）の作成がとても簡単。",[62,72,73],{},"LaravelにはAPIに関する認証や制限を設置できるので、",[13,75,76],{},"決められたお客さんのコンテンツ（サーバー）からのAPIアクセスを許可する。\nお試しユーザー、プレミアムユーザーと言ったユーザーロールごとに使用できるAPIの制限が簡単。\nと言ったことも可能です。「やっぱりLaravelはフルスタックフレームワークだけあるな」と思えるほどの物です。とにかく上記のようなLaravel アプリを今回はHerokuにデプロイします。",[13,78,79],{},"作成したLaravelアプリについての説明・構築は今回の記事ではしません。Herokuへのデプロイがメインになります。",[51,81,83],{"id":82},"なぜheroku","なぜHeroku??",[13,85,86,87],{},"今回のプロジェクト自体がまだ試験的であることも理由の一つですが、単にVPSとかで構築するのが面倒だったからです。 ",[20,88,89],{},"各種ミドルウェア やソフトが最新版の物であっても、マニュアルがあってもセキュリティや権限の設定とかを考えて構築すると日が暮れます。",[13,91,92],{},"Herokuの理念が",[94,95,99,100],"div",{"className":96},[97,98],"alert","alert-success","\nHeroku はアプリの構築、提供、監視、スケールに役立つクラウドプラットフォームで、アイデアを出してから運用を開始するまでのプロセスを迅速に進めることが可能です。また、インフラストラクチャの管理の問題からも解放されます。\n",[101,102,103],"small",{},[104,105,106],"i",{},[107,108,109],"a",{"href":109,"target":110},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fja\u002Fdocs\u002FWeb\u002FCSS\u002Ffont-feature-settings","_blank",[13,112,113],{},"とあるようにインフラの構築や管理は面倒です。そのため予めインフラがすぐに作られ、しかしある程度カスタマイズが可能なサービスを利用すると開発者としてはアプリ（今回でいうLaravelアプリ）の開発に注力できます。",[13,115,116],{},"またVPSでも可能だと思いますが、GitHubとかと連携して本番環境・開発環境を構築できるのも魅力の一つです。まだ試験的な段階なので、AWSやVPSほどの自由度は今回のアプリではいらないかな？と思いHerokuにしました。",[25,118,120],{"id":119},"herokuで構築した開発運用フロー","Herokuで構築した開発・運用フロー",[13,122,123],{},"下図のような感じです。バージョン管理などはGitHubを用いています。適宜、リモートリポジトリにpushしたらその変更が自動でHerokuのアプリ（個々のサーバーみたいなものだと思ってください。）に反映されます。",[125,126],"image-render",{":src":127,":width":128},"'_mix\u002Fheroku_archtecture-768x446.jpg'","'100%'",[13,130,131,132,135],{},"プルリクエストを送ると、 ",[20,133,134],{},"そのリクエストに応じたアプリが自動的に構築されるように設定もできます。"," Stagingで最終確認をしてOKであれば公開環境にデプロイさせます。",[13,137,138],{},"そのため今回の記事では以下のような流れで構築してます。",[140,141,142,145,148,151,154,157,160,163,166,169],"ol",{},[62,143,144],{},"Herokuのアカウント作成、アプリの作成",[62,146,147],{},"Herokuパイプラインの作成",[62,149,150],{},"Herokuアプリの設定",[62,152,153],{},"HerokuとGithubの連携",[62,155,156],{},"DBとの連携",[62,158,159],{},"環境変数の設定",[62,161,162],{},"Laravelアプリでのmigrationなど",[62,164,165],{},"本番環境との連携",[62,167,168],{},"SSL化する",[62,170,171],{},"API呼び出し確認",[25,173,175],{"id":174},"herokuへのデプロイ作業を開始","Herokuへのデプロイ作業を開始",[51,177,179],{"id":178},"herokuのアカウントを作成","Herokuのアカウントを作成",[13,181,182,183,186,190],{},"当たり前ですがHerokuでのアカウントを作成します。これがないと何も始まりません。 ",[107,184],{"href":185,"target":110},"https:\u002F\u002Fjp.heroku.com\u002F",[107,187,185],{"href":185,"rel":188},[189],"nofollow","にアクセスして「新規登録」に進みます。",[125,192],{":src":193,":width":128},"'_mix\u002Fheroku-1.png'",[13,195,196],{},"名前やアドレスなど適宜入力します。アカウントを作成する段階では日本語でも大丈夫です。選択するプランですがとりあえず無料の物にしておきましょう。",[13,198,199,203],{},[107,200,201],{"href":201,"rel":202},"https:\u002F\u002Fjp.heroku.com\u002Fpricing",[189]," にてプランの内容や価格を知ることができます。完全フリーの場合はSSL(httpsにするやつ)の設定が行えない、最低限のスペックしかないなど本当に試験的にサーバー上にアップする程度に使用します。",[13,205,206],{},"商用で運用する場合は有料のStandardなどにアップグレードしましょう。ここでは開発サーバーをフリー版、本番環境をhobbyにしておきます。",[51,208,210],{"id":209},"herokuアプリを作成する","Herokuアプリを作成する。",[13,212,213,214,217],{},"今回デプロイするLaravelのソース達を入れる、Herokuアプリを作成します。 ",[20,215,216],{},"Herokuアプリというのは簡単にいうとサーバーそのものだと思ってください。"," Herokuアプリに、使用するソフトやドキュメントルートの設定、環境変数の設定、連携するGithubのレポジトリなどの設定を行うことで、Laravelが正常に動いてアプリケーションサーバー（サービスを提供するサーバー）として機能するようになります。",[13,219,220],{},"ここでは実務の開発体制をとるのでpipelineを構築します。",[222,223,225],"h4",{"id":224},"pipe-linesを構築する","pipe linesを構築する",[13,227,228,231],{},[20,229,230],{},"pipe linesというのは「開発、レビュー、ステージング、本番」など複数の段階に分けてアプリを動かすことができるHerokuアプリのグループのことをいいます。"," もう少し簡単にいうと、",[13,233,234],{},"修正した物開発中のものをテストする「開発サーバー」、お客さんが触り実際にサービスが提供される「本番サーバー」を同時に構築して管理しやすくしたアプリ達です。今回は開発用・確認用、本番の２つの構成にします。",[13,236,237],{},"アカウントを作ると個人のダッシュボード画面に移動します。そこの「New」をクリックし、[Create New pipeline」を選択します。",[125,239],{":src":240,":width":128},"'_mix\u002Fheroku_createnew-1024x306.png'",[13,242,243],{},"pipelineを作成すると、pipelineの名前と連携するリポジトリを選ぶことができます。ここではまだ連携しないので、名前だけ入力して「create pipeline」で作成。すると二つのアプリを加えることができるpipelineが作成されました。",[125,245],{":src":246,":width":128},"'_mix\u002Fdashnoard-1024x262.png'",[13,248,249,250,253],{},"そしてたらアプリの構築をしていきます。二つ作るのは少し面倒ですが仕方ないです。では ",[20,251,252],{},"開発の方（staging）"," から作成していきます。「Add app」をクリックすると、既存のアプリを追加するか、新しく作るかを聞いてきます。ここでは「create new app」を選択。すると右側にアプリの基本情報を入力する欄が出てきます。",[13,255,256],{},"そしたらアプリの名前とリージョンを選択して「create app」します。フリープランの場合はアメリカかヨーロッパリージョンしか選べませんが、特に問題ないです。（その地域に隕石が落るとかしてサーバーが物理的にやられない限り）",[13,258,259],{},"アプリ名を英語で入力して「create」をクリックして作成されます。名前はとりあえず「staging-app-laravel」としておきます。",[51,261,263],{"id":262},"build-packを設定する","Build packを設定する",[13,265,266],{},"開発用アプリの設定が可能になったのでダッシュボードから、アプリ名をクリックして移動します。すると下のようなアプリに関する設定ができる画面に移ります。設定画面でできることは後で使用するheroku CLIでも行うことができます。今回はGUIの方をつかっていきます。",[125,268],{":src":269,":width":128},"'_mix\u002Fapp_setting_dev-1024x497.png'",[13,271,272,273,276],{},"まずは ",[20,274,275],{},"一番最初にbuild packというものを設定"," します。build packではnode.js,PHPなどを選択します。まあ使うサーバーサイドの言語を選ぶ物だと思ってください。上記の画面から「Settings」を選択します。",[13,278,279],{},"「Buildpacks」という項目があるので「Add builpack」でパックを追加します。",[125,281],{":src":282,":width":283,":center":284},"'_mix\u002Fbuldpack-300x222.png'","'500px'","true",[13,286,287],{},"LaravelなのでPHPは必須です。そしてnode modulesもあるのでnode.jsも入れます。ここでサーバーサイドの言語を複数追加すれば各言語でアプリを動かすことができます。",[51,289,291],{"id":290},"githubと連携してソースを入れる","Githubと連携してソースを入れる",[13,293,272,294,297],{},[20,295,296],{},"Laravelアプリケーションのソースをこのアプリにぶち込む必要があります。"," herokuではGithubとの連携がオススメされているので、今回はその方法で行きます。上記画面から「Deploy」をクリックして移動します。",[125,299],{":src":300,":width":128},"'_mix\u002Fdeply_set-1024x257.png'",[13,302,303],{},"deployment method でGithubを選びます。すると連携するリポジトリ名を入力する欄が出現します。もし初めてherokuを触ってgithubと連携していない場合は「githubと連携する？」みたいな物が表示されたはずなので、言われる通りに連携します。",[13,305,306],{},"githubのアカウントを持っていてログイン状態が保持されていれば、githubの連携許可画面が表示されます。それで簡単に連携ができます。",[13,308,309],{},"連携が完了すると下のようにデプロイに関する設定が出てきます。githubを用いる場合は２通りのデプロイがあります。",[59,311,312,315],{},[62,313,314],{},"ブランチを指定して手動でデプロイ",[62,316,317],{},"ブランチに変化があったら自動でデプロイ",[13,319,320],{},[20,321,322],{},"毎回herokuにログインするのも面倒なので「Enable Automatic Deploys」を選択して自動デプロイができるようにしておきます。",[125,324],{":src":325,":width":128},"'_mix\u002Fautodeploy-1024x421.png'",[13,327,328],{},"リポジトリにソースをプッシュすると以下のようにbuildしているのがダッシュボードで見ることができます。（たまに更新ボタンを押さないと表示されない時がある。）",[125,330],{":src":331,":width":283,":center":284},"'_mix\u002Fbuildings-300x166.png'",[13,333,334],{},"しかしまだDBとの連携や環境変数の設定が済んでいないので、アプリをみたところで500エラーしか表示されません。次はDBの設定に移ります。",[51,336,338],{"id":337},"jawsdbを設定する","JawsDBを設定する",[13,340,341,342,346,347,350],{},"herokuでは",[343,344,345],"code",{},"Procfile"," を用いてドキュメントルート やwebサーバー&phpの設定ができますが、DBに関してはアドオンを利用する必要があります。herokuではpostgreSQLの使用を進めていますが、私が作成したLaravelアプリはmysql仕様で作っていたので ",[20,348,349],{},"今回はmysqlをDBとして使用します。","（まあpostgreSQLに変更もできましたけど…）",[51,352,354],{"id":353},"使用するdbのアドオンについて","使用するDBのアドオンについて",[13,356,357],{},"ちなみに、「heroku mysql DB」と検索すると **「ClearDB」というmysqlベースのDBアドオンを入れるように書かれた記事が多いですが、私はそこでハマった物がありました。**以下に記述しておきます。",[222,359,361],{"id":360},"laravel-55以上の場合cleardbの使用はおすすめしない理由","Laravel 5.5以上の場合、ClearDBの使用はおすすめしない理由",[13,363,364],{},"ClearDBを用いててデータベースへマイグレーションをおこなった際に。こんなエラーが発生。",[366,367,372],"pre",{"className":368,"code":369,"language":370,"meta":371,"style":371},"language-bash shiki shiki-themes material-theme-ocean","php artisan migrate\n...\n...\nSQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was too long; max key length is 767 bytes (SQL: alter table `users` add unique `users_email_unique`(`email`))\nSQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was too long; max key length is 767 bytes\n","bash","",[343,373,374,390,397,402,511],{"__ignoreMap":371},[375,376,379,383,387],"span",{"class":377,"line":378},"line",1,[375,380,382],{"class":381},"s5Dmg","php",[375,384,386],{"class":385},"sfyAc"," artisan",[375,388,389],{"class":385}," migrate\n",[375,391,393],{"class":377,"line":392},2,[375,394,396],{"class":395},"sdLwU","...\n",[375,398,400],{"class":377,"line":399},3,[375,401,396],{"class":395},[375,403,405,408,411,414,417,420,423,427,430,433,436,439,442,446,449,451,454,457,460,463,467,470,473,476,479,482,485,488,490,493,495,498,500,503,505,508],{"class":377,"line":404},4,[375,406,407],{"class":381},"SQLSTATE[42000]:",[375,409,410],{"class":385}," Syntax",[375,412,413],{"class":385}," error",[375,415,416],{"class":385}," or",[375,418,419],{"class":385}," access",[375,421,422],{"class":385}," violation:",[375,424,426],{"class":425},"sx098"," 1071",[375,428,429],{"class":385}," Specified",[375,431,432],{"class":385}," key",[375,434,435],{"class":385}," was",[375,437,438],{"class":385}," too",[375,440,441],{"class":385}," long",[375,443,445],{"class":444},"sAklC",";",[375,447,448],{"class":381}," max",[375,450,432],{"class":385},[375,452,453],{"class":385}," length",[375,455,456],{"class":385}," is",[375,458,459],{"class":425}," 767",[375,461,462],{"class":385}," bytes",[375,464,466],{"class":465},"s0W1g"," (SQL: ",[375,468,469],{"class":385},"alter",[375,471,472],{"class":385}," table",[375,474,475],{"class":444}," `",[375,477,478],{"class":381},"users",[375,480,481],{"class":444},"`",[375,483,484],{"class":381}," add",[375,486,487],{"class":385}," unique",[375,489,475],{"class":444},[375,491,492],{"class":381},"users_email_unique",[375,494,481],{"class":444},[375,496,497],{"class":444},"(",[375,499,481],{"class":444},[375,501,502],{"class":381},"email",[375,504,481],{"class":444},[375,506,507],{"class":444},")",[375,509,510],{"class":465},")\n",[375,512,514,516,518,520,522,524,526,528,530,532,534,536,538,540,542,544,546,548,550],{"class":377,"line":513},5,[375,515,407],{"class":381},[375,517,410],{"class":385},[375,519,413],{"class":385},[375,521,416],{"class":385},[375,523,419],{"class":385},[375,525,422],{"class":385},[375,527,426],{"class":425},[375,529,429],{"class":385},[375,531,432],{"class":385},[375,533,435],{"class":385},[375,535,438],{"class":385},[375,537,441],{"class":385},[375,539,445],{"class":444},[375,541,448],{"class":381},[375,543,432],{"class":385},[375,545,453],{"class":385},[375,547,456],{"class":385},[375,549,459],{"class":425},[375,551,552],{"class":385}," bytes\n",[13,554,555],{},"ローカルでは起きなかった謎のエラー。「上限は767bytesだけど、1071bytesのキーが設定されている」と怒っている。とりあえずmigrationが中途半端にUserテーブルだけ作って止まってしまったので、migrationをリフレッシュする。",[366,557,559],{"className":368,"code":558,"language":370,"meta":371,"style":371},"php artisan migrate:fresh\n",[343,560,561],{"__ignoreMap":371},[375,562,563,565,567],{"class":377,"line":378},[375,564,382],{"class":381},[375,566,386],{"class":385},[375,568,569],{"class":385}," migrate:fresh\n",[13,571,572],{},"このエラーを調べたところどうやら使用した「ClearDB」のmysqlが5.5と古い上に、Laravelで設定していたmysqlで用いる文字コードとの関係で起きていた。以下のQiita記事が非常に参考になった。",[94,574,577,578],{"className":575},[97,576],"alert-info","\n    ",[107,579,581],{"href":580,"target":110},"https:\u002F\u002Fqiita.com\u002Fbeer_geek\u002Fitems\u002F6e4264db142745ea666f","Laravel5.4以上、MySQL5.7.7未満 でusersテーブルのマイグレーションを実行すると Syntax error が発生する",[13,583,584],{},"対処は２つ。",[59,586,587,590],{},[62,588,589],{},"mysqlのバージョンを上げて5.7にする。",[62,591,592],{},"文字コードを上限を超えないものに変更する。",[13,594,595],{},"今回の場合はmysqlのバージョンを上げる方にした。しかしこちらのStackOver Flow にもあるようにClear DBで用いられているmysqlのバージョンを上げるのは無理らしい。。",[94,597,577,599],{"className":598},[97,576],[107,600,602],{"href":601,"target":110},"https:\u002F\u002Fstackoverflow.com\u002Fquestions\u002F56253354\u002Fhow-to-update-the-version-of-the-mysql-engine-in-cleardb","How to update the version of the MySQL engine in ClearDB?\n    ",[13,604,605,606,610],{},"代わりに",[107,607,609],{"href":608,"target":110},"https:\u002F\u002Felements.heroku.com\u002Faddons\u002Fjawsdb","「JawsDB」","というのを使うとmysqlのバージョンが5.7のものが入ってくれる。",[13,612,613],{},"herokuでのmysql導入を調べると「ClearDBを入れろ」という記事が多く、言われたままにやったら起きたのでハマりポイントでした。",[51,615,617],{"id":616},"jawsdbアドオンを入れる","jawsDBアドオンを入れる",[13,619,620,621],{},"アドオンはherokuの追加機能のような物です。「Resources」を選ぶと「Add-ons」という項目がありそこでアドオンを導入できます。jawsDBと入力するとすぐに出てきます。 ",[20,622,623],{},"「jawsDB MySQL」を選択して追加。",[125,625],{":src":626,":width":283,":center":284},"'_mix\u002Fjawsdb-300x248.png'",[13,628,629],{},"データ容量などに応じたプランを選ぶことができます。ここではFreeでおk。「Provision」をクリックしてアプリに追加します。",[51,631,633],{"id":632},"dbの接続情報を手に入れる","DBの接続情報を手に入れる",[13,635,636],{},"このjawsDBはherokuのアプリ内（というかサーバー）に組み込まれている訳ではないので、ホストやポートなどを指定する必要があります。jawsDBを導入するとそのherokuアプリを結びついたデータベースが作成されます。",[13,638,639],{},"その情報はアドオンのページで、jawsDBの箇所がリンクになっているのでそこをクリックします。",[125,641],{":src":642,":width":283,":center":284},"'_mix\u002Faddons-768x194.png'",[13,644,645],{},[20,646,647],{},"するとDBへの接続情報が書かれた画面が表示されます。ここに接続先ホスト、ユーザー名、パスワードなどが書いてあるので控えておきます。",[125,649],{":src":650,":width":128},"'_mix\u002Fjawsdashboard-768x360.png'",[13,652,653],{},"自前のmysqlコマンドやクラアントから普通にアクセスすることができます。では次にこれらの情報を元に環境変数を設定します。後少しでアプリがデプロイされます！頑張ってください！",[51,655,657],{"id":656},"config-bars環境変数を設定","config bars(環境変数)を設定",[13,659,660],{},"DBの接続情報を手に入れたのでLaravel内で使用される環境変数を定義していきます。Laravelのファイル群に「.env」というのがあったと思います。そのファイルは環境変数を定義しており、DBのパスワード、APIキーなど重要な値や環境によって変化する値が格納されています。",[13,662,663,664],{},"基本的には.envはgitでは管理せず、githubにはプッシュされないように除外してあります。",[20,665,666],{},"ローカルとherokuではDBの接続情報も違うので異なる環境変数を設定する必要がります。",[13,668,669,670,673],{},"そのために ",[20,671,672],{},"「Settings」の「config bars」にて環境変数を設定","できます。Laravelの.envを開いて必要な項目をコピーして適宜適切な値を入れておきましょう。",[125,675],{":src":676,":width":128},"'_mix\u002Fconfigure-768x115.png'",[51,678,679],{"id":679},"マイグレーションを行う",[222,681,683],{"id":682},"heroku-cliを入れる","heroku CLIを入れる",[13,685,686,687,691],{},"環境変数の設定を終えればLaravelのマイグレーションが使えるようになります。heroku CLIを用いてアプリをBashで操作します。ローカルに",[107,688,690],{"href":689,"target":110},"https:\u002F\u002Fdevcenter.heroku.com\u002Farticles\u002Fheroku-cli","heroku CLI","をダウンロードします。",[222,693,694],{"id":694},"アプリをbashから操作する",[13,696,697],{},"macならターミナルを開いてログインをします。",[366,699,704],{"className":700,"code":702,"language":703},[701],"language-text","~ % heroku login\nheroku: Press any key to open up the browser to login or q to exit:[Enter]\nOpening browser to https:\u002F\u002Fcli-auth.heroku.com\u002Fauth\u002Fcli\u002Fbrowser\u002F********\nLogging in... done\nLogged in as your@email.com\n","text",[343,705,702],{"__ignoreMap":371},[13,707,708],{},"ブラウザがいったん開いてログインが完了です。そしたらアプリをbashで操作します。以下のコマンドを唱えます。",[366,710,713],{"className":711,"code":712,"language":703},[701],"~ % heroku run bash -a staging-app-laravel\nRunning bash on ⬢ staging-app-laravel... done\n",[343,714,712],{"__ignoreMap":371},[13,716,717,720],{},[20,718,719],{},"「-a」でアプリ名を指定できます。バッシュの時はアプリ名を指定しないと怒られます。"," すると表示が少し変わりバッシュ操作が可能になります。",[13,722,723,724,727],{},"ホームディレクトリ 内にLaravelのソース達が配置されているのが確認できます。このLaravelアプリのルートで ",[343,725,726],{},"php artisan","を唱えることができます。それ以外のディレクトリなどで行うと「そんなコマンド知らんよ」と怒られます。ではマイグレーション。",[366,729,732],{"className":730,"code":731,"language":703},[701],"~ $ php artisan migrate\n**************************************\n*     Application In Production!     *\n**************************************\n\n Do you really wish to run this command? (yes\u002Fno) [no]:\n > yes\n\nMigration table created successfully.\nMigrating: 2014_10_12_000000_create_users_table\nMigrated:  2014_10_12_000000_create_users_table (0.06 seconds)\nMigrating: 2014_10_12_100000_create_password_resets_table\nMigrated:  2014_10_12_100000_create_password_resets_table (0.05 seconds)\nMigrating: 2016_06_01_000001_create_oauth_auth_codes_table\nMigrated:  2016_06_01_000001_create_oauth_auth_codes_table (0.09 seconds)\nMigrating: 2016_06_01_000002_create_oauth_access_tokens_table\nMigrated:  2016_06_01_000002_create_oauth_access_tokens_table (0.09 seconds)\nMigrating: 2016_06_01_000003_create_oauth_refresh_tokens_table\nMigrated:  2016_06_01_000003_create_oauth_refresh_tokens_table (0.06 seconds)\nMigrating: 2016_06_01_000004_create_oauth_clients_table\nMigrated:  2016_06_01_000004_create_oauth_clients_table (0.05 seconds)\nMigrating: 2016_06_01_000005_create_oauth_personal_access_clients_table\nMigrated:  2016_06_01_000005_create_oauth_personal_access_clients_table (0.02 seconds)\nMigrating: 2019_08_19_000000_create_failed_jobs_table\nMigrated:  2019_08_19_000000_create_failed_jobs_table (0.03 seconds)\n...#自分で設定した他のテーブルも無事マイグレーションされた。\n",[343,733,731],{"__ignoreMap":371},[13,735,736],{},"これでDBに必要なテーブルが設定されました。ユーザーテーブルに私たちが管理するための管理ユーザーを作成するシーダーを予め作成したおいたのでそれも実行。",[366,738,741],{"className":739,"code":740,"language":703},[701],"~ $ php artisan db:seed\n",[343,742,740],{"__ignoreMap":371},[13,744,745],{},"これでDBにもテーブルが挿入されたのでアプリが動くようになりました。",[51,747,749],{"id":748},"herokuアプリをブラウザで開く","Herokuアプリをブラウザで開く",[13,751,752,753],{},"ブラウザのHerokuの画面に戻り、pipe lineの一覧を開きます。 ",[20,754,755],{},"開発アプリの「Open app」をクリックすると構築したLaravelアプリの画面が表示されました。",[13,757,758],{},"「500 Internal ServerError」などが表示されている場合は以下の４つを確かめましょう。",[140,760,761,764,767,770,773],{},[62,762,763],{},"$ heroku logs --tail -a your_app_name でログを確かめる。",[62,765,766],{},"Laravelアプリそのものにミスはないか？",[62,768,769],{},"環境変数の設定はあっているか？",[62,771,772],{},"DBに接続されているか？DBのテーブルの設定は正しいか？",[62,774,775],{},"ビルドのログを確かめてビルドが失敗していないか？",[13,777,778],{},"アプリのビルドが成功している場合はサーバー自体は構築できています。Laravelアプリや環境変数などに問題ある可能性が高いので確認してみましょう。",[51,780,782],{"id":781},"次は本番のアプリ","次は本番のアプリ！！",[13,784,785],{},"ここまでもかなりボリューミーですが、今回は２つアプリを入れたので２つ目も設定します。ですがやることは開発の方でやったように",[59,787,788,791,794,797],{},[62,789,790],{},"ビルドパックを入れる",[62,792,793],{},"アドオンを入れる",[62,795,796],{},"環境変数を設定する",[62,798,799],{},"DBと接続してマイグレーションする",[13,801,802],{},"ぐらいです。そして私の運用ではgithubと本番のアプリは連携させず、stagingのアプリを本番に移行するので少し手間が省けます。stagingのものを本番に移行する場合はpipelineの画面で「promote to production」を選択すればすぐに反映されます。",[51,804,806],{"id":805},"本番サーバーのssl化","本番サーバーのSSL化",[13,808,809],{},"最後に本番サーバーのSSL化です。無料プランのは自動でSSLは付与されません。証明書があれば付与することができますが無料のdynoはスペックが最低限な上、証明書取得でもお金がかかるので大人しくhobbyプラン以上の有料版にアップグレードしましょう。",[13,811,812],{},"本番アプリをhobbyプランに上げるとACM（自動証明書管理）が有効になり、証明書が発行できて有効になってます。SSL化はこれだけです。込み入った証明書や独自ドメイン の付与の際には少し設定が必要です。",[51,814,816],{"id":815},"apiサーバーとして機能しているかを確かめる","APIサーバーとして機能しているかを確かめる",[13,818,819],{},"とりあえず本番環境までのデプロイが完了しました。Laravel Passport を用いて認証付きAPIを投げられるのでそのチェックをしてみます。とその前にlaravel passportの設定上必要なものがあるのでアプリ内にbashで入って以下を実行。",[366,821,824],{"className":822,"code":823,"language":703},[701],"~ $ php artisan passport:install\n",[343,825,823],{"__ignoreMap":371},[13,827,828],{},"Laravel Passport を用いて認証付きAPIを投げられるのでそのチェックをしてみます。シーダーで作成した管理ユーザーでログインします。Laravel Passport ではユーザーごとのアクセストークンが発行できるので、それを作成。（トークン取得画面のUIなどは適宜作成してください。Laravel側でも用意されています。）",[125,830],{":src":831,":width":283,":center":284},"'_mix\u002Fapis-768x841.png'",[13,833,834],{},"アクセストークンを発行して手元にメモしておきます。「Talend API Tester」というChrome拡張機能を使ってこのHerokuアプリ宛にAPIを投げてみます。",[13,836,837],{},"アプリのドメイン名が表示されているのでコピー。外部からのAPIアクセスの際にはAPIキーをリクエストヘッダーに仕込んで送ると受け取る設定になっています。まずはAPIキーなして投げると、",[125,839],{":src":840,":width":128},"'_mix\u002Fapi_test_01-1-768x330.png'",[13,842,843,844,847],{},"おっ！401 Unauthorized が戻ってきました。 ",[20,845,846],{},"とりあえず指定のAPIルーティングは存在して、リクエストが受け取れています。つまりAPIサーバーとして機能していることがわかります。"," ではヘッダに先ほどのキーを仕込んで、また諸所の設定値も入れて再度投げると、",[125,849],{":src":850,":width":128},"'_mix\u002Fapitest_02-768x438.png'",[13,852,853],{},"200 OK が返りました。プレビューを見るとJSONでのデータが戻ってきているので成功です。これでHerokuにLaravelで開発したAPIサーバー及び管理アプリを構築することができました。このサーバーに認証キー付きAPIを投げることでデータを取得できます。",[51,855,857],{"id":856},"終了","終了〜〜〜",[13,859,860],{},"以上がherokuにlaravel で作成したAPIサービスを構築する手順でした。めちゃくちゃ長い記事になりましたが、VPSならもっと長くなっています笑。githubやSSL化そして他の管理を気にせず簡単にデプロイできるのでherokuが人気なのもわかります。Xserverとかは異なってスケーラブルでnodeとかpythonとかも入れられ、githubで管理できるのは大きいです。",[862,863,864],"style",{},"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 pre.shiki code .sx098, html code.shiki .sx098{--shiki-default:#F78C6C}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 .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":371,"searchDepth":399,"depth":399,"links":866},[867,871,872],{"id":27,"depth":392,"text":28,"children":868},[869,870],{"id":53,"depth":399,"text":54},{"id":82,"depth":399,"text":83},{"id":119,"depth":392,"text":120},{"id":174,"depth":392,"text":175,"children":873},[874,875,878,879,880,881,884,885,886,887,891,892,893,894,895],{"id":178,"depth":399,"text":179},{"id":209,"depth":399,"text":210,"children":876},[877],{"id":224,"depth":404,"text":225},{"id":262,"depth":399,"text":263},{"id":290,"depth":399,"text":291},{"id":337,"depth":399,"text":338},{"id":353,"depth":399,"text":354,"children":882},[883],{"id":360,"depth":404,"text":361},{"id":616,"depth":399,"text":617},{"id":632,"depth":399,"text":633},{"id":656,"depth":399,"text":657},{"id":679,"depth":399,"text":679,"children":888},[889,890],{"id":682,"depth":404,"text":683},{"id":694,"depth":404,"text":694},{"id":748,"depth":399,"text":749},{"id":781,"depth":399,"text":782},{"id":805,"depth":399,"text":806},{"id":815,"depth":399,"text":816},{"id":856,"depth":399,"text":857},[897],"devstack","2020-07-07","md",null,{},true,"\u002Farticles\u002Flalavel7-deplay-on-heroku",{"title":8,"description":8},"articles\u002Flalavel7-deplay-on-heroku",[382,907,908],"laravel","infrastructure","_mix\u002Fheroku_archtecture-768x446.jpg","i3Z8K7cn-JUTupKxFl5lgfnHLhl9EAJPK97f9yd7CX0",{"id":912,"title":913,"body":914,"category":1283,"createdAt":1285,"description":913,"extension":899,"index":900,"meta":1286,"navigation":902,"path":1287,"publish":902,"seo":1288,"series":900,"seriesTitle":900,"stem":1289,"tag":1290,"thumbnail":900,"updatedAt":900,"__hash__":1293},"articles\u002Farticles\u002Fjquery-anker.md","jqueryを用いてページの特定箇所までアニメーションスクロールさせる方法",{"type":10,"value":915,"toc":1277},[916,919,922,925,1234,1245,1248,1251,1255,1258,1261,1264,1268,1271,1274],[13,917,918],{},"こんにちはjunです。どちらかというとメモ的な内容になります。このサイトの「目次」にも実装されているような、見出し名をクリックするとそのコンテンツが書いてある場所まで飛ぶ動きを実装させます。",[25,920,921],{"id":921},"自動スクロールの原理",[13,923,924],{},"この自動スクロールの原理について解説します。",[366,926,930],{"className":927,"code":928,"language":929,"meta":371,"style":371},"language-javascript shiki shiki-themes material-theme-ocean","$('a[href^=#]').on('click',function() {\n    \u002F\u002F スクロールの速度\n    var speed = 400; \u002F\u002F ミリ秒\n\n    \u002F\u002F アンカーの値取得（aタグのhref属性）\n    var href= $(this).attr(\"href\");\n\n    \u002F\u002F 移動先の要素を取得\n    var target = $(href == \"#\" || href == \"\" ? 'html' : href);\n\n    \u002F\u002F 移動先の高さを取得\n    var position = target.offset().top;\n\n    \u002F\u002F スムーススクロール\n    $('body,html').animate({scrollTop:position}, speed, 'swing');\n});\n","javascript",[343,931,932,977,983,1002,1007,1012,1054,1059,1065,1122,1127,1133,1159,1164,1170,1224],{"__ignoreMap":371},[375,933,934,937,939,942,945,947,949,952,955,957,959,962,964,967,971,974],{"class":377,"line":378},[375,935,936],{"class":395},"$",[375,938,497],{"class":465},[375,940,941],{"class":444},"'",[375,943,944],{"class":385},"a[href^=#]",[375,946,941],{"class":444},[375,948,507],{"class":465},[375,950,951],{"class":444},".",[375,953,954],{"class":395},"on",[375,956,497],{"class":465},[375,958,941],{"class":444},[375,960,961],{"class":385},"click",[375,963,941],{"class":444},[375,965,966],{"class":444},",",[375,968,970],{"class":969},"sJ14y","function",[375,972,973],{"class":444},"()",[375,975,976],{"class":444}," {\n",[375,978,979],{"class":377,"line":392},[375,980,982],{"class":981},"sC9rS","    \u002F\u002F スクロールの速度\n",[375,984,985,988,991,994,997,999],{"class":377,"line":399},[375,986,987],{"class":969},"    var",[375,989,990],{"class":465}," speed",[375,992,993],{"class":444}," =",[375,995,996],{"class":425}," 400",[375,998,445],{"class":444},[375,1000,1001],{"class":981}," \u002F\u002F ミリ秒\n",[375,1003,1004],{"class":377,"line":404},[375,1005,1006],{"emptyLinePlaceholder":902},"\n",[375,1008,1009],{"class":377,"line":513},[375,1010,1011],{"class":981},"    \u002F\u002F アンカーの値取得（aタグのhref属性）\n",[375,1013,1015,1017,1020,1023,1026,1029,1032,1034,1036,1039,1041,1044,1047,1049,1051],{"class":377,"line":1014},6,[375,1016,987],{"class":969},[375,1018,1019],{"class":465}," href",[375,1021,1022],{"class":444},"=",[375,1024,1025],{"class":395}," $",[375,1027,497],{"class":1028},"s-wAU",[375,1030,1031],{"class":444},"this",[375,1033,507],{"class":1028},[375,1035,951],{"class":444},[375,1037,1038],{"class":395},"attr",[375,1040,497],{"class":1028},[375,1042,1043],{"class":444},"\"",[375,1045,1046],{"class":385},"href",[375,1048,1043],{"class":444},[375,1050,507],{"class":1028},[375,1052,1053],{"class":444},";\n",[375,1055,1057],{"class":377,"line":1056},7,[375,1058,1006],{"emptyLinePlaceholder":902},[375,1060,1062],{"class":377,"line":1061},8,[375,1063,1064],{"class":981},"    \u002F\u002F 移動先の要素を取得\n",[375,1066,1068,1070,1073,1075,1077,1079,1081,1084,1087,1090,1092,1095,1097,1099,1102,1105,1108,1111,1113,1116,1118,1120],{"class":377,"line":1067},9,[375,1069,987],{"class":969},[375,1071,1072],{"class":465}," target",[375,1074,993],{"class":444},[375,1076,1025],{"class":395},[375,1078,497],{"class":1028},[375,1080,1046],{"class":465},[375,1082,1083],{"class":444}," ==",[375,1085,1086],{"class":444}," \"",[375,1088,1089],{"class":385},"#",[375,1091,1043],{"class":444},[375,1093,1094],{"class":444}," ||",[375,1096,1019],{"class":465},[375,1098,1083],{"class":444},[375,1100,1101],{"class":444}," \"\"",[375,1103,1104],{"class":444}," ?",[375,1106,1107],{"class":444}," '",[375,1109,1110],{"class":385},"html",[375,1112,941],{"class":444},[375,1114,1115],{"class":444}," :",[375,1117,1019],{"class":465},[375,1119,507],{"class":1028},[375,1121,1053],{"class":444},[375,1123,1125],{"class":377,"line":1124},10,[375,1126,1006],{"emptyLinePlaceholder":902},[375,1128,1130],{"class":377,"line":1129},11,[375,1131,1132],{"class":981},"    \u002F\u002F 移動先の高さを取得\n",[375,1134,1136,1138,1141,1143,1145,1147,1150,1152,1154,1157],{"class":377,"line":1135},12,[375,1137,987],{"class":969},[375,1139,1140],{"class":465}," position",[375,1142,993],{"class":444},[375,1144,1072],{"class":465},[375,1146,951],{"class":444},[375,1148,1149],{"class":395},"offset",[375,1151,973],{"class":1028},[375,1153,951],{"class":444},[375,1155,1156],{"class":465},"top",[375,1158,1053],{"class":444},[375,1160,1162],{"class":377,"line":1161},13,[375,1163,1006],{"emptyLinePlaceholder":902},[375,1165,1167],{"class":377,"line":1166},14,[375,1168,1169],{"class":981},"    \u002F\u002F スムーススクロール\n",[375,1171,1173,1176,1178,1180,1183,1185,1187,1189,1192,1194,1197,1200,1203,1206,1209,1211,1213,1215,1218,1220,1222],{"class":377,"line":1172},15,[375,1174,1175],{"class":395},"    $",[375,1177,497],{"class":1028},[375,1179,941],{"class":444},[375,1181,1182],{"class":385},"body,html",[375,1184,941],{"class":444},[375,1186,507],{"class":1028},[375,1188,951],{"class":444},[375,1190,1191],{"class":395},"animate",[375,1193,497],{"class":1028},[375,1195,1196],{"class":444},"{",[375,1198,1199],{"class":1028},"scrollTop",[375,1201,1202],{"class":444},":",[375,1204,1205],{"class":465},"position",[375,1207,1208],{"class":444},"},",[375,1210,990],{"class":465},[375,1212,966],{"class":444},[375,1214,1107],{"class":444},[375,1216,1217],{"class":385},"swing",[375,1219,941],{"class":444},[375,1221,507],{"class":1028},[375,1223,1053],{"class":444},[375,1225,1227,1230,1232],{"class":377,"line":1226},16,[375,1228,1229],{"class":444},"}",[375,1231,507],{"class":465},[375,1233,1053],{"class":444},[13,1235,1236,1237,1240,1241,1244],{},"aタグのhref属性の文字列",[343,1238,1239],{},"[href=”#hoge”]","と移動先の要素にID",[343,1242,1243],{},"\u003Cdiv id=”hoge”>\u003C\u002Fdiv>","とさせることでこのスクロールができます。",[13,1246,1247],{},"しかし、原理的に必要なものは",[13,1249,1250],{},"どの場所に飛ばすかの情報を持ったトリガー（今回のaタグ）\n自身の場所を示すページ内で一意のIDをもつクラスまたはID\nがあれば可能です。しかし後述する理由よりできたらページスイングはaタグで行い、IDで区別する方法が中規模以上のサイトでは便利です。",[25,1252,1254],{"id":1253},"url-そのページ内のdom-idを参照している","URL + # =そのページ内のDOM IDを参照している",[13,1256,1257],{},"今回のスクロールではトリガーとなるaタグをクリックすると、urlに「#hoge」というのが付きます。実はurlに#がつくと、",[13,1259,1260],{},"そのurlつまり、そのページ内におけるDOMと#以降の文字列に合致する要素に飛ばしてくれます。実はJSがなくても自動スクロースができます。ただしアニメーションもなく画面が一瞬で切り替わるので、味気っぽなさはあります。",[13,1262,1263],{},"js(jquery)ではアニメーションを実装したいので書いただけです。",[51,1265,1267],{"id":1266},"つきで行うメリット","#つきで行うメリット",[13,1269,1270],{},"#を用いて移動先の要素を取得してアニメーションを実装すると、「url+#」でアクセスしたときにもアニメーションスクロールが効くように改変が可能になります。",[13,1272,1273],{},"今回はその実装はありませんが、Webにそのわっている機能を下地にスクリプトを書いた方が、独自のスクリプトより拡張性が高くなります。",[862,1275,1276],{},"html pre.shiki code .sdLwU, html code.shiki .sdLwU{--shiki-default:#82AAFF}html pre.shiki code .s0W1g, html code.shiki .s0W1g{--shiki-default:#BABED8}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 pre.shiki code .sJ14y, html code.shiki .sJ14y{--shiki-default:#C792EA}html pre.shiki code .sC9rS, html code.shiki .sC9rS{--shiki-default:#464B5D;--shiki-default-font-style:italic}html pre.shiki code .sx098, html code.shiki .sx098{--shiki-default:#F78C6C}html pre.shiki code .s-wAU, html code.shiki .s-wAU{--shiki-default:#F07178}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":371,"searchDepth":399,"depth":399,"links":1278},[1279,1280],{"id":921,"depth":392,"text":921},{"id":1253,"depth":392,"text":1254,"children":1281},[1282],{"id":1266,"depth":399,"text":1267},[1284],"ministack","2020-05-01",{},"\u002Farticles\u002Fjquery-anker",{"title":913,"description":913},"articles\u002Fjquery-anker",[1291,1292],"js","jquery","m8IR5HZJkkmOljGoU2QAnguTcrNVhjIf3JLMRS3OI-s",{"id":1295,"title":1296,"body":1297,"category":1982,"createdAt":1983,"description":1296,"extension":899,"index":900,"meta":1984,"navigation":902,"path":1985,"publish":902,"seo":1986,"series":900,"seriesTitle":900,"stem":1987,"tag":1988,"thumbnail":1989,"updatedAt":900,"__hash__":1990},"articles\u002Farticles\u002Fpseudo-element-icon.md","動的な変更に強い！擬似要素でWebアイコンを表示させる。(IE対策も)",{"type":10,"value":1298,"toc":1973},[1299,1302,1305,1339,1342,1345,1348,1351,1355,1358,1362,1365,1373,1376,1382,1385,1389,1395,1398,1567,1758,1761,1764,1770,1777,1780,1783,1789,1792,1796,1802,1863,1866,1943,1946,1958,1961,1964,1967,1970],[13,1300,1301],{},"こんにちはjunです。Webアイコンっていいですよね。サイトはお洒落になるし、各ボタンの意図やUX\u002FUIの観点では重要な物です。フォントを実装するといば、FontAwsome、GoogleIconを使うのがメインだと思います。中には画像でやる人もいるけどね。",[13,1303,1304],{},"ただアイコンというのは単独ではなくて、文字と併用したり、要素の一部としてひっそりと使うことが多いです。これらのアイコンを入れるときは",[366,1306,1310],{"className":1307,"code":1308,"language":1309,"meta":371,"style":371},"language-HTML shiki shiki-themes material-theme-ocean","\u003Ci class=\"far fa-smile-wink\">\u003C\u002Fi>\n","HTML",[343,1311,1312],{"__ignoreMap":371},[375,1313,1314,1317,1319,1322,1324,1326,1329,1331,1334,1336],{"class":377,"line":378},[375,1315,1316],{"class":444},"\u003C",[375,1318,104],{"class":1028},[375,1320,1321],{"class":969}," class",[375,1323,1022],{"class":444},[375,1325,1043],{"class":444},[375,1327,1328],{"class":385},"far fa-smile-wink",[375,1330,1043],{"class":444},[375,1332,1333],{"class":444},">\u003C\u002F",[375,1335,104],{"class":1028},[375,1337,1338],{"class":444},">\n",[13,1340,1341],{},"と言ったiタグを入れるのが簡単ですが実務で扱うようなサイトの場合、この方法では面倒になることがあります。",[25,1343,1344],{"id":1344},"タグの操作が必要",[13,1346,1347],{},"iタグと対応するアイコンのクラス名を入れることで、アイコンは表示することはできます。しかしタグでアイコンを打ち込むと、対応するアイコンごとのHTMLタグを埋め込む、必要性が出てきてしまいます。",[13,1349,1350],{},"特に動的に内容を出力したり変更したりする場合は特に大変です。例えば下図のようなリストタグの要件があるとします。",[125,1352],{":src":1353,":width":1354},"'_mix\u002Fpseudo-element-icon.png'","'300px'",[13,1356,1357],{},"胡散臭い内容ですが、とりあえずUX的にもこんなリストデザインが必要になったとしましょう。",[51,1359,1361],{"id":1360},"iタグでやってみると","iタグでやってみると...",[13,1363,1364],{},"今回の要件ではページの至る箇所で使われることとします。するとリストタグとして使う性質上、",[59,1366,1367,1370],{},[62,1368,1369],{},"リストの数が決まっているわけではない。（可変的）",[62,1371,1372],{},"内容によって数が変わる。",[13,1374,1375],{},"という内容が可変的であるという考慮が必要です。そのためiタグというHTML自体を埋め込んで対処すると、上記のデザインが必要な箇所全てにiタグを入れる必要が出てきます。",[13,1377,1378,1379],{},"リストの数が少なければなんとか対処できますが、大量にあったり、リリース前段階に内容が変わるなんて普通に起きます。差し替えというのは間違えが起きやすいです。そのためiタグでアイコンを埋め込んで表示する方法は、",[20,1380,1381],{},"このような動的な変更に弱いです。",[13,1383,1384],{},"もしiタグ埋め込み方式をするとなれば予め、アイコンのiタグを入れてdisplayで操作したりとか、jsで気合でタグを埋め込むとかになりますが、どちらもいい手段とは言えません。",[25,1386,1388],{"id":1387},"擬似要素でアイコンをつけるクラス名で操作可能","擬似要素でアイコンをつける＝クラス名で操作可能",[13,1390,1391,1392],{},"iタグでアイコンを変える場合、要素であるiタグ、そしてクラス名が必要です。",[20,1393,1394],{},"ただし擬似要素を扱うことでクラス名を付け加える、書き換えるだけでアイコンの変更と、付与・削除ができるようになります。",[13,1396,1397],{},"擬似要素は特に「付与・削除できる」という点がiタグ埋め込みより優れています。上のリストタグのHTML\u002FCSSは以下のように書かれています。",[366,1399,1402],{"className":1400,"code":1401,"language":1110,"meta":371,"style":371},"language-html shiki shiki-themes material-theme-ocean","\u003Cul class=\"p-deco_list u-list_01\">\n    \u003Cli>最近よく眠れない..\u003C\u002Fli>\n    \u003Cli>なかなか疲れが取れない..\u003C\u002Fli>\n    \u003Cli>朝起きるのが辛い..\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cul class=\"p-deco_list u-list_02\">\n    \u003Cli>使い始めてから、寝つきがよくなった！\u003C\u002Fli>\n    \u003Cli>疲れが嘘のように取れた！\u003C\u002Fli>\n    \u003Cli>朝の目覚めが変わった！\u003C\u002Fli>\n\u003C\u002Ful>\n",[343,1403,1404,1423,1443,1460,1477,1485,1489,1508,1525,1542,1559],{"__ignoreMap":371},[375,1405,1406,1408,1410,1412,1414,1416,1419,1421],{"class":377,"line":378},[375,1407,1316],{"class":444},[375,1409,59],{"class":1028},[375,1411,1321],{"class":969},[375,1413,1022],{"class":444},[375,1415,1043],{"class":444},[375,1417,1418],{"class":385},"p-deco_list u-list_01",[375,1420,1043],{"class":444},[375,1422,1338],{"class":444},[375,1424,1425,1428,1430,1433,1436,1439,1441],{"class":377,"line":392},[375,1426,1427],{"class":444},"    \u003C",[375,1429,62],{"class":1028},[375,1431,1432],{"class":444},">",[375,1434,1435],{"class":465},"最近よく眠れない..",[375,1437,1438],{"class":444},"\u003C\u002F",[375,1440,62],{"class":1028},[375,1442,1338],{"class":444},[375,1444,1445,1447,1449,1451,1454,1456,1458],{"class":377,"line":399},[375,1446,1427],{"class":444},[375,1448,62],{"class":1028},[375,1450,1432],{"class":444},[375,1452,1453],{"class":465},"なかなか疲れが取れない..",[375,1455,1438],{"class":444},[375,1457,62],{"class":1028},[375,1459,1338],{"class":444},[375,1461,1462,1464,1466,1468,1471,1473,1475],{"class":377,"line":404},[375,1463,1427],{"class":444},[375,1465,62],{"class":1028},[375,1467,1432],{"class":444},[375,1469,1470],{"class":465},"朝起きるのが辛い..",[375,1472,1438],{"class":444},[375,1474,62],{"class":1028},[375,1476,1338],{"class":444},[375,1478,1479,1481,1483],{"class":377,"line":513},[375,1480,1438],{"class":444},[375,1482,59],{"class":1028},[375,1484,1338],{"class":444},[375,1486,1487],{"class":377,"line":1014},[375,1488,1006],{"emptyLinePlaceholder":902},[375,1490,1491,1493,1495,1497,1499,1501,1504,1506],{"class":377,"line":1056},[375,1492,1316],{"class":444},[375,1494,59],{"class":1028},[375,1496,1321],{"class":969},[375,1498,1022],{"class":444},[375,1500,1043],{"class":444},[375,1502,1503],{"class":385},"p-deco_list u-list_02",[375,1505,1043],{"class":444},[375,1507,1338],{"class":444},[375,1509,1510,1512,1514,1516,1519,1521,1523],{"class":377,"line":1061},[375,1511,1427],{"class":444},[375,1513,62],{"class":1028},[375,1515,1432],{"class":444},[375,1517,1518],{"class":465},"使い始めてから、寝つきがよくなった！",[375,1520,1438],{"class":444},[375,1522,62],{"class":1028},[375,1524,1338],{"class":444},[375,1526,1527,1529,1531,1533,1536,1538,1540],{"class":377,"line":1067},[375,1528,1427],{"class":444},[375,1530,62],{"class":1028},[375,1532,1432],{"class":444},[375,1534,1535],{"class":465},"疲れが嘘のように取れた！",[375,1537,1438],{"class":444},[375,1539,62],{"class":1028},[375,1541,1338],{"class":444},[375,1543,1544,1546,1548,1550,1553,1555,1557],{"class":377,"line":1124},[375,1545,1427],{"class":444},[375,1547,62],{"class":1028},[375,1549,1432],{"class":444},[375,1551,1552],{"class":465},"朝の目覚めが変わった！",[375,1554,1438],{"class":444},[375,1556,62],{"class":1028},[375,1558,1338],{"class":444},[375,1560,1561,1563,1565],{"class":377,"line":1129},[375,1562,1438],{"class":444},[375,1564,59],{"class":1028},[375,1566,1338],{"class":444},[366,1568,1572],{"className":1569,"code":1570,"language":1571,"meta":371,"style":371},"language-css shiki shiki-themes material-theme-ocean",".p-deco_list li{\n    list-style:none;\n}\n\n \u002F*　擬似要素アイコンの共通設定*\u002F\n.p-deco_list li::before{\n    content:'';\n    font-family: 'FontAwesome';\n    padding:5px 10px; \u002F* 文字間隔調整 *\u002F\n}\n\n.u-list_01 li::before{\n    content:'\\f119';\n}\n\n.u-list_02 li::before{\n    content:'\\f4da';\n}\n","css",[343,1573,1574,1587,1600,1605,1609,1614,1630,1642,1658,1676,1680,1684,1699,1714,1718,1722,1737,1753],{"__ignoreMap":371},[375,1575,1576,1578,1581,1584],{"class":377,"line":378},[375,1577,951],{"class":444},[375,1579,1580],{"class":381},"p-deco_list",[375,1582,1583],{"class":381}," li",[375,1585,1586],{"class":444},"{\n",[375,1588,1589,1593,1595,1598],{"class":377,"line":392},[375,1590,1592],{"class":1591},"s6YsC","    list-style",[375,1594,1202],{"class":444},[375,1596,1597],{"class":465},"none",[375,1599,1053],{"class":444},[375,1601,1602],{"class":377,"line":399},[375,1603,1604],{"class":444},"}\n",[375,1606,1607],{"class":377,"line":404},[375,1608,1006],{"emptyLinePlaceholder":902},[375,1610,1611],{"class":377,"line":513},[375,1612,1613],{"class":981}," \u002F*　擬似要素アイコンの共通設定*\u002F\n",[375,1615,1616,1618,1620,1622,1625,1628],{"class":377,"line":1014},[375,1617,951],{"class":444},[375,1619,1580],{"class":381},[375,1621,1583],{"class":381},[375,1623,1624],{"class":444},"::",[375,1626,1627],{"class":969},"before",[375,1629,1586],{"class":444},[375,1631,1632,1635,1637,1640],{"class":377,"line":1056},[375,1633,1634],{"class":1591},"    content",[375,1636,1202],{"class":444},[375,1638,1639],{"class":444},"''",[375,1641,1053],{"class":444},[375,1643,1644,1647,1649,1651,1654,1656],{"class":377,"line":1061},[375,1645,1646],{"class":1591},"    font-family",[375,1648,1202],{"class":444},[375,1650,1107],{"class":444},[375,1652,1653],{"class":385},"FontAwesome",[375,1655,941],{"class":444},[375,1657,1053],{"class":444},[375,1659,1660,1663,1665,1668,1671,1673],{"class":377,"line":1067},[375,1661,1662],{"class":1591},"    padding",[375,1664,1202],{"class":444},[375,1666,1667],{"class":425},"5px",[375,1669,1670],{"class":425}," 10px",[375,1672,445],{"class":444},[375,1674,1675],{"class":981}," \u002F* 文字間隔調整 *\u002F\n",[375,1677,1678],{"class":377,"line":1124},[375,1679,1604],{"class":444},[375,1681,1682],{"class":377,"line":1129},[375,1683,1006],{"emptyLinePlaceholder":902},[375,1685,1686,1688,1691,1693,1695,1697],{"class":377,"line":1135},[375,1687,951],{"class":444},[375,1689,1690],{"class":381},"u-list_01",[375,1692,1583],{"class":381},[375,1694,1624],{"class":444},[375,1696,1627],{"class":969},[375,1698,1586],{"class":444},[375,1700,1701,1703,1705,1707,1710,1712],{"class":377,"line":1161},[375,1702,1634],{"class":1591},[375,1704,1202],{"class":444},[375,1706,941],{"class":444},[375,1708,1709],{"class":465},"\\f119",[375,1711,941],{"class":444},[375,1713,1053],{"class":444},[375,1715,1716],{"class":377,"line":1166},[375,1717,1604],{"class":444},[375,1719,1720],{"class":377,"line":1172},[375,1721,1006],{"emptyLinePlaceholder":902},[375,1723,1724,1726,1729,1731,1733,1735],{"class":377,"line":1226},[375,1725,951],{"class":444},[375,1727,1728],{"class":381},"u-list_02",[375,1730,1583],{"class":381},[375,1732,1624],{"class":444},[375,1734,1627],{"class":969},[375,1736,1586],{"class":444},[375,1738,1740,1742,1744,1746,1749,1751],{"class":377,"line":1739},17,[375,1741,1634],{"class":1591},[375,1743,1202],{"class":444},[375,1745,941],{"class":444},[375,1747,1748],{"class":465},"\\f4da",[375,1750,941],{"class":444},[375,1752,1053],{"class":444},[375,1754,1756],{"class":377,"line":1755},18,[375,1757,1604],{"class":444},[13,1759,1760],{},"iタグを入れない代わりに、ulタグに擬似要素アイコンを付けるための設定（p-deco_list）とアイコンの種類を指定（u-list_*）しています。そしてCSSでクラス名配下のliに対して擬似要素を付けるようにしています。",[25,1762,1763],{"id":1763},"擬似要素でアイコンを表示するのは意外と簡単",[13,1765,1766,1767],{},"CSSを見ればわかりますが、意外と簡単です。fontawesome、GoogleIconは「フォントアイコン」とも呼ばれているように、",[20,1768,1769],{},"文字のように扱うことができます。",[13,1771,1772,1773,1776],{},"fontawesomeを使うならば、",[20,1774,1775],{},"font-familyに’FontAwesome’と入れればいいのです。"," するとCDNとかがちゃんと読み込まれていれば、fontawesomeのアイコンが使えるようになります。",[13,1778,1779],{},"そしてアイコンの種類はcontentで指定します。「\\f4da」みたいなのは文字コードです。この文字コードはfontawesomeで簡単に見つけられます。下図のfontawesomeのアイコン詳細画面を開くと、iタグの横に「f4da」とあります。",[125,1781],{":src":1782,":width":128},"'_mix\u002Ffontawesome-1024x296.png'",[13,1784,1785,1786],{},"この英数字とバックスラッシュ（\\）をCSSのcontentに入れればアイコンの指定が完了です。",[20,1787,1788],{},"バックスラッシュを入れないと、文字コードでなく「f4da」という文字列として認識されてしまいます。",[13,1790,1791],{},"「擬似要素！？iタグより難しいじゃん..」と思う方は、iタグで表示されたアイコンを開発者ツールでみてみましょう。実はiタグもこの方法と同じように、クラス名で文字コードを指定して、擬似要素内にアイコンを表示しているだけなんですよ。その方法をiタグ使わないでやっているだけです。",[25,1793,1795],{"id":1794},"ie野郎対策","IE野郎対策",[13,1797,1798,1799],{},"私もiタグがいらなくて、クラスを指定すればいいことにウキウキしながら実装を進めていくと問題にぶち当たりました。IEです。",[20,1800,1801],{},"実は下のコードだけではIEは表示されないという事件が発覚して、頭を悩ませていました。",[366,1803,1805],{"className":1569,"code":1804,"language":1571,"meta":371,"style":371},".p-deco_list li::before{\n    content:'';\n    font-family: 'FontAwesome';\n    padding:5px 10px; \u002F* 文字間隔調整 *\u002F\n}\n",[343,1806,1807,1821,1831,1845,1859],{"__ignoreMap":371},[375,1808,1809,1811,1813,1815,1817,1819],{"class":377,"line":378},[375,1810,951],{"class":444},[375,1812,1580],{"class":381},[375,1814,1583],{"class":381},[375,1816,1624],{"class":444},[375,1818,1627],{"class":969},[375,1820,1586],{"class":444},[375,1822,1823,1825,1827,1829],{"class":377,"line":392},[375,1824,1634],{"class":1591},[375,1826,1202],{"class":444},[375,1828,1639],{"class":444},[375,1830,1053],{"class":444},[375,1832,1833,1835,1837,1839,1841,1843],{"class":377,"line":399},[375,1834,1646],{"class":1591},[375,1836,1202],{"class":444},[375,1838,1107],{"class":444},[375,1840,1653],{"class":385},[375,1842,941],{"class":444},[375,1844,1053],{"class":444},[375,1846,1847,1849,1851,1853,1855,1857],{"class":377,"line":404},[375,1848,1662],{"class":1591},[375,1850,1202],{"class":444},[375,1852,1667],{"class":425},[375,1854,1670],{"class":425},[375,1856,445],{"class":444},[375,1858,1675],{"class":981},[375,1860,1861],{"class":377,"line":513},[375,1862,1604],{"class":444},[13,1864,1865],{},"chromeやFireFox（一応Edge）は表示されるんですが、IE11は表示されてませんでした。要件にはIE11が入っていたので実装は必須です。頭を悩ませてググっていると方法がありました。",[366,1867,1869],{"className":1569,"code":1868,"language":1571,"meta":371,"style":371},".p-deco_list li::before{\n    content:'';\n    font-family: 'FontAwesome';\n    font-feature-settings: 'liga';\n    padding:5px 10px; \u002F* 文字間隔調整 *\u002F\n}\n",[343,1870,1871,1885,1895,1909,1925,1939],{"__ignoreMap":371},[375,1872,1873,1875,1877,1879,1881,1883],{"class":377,"line":378},[375,1874,951],{"class":444},[375,1876,1580],{"class":381},[375,1878,1583],{"class":381},[375,1880,1624],{"class":444},[375,1882,1627],{"class":969},[375,1884,1586],{"class":444},[375,1886,1887,1889,1891,1893],{"class":377,"line":392},[375,1888,1634],{"class":1591},[375,1890,1202],{"class":444},[375,1892,1639],{"class":444},[375,1894,1053],{"class":444},[375,1896,1897,1899,1901,1903,1905,1907],{"class":377,"line":399},[375,1898,1646],{"class":1591},[375,1900,1202],{"class":444},[375,1902,1107],{"class":444},[375,1904,1653],{"class":385},[375,1906,941],{"class":444},[375,1908,1053],{"class":444},[375,1910,1911,1914,1916,1918,1921,1923],{"class":377,"line":404},[375,1912,1913],{"class":1591},"    font-feature-settings",[375,1915,1202],{"class":444},[375,1917,1107],{"class":444},[375,1919,1920],{"class":385},"liga",[375,1922,941],{"class":444},[375,1924,1053],{"class":444},[375,1926,1927,1929,1931,1933,1935,1937],{"class":377,"line":513},[375,1928,1662],{"class":1591},[375,1930,1202],{"class":444},[375,1932,1667],{"class":425},[375,1934,1670],{"class":425},[375,1936,445],{"class":444},[375,1938,1675],{"class":981},[375,1940,1941],{"class":377,"line":1014},[375,1942,1604],{"class":444},[13,1944,1945],{},"「font-feature-settings: ‘liga’;」というのを設定したらなんと！表示されました。このプロパティですがmdnでは",[94,1947,1949,1950,1953,1954],{"className":1948},[97,576],"\nCSS の ",[343,1951,1952],{},"font-feature-settings"," プロパティは、 OpenType フォントの拡張タイポグラフィの特性を制御します。\n",[104,1955,1956],{},[107,1957,109],{"href":109,"target":110},[13,1959,1960],{},"(；´Д`A？",[13,1962,1963],{},"全然分からん。とりあえずフォントアイコン＝フォントなので、そのアイコンフォントを表示させるための設定がIEではないのか？と結論づけました。フォントについて詳しい人教えてください。",[25,1965,1966],{"id":1966},"アイコンは基本的には擬似要素がいいかも",[13,1968,1969],{},"そんな感じで、擬似要素を用いてアイコンを付ける方法は以上です。今回の例で出したような、動的な内容にアイコンが伴う場合はクラス名で指定できる、擬似要素アイコンがiタグ埋め込みより優れています。意外とやっている原理は簡単なのでぜひ使えると便利ですよ。",[862,1971,1972],{},"html pre.shiki code .sAklC, html code.shiki .sAklC{--shiki-default:#89DDFF}html pre.shiki code .s-wAU, html code.shiki .s-wAU{--shiki-default:#F07178}html pre.shiki code .sJ14y, html code.shiki .sJ14y{--shiki-default:#C792EA}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 .s5Dmg, html code.shiki .s5Dmg{--shiki-default:#FFCB6B}html pre.shiki code .s6YsC, html code.shiki .s6YsC{--shiki-default:#B2CCD6}html pre.shiki code .sC9rS, html code.shiki .sC9rS{--shiki-default:#464B5D;--shiki-default-font-style:italic}html pre.shiki code .sx098, html code.shiki .sx098{--shiki-default:#F78C6C}",{"title":371,"searchDepth":399,"depth":399,"links":1974},[1975,1978,1979,1980,1981],{"id":1344,"depth":392,"text":1344,"children":1976},[1977],{"id":1360,"depth":399,"text":1361},{"id":1387,"depth":392,"text":1388},{"id":1763,"depth":392,"text":1763},{"id":1794,"depth":392,"text":1795},{"id":1966,"depth":392,"text":1966},[897],"2020-04-26",{},"\u002Farticles\u002Fpseudo-element-icon",{"title":1296,"description":1296},"articles\u002Fpseudo-element-icon",[1110,1571],"_mix\u002Fpseudo-element-icon.png","vqsAO5STJjp70MfR8z6uFmS_BPvxDfVteLbqAiJvnO0",1780987151812]