[{"data":1,"prerenderedAt":2439},["ShallowReactive",2],{"series-vue_laravel_app-1":3},{"doc":4,"prev":2437,"next":2437},{"id":5,"title":6,"body":7,"category":2424,"createdAt":2426,"description":2427,"extension":2428,"index":236,"meta":2429,"navigation":260,"path":2430,"publish":260,"seo":2431,"series":2432,"seriesTitle":2433,"stem":2434,"tag":2435,"thumbnail":2436,"updatedAt":2437,"__hash__":2438},"series\u002Fseries\u002Fvue-laravel-app-1.md","Vue SPA x Laravelでつくる実務パチモンアプリその１：アプリの概要と環境構築",{"type":8,"value":9,"toc":2400},"minimark",[10,14,33,45,48,68,71,75,78,82,85,89,120,123,160,163,174,177,180,185,188,191,202,216,223,302,316,321,325,328,382,385,388,391,409,467,474,478,481,840,857,866,924,927,935,949,958,964,979,982,985,989,996,1002,1005,1009,1012,1016,1022,1045,1051,1183,1194,1197,1222,1229,1235,1239,1246,1252,1255,1316,1319,1458,1465,1468,1593,1676,1679,2014,2021,2028,2031,2050,2225,2234,2246,2283,2286,2325,2331,2344,2355,2358,2371,2374,2383,2386,2389,2393,2396],[11,12,13],"p",{},"こんにちはjunです。仕事で中規模プロジェクトのディレクターとしてアサインされて、半年ほどブログを書けませんでした。ようやく最近時間が出てきたので新しいシリーズ記事を作成しようと思いました。とにかく最近１年はシステムばっかり作っていました。それぞのれプロジェクトでは",[15,16,17,21,24,27,30],"ul",{},[18,19,20],"li",{},"CRUDなAPI",[18,22,23],{},"管理画面の構築",[18,25,26],{},"ユーザー権限によるアクセス制限",[18,28,29],{},"ログイン・ログアウト",[18,31,32],{},"ジョブ・キューを用いた長い処理",[11,34,35,36,40,41,44],{},"など使用するシステムの構築を多く行い、いろいろ学ぶことできました。その中で「CRUDなAPI」を考えた「管理画面」の構築をすることが多くありました。バックエンドは",[37,38,39],"code",{},"Laravel","を、フロントエンドは",[37,42,43],{},"Vue.js","を使用していました。Vue.jsのおかげで高速にリッチなUIを構築し、システムを開発することができました。",[11,46,47],{},"この知見を共有し、まとめるためにも今回は",[15,49,50,53,56,59,62,65],{},[18,51,52],{},"管理画面はVue.js SPA",[18,54,55],{},"web APIによるCRUD操作",[18,57,58],{},"ルーティング設計",[18,60,61],{},"バリデーション",[18,63,64],{},"MVCによるアプリケーション設計",[18,66,67],{},"laravel\u002Fpermissionを用いたユーザー権限",[11,69,70],{},"以上の様な機能を持ったデモアプリを作ろうと思います。Laravelを用いたシステム開発と、Vue.jsによる管理画面フロントエンド開発を解説していきます。",[72,73,74],"h2",{"id":74},"作るアプリの概要",[11,76,77],{},"今回のアプリはn○teみたいなブログサービスを作ります。CRUDや管理画面の構築をメインに行いたいのでシンプルにいきます。",[79,80,81],"h3",{"id":81},"機能概要",[11,83,84],{},"ざっくりとした機能概要は以下のとおりです。",[86,87,88],"h4",{"id":88},"ユーザー系の特徴",[15,90,91,94,97,100,103,106],{},[18,92,93],{},"利用する際にはユーザーとしてアカウントを作る（今回は全てゲストとする）。",[18,95,96],{},"ログインはアドレスとパスワードを使用する。",[18,98,99],{},"アプリを利用する一般ユーザーと管理を行う管理ユーザーが存在する。",[18,101,102],{},"管理ユーザーは特定の管理用ルートから入る。",[18,104,105],{},"退会可能。退会時は関連するデータは削除される。",[18,107,108,109],{},"プロフィールでは以下の項目を持つ\n",[15,110,111,114,117],{},[18,112,113],{},"ユーザー名（仮名）",[18,115,116],{},"プロフィール文",[18,118,119],{},"アバター写真",[86,121,122],{"id":122},"ブログ機能",[15,124,125,128,145,148,151,154,157],{},[18,126,127],{},"ブログを作成することができ、公開することができる。",[18,129,130,131],{},"ブログは以下の項目を持つ\n",[15,132,133,136,139,142],{},[18,134,135],{},"タイトル",[18,137,138],{},"内容（リッチテキスト）",[18,140,141],{},"タグ（任意）",[18,143,144],{},"公開日時",[18,146,147],{},"下書きとして保存することができる。更新時も下書き可能。",[18,149,150],{},"任意数のタグを添付できる。タグはユーザーが新しく作成できる。",[18,152,153],{},"削除可能",[18,155,156],{},"画像の添付が可能。",[18,158,159],{},"アップロードした画像は管理できる。",[86,161,162],{"id":162},"一覧検索機能",[15,164,165,168,171],{},[18,166,167],{},"サイトトップは投稿された記事が新着順に並ぶ。",[18,169,170],{},"任意のタグを選んで一覧表示することが可能。",[18,172,173],{},"部分一致検索も一応可能とする。",[79,175,176],{"id":176},"アプリケーションアーキテクチャー",[11,178,179],{},"バックエンドにはLaravel9、管理画面はVue.js(2系)を使用して構築します。なおVue.jsはSPA構成とします。記事の一覧や詳細画面などの公開側はLaravelのレンダリングで表示します。",[181,182],"image-render",{":src":183,":center":184},"'vue_laravel_app\u002Farchitecture.jpg'","true",[11,186,187],{},"デザインに関してはセンスが皆無なので管理画面・公開側ともにBootstrapを使用します。",[72,189,190],{"id":190},"環境構築",[11,192,193,194,201],{},"ではまずは環境構築を行っていきましょう。環境構築にはDockerを使用してLAMP環境を作ります。",[195,196,200],"a",{"href":197,"rel":198},"https:\u002F\u002Fwww.twilio.com\u002Fblog\u002Fget-started-docker-laravel-jp",[199],"nofollow","こちらの記事","を参考にし、",[15,203,204,207,210,213],{},[18,205,206],{},"mysql5.7",[18,208,209],{},"phpmyadmin",[18,211,212],{},"apache2.4",[18,214,215],{},"php8.1",[11,217,218,219,222],{},"以上の構成を作成したいと思います。ディレクトリを作成して",[37,220,221],{},"docker-compose.yaml","を作成します。",[224,225,230],"pre",{"className":226,"code":227,"language":228,"meta":229,"style":229},"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","",[37,231,232,245,255,262,271,279,287,294],{"__ignoreMap":229},[233,234,237,241],"span",{"class":235,"line":236},"line",1,[233,238,240],{"class":239},"s5Dmg","mkdir",[233,242,244],{"class":243},"sfyAc"," ~\u002Flaravel_vue\n",[233,246,248,252],{"class":235,"line":247},2,[233,249,251],{"class":250},"sdLwU","cd",[233,253,254],{"class":243},"  ~\u002Flaravel_vue\n",[233,256,258],{"class":235,"line":257},3,[233,259,261],{"emptyLinePlaceholder":260},true,"\n",[233,263,265,268],{"class":235,"line":264},4,[233,266,267],{"class":239},"touch",[233,269,270],{"class":243}," docker-compose.yaml\n",[233,272,274,276],{"class":235,"line":273},5,[233,275,240],{"class":239},[233,277,278],{"class":243}," html\n",[233,280,282,284],{"class":235,"line":281},6,[233,283,240],{"class":239},[233,285,286],{"class":243}," web_1\n",[233,288,290,292],{"class":235,"line":289},7,[233,291,251],{"class":250},[233,293,286],{"class":243},[233,295,297,299],{"class":235,"line":296},8,[233,298,267],{"class":239},[233,300,301],{"class":243}," Dockerfile\n",[11,303,304,307,308,311,312,315],{},[37,305,306],{},"html\u002F","という名前で作成したディレクトリにLaravelのソースが作成され、Dockerコンテナにマウントされる様にします。",[37,309,310],{},"web_1\u002F","ディレクトリにはphpとapacheが構築できる",[37,313,314],{},"Dockerfile","を作成してビルドします。",[11,317,318,320],{},[37,319,221],{},"でDBとapache+phpと連結したいと思います。",[79,322,324],{"id":323},"dockerfile設定","Dockerfile設定",[11,326,327],{},"まずはapacheとphpのイメージをDockerfileを作成します。",[224,329,334],{"className":330,"code":331,"filename":332,"language":333,"meta":229,"style":229},"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",[37,335,336,341,346,351,356,361,366,371,376],{"__ignoreMap":229},[233,337,338],{"class":235,"line":236},[233,339,340],{},"FROM php:8.1-apache\n",[233,342,343],{"class":235,"line":247},[233,344,345],{},"RUN apt update \\\n",[233,347,348],{"class":235,"line":257},[233,349,350],{},"        && apt install -y g++ libicu-dev libpq-dev libzip-dev zip zlib1g-dev \\\n",[233,352,353],{"class":235,"line":264},[233,354,355],{},"        && mv \u002Fetc\u002Fapache2\u002Fmods-available\u002Frewrite.load \u002Fetc\u002Fapache2\u002Fmods-enabled\n",[233,357,358],{"class":235,"line":273},[233,359,360],{},"RUN docker-php-ext-install pdo pdo_mysql\n",[233,362,363],{"class":235,"line":281},[233,364,365],{},"WORKDIR \u002Fvar\u002Fwww\u002Fhtml\n",[233,367,368],{"class":235,"line":289},[233,369,370],{},"RUN curl -sS https:\u002F\u002Fgetcomposer.org\u002Finstaller | php -- --install-dir=\u002Fusr\u002Flocal\u002Fbin --filename=composer\n",[233,372,373],{"class":235,"line":296},[233,374,375],{},"RUN curl -fsSL https:\u002F\u002Fdeb.nodesource.com\u002Fsetup_lts.x | bash - \\\n",[233,377,379],{"class":235,"line":378},9,[233,380,381],{},"    && apt-get install -y nodejs\n",[11,383,384],{},"このファイルではphpとapacheが入った環境にてLaravelの依存PHPモジュールのインストールとcomposerをインストールする内容を記述しています。そしてLaravelのアセットビルドにはnode.jsを使用するのでそのインストールも記述しています。",[79,386,387],{"id":387},"apacheの設定ファイルを作成",[11,389,390],{},"Laravelのドキュメントルートを設定するためにapacheの設定ファイル作成して、マウントできる様にします。",[224,392,394],{"className":226,"code":393,"language":228,"meta":229,"style":229},"cd web_1\ntouch default.conf\n",[37,395,396,402],{"__ignoreMap":229},[233,397,398,400],{"class":235,"line":236},[233,399,251],{"class":250},[233,401,286],{"class":243},[233,403,404,406],{"class":235,"line":247},[233,405,267],{"class":239},[233,407,408],{"class":243}," default.conf\n",[224,410,415],{"className":411,"code":412,"filename":413,"language":414,"meta":229,"style":229},"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",[37,416,417,422,427,432,436,441,446,451,456,461],{"__ignoreMap":229},[233,418,419],{"class":235,"line":236},[233,420,421],{},"\u003CVirtualHost *:80>\n",[233,423,424],{"class":235,"line":247},[233,425,426],{},"    ServerName laravel_docker\n",[233,428,429],{"class":235,"line":257},[233,430,431],{},"    DocumentRoot \u002Fvar\u002Fwww\u002Fhtml\u002Fpublic\n",[233,433,434],{"class":235,"line":264},[233,435,261],{"emptyLinePlaceholder":260},[233,437,438],{"class":235,"line":273},[233,439,440],{},"    \u003CDirectory \"\u002Fvar\u002Fwww\u002Fhtml\u002Fpublic\">\n",[233,442,443],{"class":235,"line":281},[233,444,445],{},"        AllowOverride All\n",[233,447,448],{"class":235,"line":289},[233,449,450],{},"    \u003C\u002FDirectory>\n",[233,452,453],{"class":235,"line":296},[233,454,455],{},"    ErrorLog ${APACHE_LOG_DIR}\u002Ferror.log\n",[233,457,458],{"class":235,"line":378},[233,459,460],{},"    CustomLog ${APACHE_LOG_DIR}\u002Faccess.log combined\n",[233,462,464],{"class":235,"line":463},10,[233,465,466],{},"\u003C\u002FVirtualHost>\n",[11,468,469,470,473],{},"このファイルはコンテナの",[37,471,472],{},"\u002Fetc\u002Fapache2\u002Fsites-enabled\u002F000-default.conf","にマウントされます。",[79,475,477],{"id":476},"docker-composeyamlを作成","docker-compose.yamlを作成",[11,479,480],{},"次にdocker-comose.yamlを作成してDBとphpmyadmin",[224,482,487],{"className":483,"code":484,"filename":485,"language":486,"meta":229,"style":229},"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",[37,488,489,508,519,527,537,546,554,563,570,577,586,600,608,619,630,637,649,657,666,674,682,690,698,708,719,726,737,748,759,770,781,790,802,811,819,829],{"__ignoreMap":229},[233,490,491,495,499,502,505],{"class":235,"line":236},[233,492,494],{"class":493},"s-wAU","version",[233,496,498],{"class":497},"sAklC",":",[233,500,501],{"class":497}," '",[233,503,504],{"class":243},"3",[233,506,507],{"class":497},"'\n",[233,509,510,513,515],{"class":235,"line":247},[233,511,512],{"class":493},"services",[233,514,498],{"class":497},[233,516,518],{"class":517},"s0W1g"," \n",[233,520,521,524],{"class":235,"line":257},[233,522,523],{"class":493},"  web_1",[233,525,526],{"class":497},":\n",[233,528,529,532,534],{"class":235,"line":264},[233,530,531],{"class":493},"    build",[233,533,498],{"class":497},[233,535,536],{"class":243}," .\u002Fweb_1\n",[233,538,539,542,544],{"class":235,"line":273},[233,540,541],{"class":493},"    depends_on",[233,543,498],{"class":497},[233,545,518],{"class":517},[233,547,548,551],{"class":235,"line":281},[233,549,550],{"class":497},"      -",[233,552,553],{"class":243}," db\n",[233,555,556,559,561],{"class":235,"line":289},[233,557,558],{"class":493},"    volumes",[233,560,498],{"class":497},[233,562,518],{"class":517},[233,564,565,567],{"class":235,"line":296},[233,566,550],{"class":497},[233,568,569],{"class":243}," .\u002Fhtml\u002F:\u002Fvar\u002Fwww\u002Fhtml\u002F\n",[233,571,572,574],{"class":235,"line":378},[233,573,550],{"class":497},[233,575,576],{"class":243}," .\u002Fweb_1\u002Fdefault.conf:\u002Fetc\u002Fapache2\u002Fsites-enabled\u002F000-default.conf\n",[233,578,579,582,584],{"class":235,"line":463},[233,580,581],{"class":493},"    ports",[233,583,498],{"class":497},[233,585,518],{"class":517},[233,587,589,591,594,597],{"class":235,"line":588},11,[233,590,550],{"class":497},[233,592,593],{"class":497}," \"",[233,595,596],{"class":243},"8005:80",[233,598,599],{"class":497},"\"\n",[233,601,603,606],{"class":235,"line":602},12,[233,604,605],{"class":493},"  phpmyadmin",[233,607,526],{"class":497},[233,609,611,614,616],{"class":235,"line":610},13,[233,612,613],{"class":493},"    image",[233,615,498],{"class":497},[233,617,618],{"class":243}," phpmyadmin\n",[233,620,622,625,627],{"class":235,"line":621},14,[233,623,624],{"class":493},"    restart",[233,626,498],{"class":497},[233,628,629],{"class":243}," always\n",[233,631,633,635],{"class":235,"line":632},15,[233,634,581],{"class":493},[233,636,526],{"class":497},[233,638,640,642,644,647],{"class":235,"line":639},16,[233,641,550],{"class":497},[233,643,593],{"class":497},[233,645,646],{"class":243},"8080:80",[233,648,599],{"class":497},[233,650,652,655],{"class":235,"line":651},17,[233,653,654],{"class":493},"    environment",[233,656,526],{"class":497},[233,658,660,663],{"class":235,"line":659},18,[233,661,662],{"class":497},"     -",[233,664,665],{"class":243}," PMA_ARBITRARY=1\n",[233,667,669,671],{"class":235,"line":668},19,[233,670,662],{"class":497},[233,672,673],{"class":243}," PMA_HOST=db:3306\n",[233,675,677,679],{"class":235,"line":676},20,[233,678,662],{"class":497},[233,680,681],{"class":243}," PMA_USER=root\n",[233,683,685,687],{"class":235,"line":684},21,[233,686,662],{"class":497},[233,688,689],{"class":243}," PMA_PASSWORD=rootroot\n",[233,691,693,696],{"class":235,"line":692},22,[233,694,695],{"class":493},"  db",[233,697,526],{"class":497},[233,699,701,703,705],{"class":235,"line":700},23,[233,702,613],{"class":493},[233,704,498],{"class":497},[233,706,707],{"class":243}," mysql:5.7\n",[233,709,711,714,716],{"class":235,"line":710},24,[233,712,713],{"class":493},"    command",[233,715,498],{"class":497},[233,717,718],{"class":243}," mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci\n",[233,720,722,724],{"class":235,"line":721},25,[233,723,654],{"class":493},[233,725,526],{"class":497},[233,727,729,732,734],{"class":235,"line":728},26,[233,730,731],{"class":493},"      MYSQL_DATABASE",[233,733,498],{"class":497},[233,735,736],{"class":243}," preform\n",[233,738,740,743,745],{"class":235,"line":739},27,[233,741,742],{"class":493},"      MYSQL_USER",[233,744,498],{"class":497},[233,746,747],{"class":243}," test\n",[233,749,751,754,756],{"class":235,"line":750},28,[233,752,753],{"class":493},"      MYSQL_PASSWORD",[233,755,498],{"class":497},[233,757,758],{"class":243}," testtest\n",[233,760,762,765,767],{"class":235,"line":761},29,[233,763,764],{"class":493},"      MYSQL_ROOT_PASSWORD",[233,766,498],{"class":497},[233,768,769],{"class":243}," rootroot\n",[233,771,773,776,778],{"class":235,"line":772},30,[233,774,775],{"class":493},"      TZ",[233,777,498],{"class":497},[233,779,780],{"class":243}," Asia\u002FTokyo\n",[233,782,784,786,788],{"class":235,"line":783},31,[233,785,581],{"class":493},[233,787,498],{"class":497},[233,789,518],{"class":517},[233,791,793,795,797,800],{"class":235,"line":792},32,[233,794,550],{"class":497},[233,796,593],{"class":497},[233,798,799],{"class":243},"3306:3306",[233,801,599],{"class":497},[233,803,805,807,809],{"class":235,"line":804},33,[233,806,558],{"class":493},[233,808,498],{"class":497},[233,810,518],{"class":517},[233,812,814,816],{"class":235,"line":813},34,[233,815,550],{"class":497},[233,817,818],{"class":243}," vue_laravel:\u002Fvar\u002Flib\u002Fmysql\n",[233,820,822,825,827],{"class":235,"line":821},35,[233,823,824],{"class":493},"volumes",[233,826,498],{"class":497},[233,828,518],{"class":517},[233,830,832,835,837],{"class":235,"line":831},36,[233,833,834],{"class":493},"  vue_laravel",[233,836,498],{"class":497},[233,838,839],{"class":497}," {}\n",[11,841,842,845,846,848,849,852,853,856],{},[37,843,844],{},"web_1","サービスでapacheとphpの環境をビルドします。そして",[37,847,306],{},"をコンテナ内の",[37,850,851],{},"\u002Fvar\u002Fwww\u002Fhtml\u002F","にマウントされます。ubuntuのapacheは",[37,854,855],{},"\u002Fetc\u002Fapache2\u002Fsites-enabled\u002F","配下にconfファイルを設置することで設定を追加したりオーバーライドできます。",[11,858,859,861,862,865],{},[37,860,209],{},"サービスでphpmyadminを起動してDBの内容を編集しやすくします。",[37,863,864],{},"db","サービスではmysqlの環境を作成します。ボリュームの設定をして永続化を行います。以上の設定を行ったらコンテナを立ち上げます。",[224,867,869],{"className":226,"code":868,"language":228,"meta":229,"style":229},"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",[37,870,871,882,886,900,912],{"__ignoreMap":229},[233,872,873,876,879],{"class":235,"line":236},[233,874,875],{"class":239},"docker-compose",[233,877,878],{"class":243}," up",[233,880,881],{"class":243}," -d\n",[233,883,884],{"class":235,"line":247},[233,885,261],{"emptyLinePlaceholder":260},[233,887,888,891,894,897],{"class":235,"line":257},[233,889,890],{"class":239},"Creating",[233,892,893],{"class":243}," laravel_vue_phpmyadmin_1",[233,895,896],{"class":243}," ...",[233,898,899],{"class":243}," done\n",[233,901,902,904,907,910],{"class":235,"line":264},[233,903,890],{"class":239},[233,905,906],{"class":243}," laravel_vue_db_1",[233,908,909],{"class":243},"         ...",[233,911,899],{"class":243},[233,913,914,916,919,922],{"class":235,"line":273},[233,915,890],{"class":239},[233,917,918],{"class":243}," laravel_vue_web_1_1",[233,920,921],{"class":243},"      ...",[233,923,899],{"class":243},[11,925,926],{},"そしてコンテナ内に入ってLaravelをインストールしていきます。",[224,928,933],{"className":929,"code":931,"language":932},[930],"language-text","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","text",[37,934,931],{"__ignoreMap":229},[11,936,937,938,941,942,945,946,948],{},"これで",[37,939,940],{},"\u002Fvar\u002Fwww\u002Fhtml","配下にLaravelプロジェクトが入ります。コンテナの",[37,943,944],{}," \u002Fvar\u002Fwww\u002Fhtml","はホストマシンの",[37,947,306],{},"に同期されます。",[11,950,951,953,954,957],{},[37,952,306],{},"配下にソースが生成されたのを確認しましたら、",[37,955,956],{},".env","ファイルにDBの接続設定を記述します。",[224,959,962],{"className":960,"code":961,"filename":956,"language":932,"meta":229},[930],"DB_CONNECTION=mysql\nDB_HOST=db\nDB_PORT=3306\nDB_DATABASE=laravel\nDB_USERNAME=root\nDB_PASSWORD=rootroot\n",[37,963,961],{"__ignoreMap":229},[11,965,966,967,970,971,974,975,978],{},"phpmyadminとかで",[37,968,969],{},"laravel","データベースは作成しておいてください。",[37,972,973],{},"DB_HOST","はdockerのDBのサービス名を入力します。設定したらブラウザで",[37,976,977],{},"localhost:8005","にアクセスします。以下の様なウェルカムページが表示されれば成功です。",[181,980],{":src":981,":center":184},"'vue_laravel_app\u002Flaravel_welcome.png'",[11,983,984],{},"（特に断りがない限り、以降ではDockerコンテナ内の操作とします。）",[72,986,988],{"id":987},"laravelとvueセットアップ","LaravelとVueセットアップ",[11,990,991,992,995],{},"それではアセットと認証機能のセットアップをしましょう。今回は",[37,993,994],{},"laravel\u002Fbreeze","をスターターキットとして利用します。",[224,997,1000],{"className":998,"code":999,"language":932},[930],"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",[37,1001,999],{"__ignoreMap":229},[11,1003,1004],{},"マイグレーションが完了したので登録などができる様になっているはずです。ひとまずLaravelのセットアップは以上となります。",[72,1006,1008],{"id":1007},"とりあえずspa構成ができるかチェック","とりあえずSPA構成ができるかチェック",[11,1010,1011],{},"次は管理画面のフロントエンドのセットアップをしていきます。管理画面はvue.jsで作成し、公開側コンテンツはLaravelのサーバーサイドレンダリングをします。ここではSPA構成のセットアップをします。",[79,1013,1015],{"id":1014},"spaのビューとルートの設定","SPAのビューとルートの設定",[11,1017,1018,1021],{},[37,1019,1020],{},"\u002Fsystem","配下を管理画面として構築していきます。以下の様なビューテンプレートとルートを作成します。",[224,1023,1028],{"className":1024,"code":1025,"filename":1026,"language":1027,"meta":229,"style":229},"language-php shiki shiki-themes material-theme-ocean","Route::get('\u002Fsystem\u002F{path?}', function () {\n    return view('admin');\n})->where('path', '.*')->name('admin');\n","routes\u002Fweb.php","php",[37,1029,1030,1035,1040],{"__ignoreMap":229},[233,1031,1032],{"class":235,"line":236},[233,1033,1034],{},"Route::get('\u002Fsystem\u002F{path?}', function () {\n",[233,1036,1037],{"class":235,"line":247},[233,1038,1039],{},"    return view('admin');\n",[233,1041,1042],{"class":235,"line":257},[233,1043,1044],{},"})->where('path', '.*')->name('admin');\n",[11,1046,1047,1048,1050],{},"このルートは",[37,1049,1020],{},"配下すべてのルートに対してadminビューを返すことを示しています。",[224,1052,1055],{"className":1024,"code":1053,"filename":1054,"language":1027,"meta":229,"style":229},"\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",[37,1056,1057,1062,1067,1072,1077,1082,1087,1091,1096,1100,1105,1110,1114,1119,1124,1128,1133,1138,1143,1148,1153,1158,1163,1168,1173,1178],{"__ignoreMap":229},[233,1058,1059],{"class":235,"line":236},[233,1060,1061],{},"\u003C!DOCTYPE html>\n",[233,1063,1064],{"class":235,"line":247},[233,1065,1066],{},"\u003Chtml lang=\"{{ str_replace('_', '-', app()->getLocale()) }}\">\n",[233,1068,1069],{"class":235,"line":257},[233,1070,1071],{},"    \u003Chead>\n",[233,1073,1074],{"class":235,"line":264},[233,1075,1076],{},"        \u003Cmeta charset=\"utf-8\">\n",[233,1078,1079],{"class":235,"line":273},[233,1080,1081],{},"        \u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n",[233,1083,1084],{"class":235,"line":281},[233,1085,1086],{},"        \u003Cmeta name=\"csrf-token\" content=\"{{ csrf_token() }}\">\n",[233,1088,1089],{"class":235,"line":289},[233,1090,261],{"emptyLinePlaceholder":260},[233,1092,1093],{"class":235,"line":296},[233,1094,1095],{},"        \u003Ctitle>{{ config('app.name', 'Laravel') }}\u003C\u002Ftitle>\n",[233,1097,1098],{"class":235,"line":378},[233,1099,261],{"emptyLinePlaceholder":260},[233,1101,1102],{"class":235,"line":463},[233,1103,1104],{},"        \u003C!-- Fonts -->\n",[233,1106,1107],{"class":235,"line":588},[233,1108,1109],{},"        \u003Clink rel=\"stylesheet\" href=\"https:\u002F\u002Ffonts.googleapis.com\u002Fcss2?family=Nunito:wght@400;600;700&display=swap\">\n",[233,1111,1112],{"class":235,"line":602},[233,1113,261],{"emptyLinePlaceholder":260},[233,1115,1116],{"class":235,"line":610},[233,1117,1118],{},"        \u003C!-- Styles -->\n",[233,1120,1121],{"class":235,"line":621},[233,1122,1123],{},"        \u003Clink rel=\"stylesheet\" href=\"{{ asset('css\u002Fapp.css') }}\">\n",[233,1125,1126],{"class":235,"line":632},[233,1127,261],{"emptyLinePlaceholder":260},[233,1129,1130],{"class":235,"line":639},[233,1131,1132],{},"    \u003C\u002Fhead>\n",[233,1134,1135],{"class":235,"line":651},[233,1136,1137],{},"    \u003Cbody>\n",[233,1139,1140],{"class":235,"line":659},[233,1141,1142],{},"        \u003Cdiv class=\"font-sans text-gray-900 antialiased\">\n",[233,1144,1145],{"class":235,"line":668},[233,1146,1147],{},"            \u003Cdiv id=\"app\">\n",[233,1149,1150],{"class":235,"line":676},[233,1151,1152],{},"                \u003Crouter-view\u002F>\n",[233,1154,1155],{"class":235,"line":684},[233,1156,1157],{},"            \u003C\u002Fdiv>\n",[233,1159,1160],{"class":235,"line":692},[233,1161,1162],{},"        \u003C\u002Fdiv>\n",[233,1164,1165],{"class":235,"line":700},[233,1166,1167],{},"    \u003C\u002Fbody>\n",[233,1169,1170],{"class":235,"line":710},[233,1171,1172],{},"    \u003C!-- Scripts -->\n",[233,1174,1175],{"class":235,"line":721},[233,1176,1177],{},"    \u003Cscript src=\"{{ asset('js\u002Fadmin\u002Findex.js') }}\" defer>\u003C\u002Fscript>\n",[233,1179,1180],{"class":235,"line":728},[233,1181,1182],{},"\u003C\u002Fhtml>\n",[11,1184,1185,1186,1189,1190,1193],{},"テンプレートは上記の様にして",[37,1187,1188],{},"\u003Cdiv id=\"app\">\u003Crouter-view\u002F>\u003C\u002Fdiv>","を設置して、vueのエントリーポイントと、vueのビルドファイルを読み込む様にします。実際はヘッダーなどを分けたほうがいいですが、今はとりあえずこんな感じでOKです。\n次に",[37,1191,1192],{},"'js\u002Fadmin\u002Findex.js","で読み込ませるjsファイルを作成していきます。",[79,1195,1196],{"id":1196},"必要なライブラリをインストール",[11,1198,1199,1200,1203,1204,1207,1208,1211,1212,1214,1215,1207,1218,1221],{},"一昔前の",[37,1201,1202],{},"laravel\u002Fui","の時は",[37,1205,1206],{},"vue","と",[37,1209,1210],{},"bootstrap","の設定がインストール時に自動設定されますが、今回の",[37,1213,994],{},"は",[37,1216,1217],{},"alpinejs",[37,1219,1220],{},"tailwindcss","が入っているので、vue一式を自分でインストールする必要があります。まずはVue・Vuex・Vue-Routerをインストールしましょう。",[1223,1224,1228],"div",{"className":1225},[1226,1227],"alert","alert-danger","\nこの記事を作成した2021年3月現在、Vue3がリリースされておりバージョンを指定しないと、それぞれ最新版がインストールされる可能性があります。この記事はVue2を用いて解説しますのでバージョンを指定してインストールしています。\n",[224,1230,1233],{"className":1231,"code":1232,"language":932},[930],"npm install vue@2 vue-loader@15 vue-template-compiler@2 --save-dev\nnpm install vuex@3 vue-router@3\n",[37,1234,1232],{"__ignoreMap":229},[79,1236,1238],{"id":1237},"spa用のjsファイルを作成する","SPA用のJSファイルを作成する",[11,1240,1241,1242,1245],{},"それでは",[37,1243,1244],{},"resources\u002Fjs","配下にSPAのファイルを作成を行います。以下の様にファイルとディレクトリを作成してください。",[224,1247,1250],{"className":1248,"code":1249,"language":932},[930],"js\n├── admin\n│   ├── index.js\n│   ├── router.js\n│   └── store.js\n└── vue\n    └── page\n        ├── Home.vue\n        └── Profile.vue\n",[37,1251,1249],{"__ignoreMap":229},[11,1253,1254],{},"store.jsとrouter.jsは以下の様にします。",[224,1256,1261],{"className":1257,"code":1258,"filename":1259,"language":1260,"meta":229,"style":229},"language-js shiki shiki-themes material-theme-ocean","export default {\n    state:{},\n    getters: {},\n    mutations: {},\n    actions: {}\n}\n","store.js","js",[37,1262,1263,1275,1283,1293,1302,1311],{"__ignoreMap":229},[233,1264,1265,1269,1272],{"class":235,"line":236},[233,1266,1268],{"class":1267},"s6cf3","export",[233,1270,1271],{"class":1267}," default",[233,1273,1274],{"class":497}," {\n",[233,1276,1277,1280],{"class":235,"line":247},[233,1278,1279],{"class":493},"    state",[233,1281,1282],{"class":497},":{},\n",[233,1284,1285,1288,1290],{"class":235,"line":257},[233,1286,1287],{"class":493},"    getters",[233,1289,498],{"class":497},[233,1291,1292],{"class":497}," {},\n",[233,1294,1295,1298,1300],{"class":235,"line":264},[233,1296,1297],{"class":493},"    mutations",[233,1299,498],{"class":497},[233,1301,1292],{"class":497},[233,1303,1304,1307,1309],{"class":235,"line":273},[233,1305,1306],{"class":493},"    actions",[233,1308,498],{"class":497},[233,1310,839],{"class":497},[233,1312,1313],{"class":235,"line":281},[233,1314,1315],{"class":497},"}\n",[11,1317,1318],{},"storeはひとまず必要な型だけ準備します。",[224,1320,1323],{"className":1257,"code":1321,"filename":1322,"language":1260,"meta":229,"style":229},"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",[37,1324,1325,1347,1365,1369,1384,1413,1438,1443,1447],{"__ignoreMap":229},[233,1326,1327,1330,1333,1336,1338,1341,1344],{"class":235,"line":236},[233,1328,1329],{"class":1267},"import",[233,1331,1332],{"class":517}," Home ",[233,1334,1335],{"class":1267},"from",[233,1337,501],{"class":497},[233,1339,1340],{"class":243},"~js\u002Fvue\u002Fpage\u002FHome.vue",[233,1342,1343],{"class":497},"'",[233,1345,1346],{"class":497},";\n",[233,1348,1349,1351,1354,1356,1358,1361,1363],{"class":235,"line":247},[233,1350,1329],{"class":1267},[233,1352,1353],{"class":517}," Profile ",[233,1355,1335],{"class":1267},[233,1357,501],{"class":497},[233,1359,1360],{"class":243},"~js\u002Fvue\u002Fpage\u002FProfile.vue",[233,1362,1343],{"class":497},[233,1364,1346],{"class":497},[233,1366,1367],{"class":235,"line":257},[233,1368,261],{"emptyLinePlaceholder":260},[233,1370,1371,1375,1378,1381],{"class":235,"line":264},[233,1372,1374],{"class":1373},"sJ14y","const",[233,1376,1377],{"class":517}," routes ",[233,1379,1380],{"class":497},"=",[233,1382,1383],{"class":517}," [\n",[233,1385,1386,1389,1392,1394,1396,1398,1400,1403,1406,1408,1410],{"class":235,"line":273},[233,1387,1388],{"class":497},"    {",[233,1390,1391],{"class":493}," path",[233,1393,498],{"class":497},[233,1395,501],{"class":497},[233,1397,1020],{"class":243},[233,1399,1343],{"class":497},[233,1401,1402],{"class":497},",",[233,1404,1405],{"class":493}," component",[233,1407,498],{"class":497},[233,1409,1332],{"class":517},[233,1411,1412],{"class":497},"},\n",[233,1414,1415,1417,1419,1421,1423,1426,1428,1430,1432,1434,1436],{"class":235,"line":281},[233,1416,1388],{"class":497},[233,1418,1391],{"class":493},[233,1420,498],{"class":497},[233,1422,501],{"class":497},[233,1424,1425],{"class":243},"\u002Fsystem\u002Fprofile",[233,1427,1343],{"class":497},[233,1429,1402],{"class":497},[233,1431,1405],{"class":493},[233,1433,498],{"class":497},[233,1435,1353],{"class":517},[233,1437,1412],{"class":497},[233,1439,1440],{"class":235,"line":289},[233,1441,1442],{"class":517},"]\n",[233,1444,1445],{"class":235,"line":296},[233,1446,261],{"emptyLinePlaceholder":260},[233,1448,1449,1451,1453,1456],{"class":235,"line":378},[233,1450,1268],{"class":1267},[233,1452,1271],{"class":1267},[233,1454,1455],{"class":517}," routes",[233,1457,1346],{"class":497},[11,1459,1460,1461,1464],{},"router.jsでは指定のパスに対してどのコンポーネントを出すかを定義します。",[37,1462,1463],{},"~js","という記述は後で解説しますので、ひとまず記述してください。",[11,1466,1467],{},"router.jsで参照しているコンポーネントは以下の様にしておきます。",[224,1469,1473],{"className":1470,"code":1471,"filename":1472,"language":1206,"meta":229,"style":229},"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",[37,1474,1475,1486,1495,1500,1533,1542,1550,1559,1567,1581,1585],{"__ignoreMap":229},[233,1476,1477,1480,1483],{"class":235,"line":236},[233,1478,1479],{"class":497},"\u003C",[233,1481,1482],{"class":493},"template",[233,1484,1485],{"class":497},">\n",[233,1487,1488,1491,1493],{"class":235,"line":247},[233,1489,1490],{"class":497},"    \u003C",[233,1492,1223],{"class":493},[233,1494,1485],{"class":497},[233,1496,1497],{"class":235,"line":257},[233,1498,1499],{"class":517},"        home\n",[233,1501,1502,1505,1508,1511,1513,1516,1518,1520,1523,1526,1529,1531],{"class":235,"line":264},[233,1503,1504],{"class":497},"        \u003C",[233,1506,1507],{"class":493},"router-link",[233,1509,1510],{"class":1373}," to",[233,1512,1380],{"class":497},[233,1514,1515],{"class":497},"\"",[233,1517,1425],{"class":243},[233,1519,1515],{"class":497},[233,1521,1522],{"class":497},">",[233,1524,1525],{"class":517},"profile",[233,1527,1528],{"class":497},"\u003C\u002F",[233,1530,1507],{"class":493},[233,1532,1485],{"class":497},[233,1534,1535,1538,1540],{"class":235,"line":273},[233,1536,1537],{"class":497},"    \u003C\u002F",[233,1539,1223],{"class":493},[233,1541,1485],{"class":497},[233,1543,1544,1546,1548],{"class":235,"line":281},[233,1545,1528],{"class":497},[233,1547,1482],{"class":493},[233,1549,1485],{"class":497},[233,1551,1552,1554,1557],{"class":235,"line":289},[233,1553,1479],{"class":497},[233,1555,1556],{"class":493},"script",[233,1558,1485],{"class":497},[233,1560,1561,1563,1565],{"class":235,"line":296},[233,1562,1268],{"class":1267},[233,1564,1271],{"class":1267},[233,1566,1274],{"class":497},[233,1568,1569,1572,1574,1576,1579],{"class":235,"line":378},[233,1570,1571],{"class":493},"    name",[233,1573,498],{"class":497},[233,1575,1343],{"class":497},[233,1577,1578],{"class":243},"home",[233,1580,507],{"class":497},[233,1582,1583],{"class":235,"line":463},[233,1584,1315],{"class":497},[233,1586,1587,1589,1591],{"class":235,"line":588},[233,1588,1528],{"class":497},[233,1590,1556],{"class":493},[233,1592,1485],{"class":497},[224,1594,1597],{"className":1470,"code":1595,"filename":1596,"language":1206,"meta":229,"style":229},"\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",[37,1598,1599,1607,1615,1620,1628,1636,1644,1652,1664,1668],{"__ignoreMap":229},[233,1600,1601,1603,1605],{"class":235,"line":236},[233,1602,1479],{"class":497},[233,1604,1482],{"class":493},[233,1606,1485],{"class":497},[233,1608,1609,1611,1613],{"class":235,"line":247},[233,1610,1490],{"class":497},[233,1612,1223],{"class":493},[233,1614,1485],{"class":497},[233,1616,1617],{"class":235,"line":257},[233,1618,1619],{"class":517},"        profile\n",[233,1621,1622,1624,1626],{"class":235,"line":264},[233,1623,1537],{"class":497},[233,1625,1223],{"class":493},[233,1627,1485],{"class":497},[233,1629,1630,1632,1634],{"class":235,"line":273},[233,1631,1528],{"class":497},[233,1633,1482],{"class":493},[233,1635,1485],{"class":497},[233,1637,1638,1640,1642],{"class":235,"line":281},[233,1639,1479],{"class":497},[233,1641,1556],{"class":493},[233,1643,1485],{"class":497},[233,1645,1646,1648,1650],{"class":235,"line":289},[233,1647,1268],{"class":1267},[233,1649,1271],{"class":1267},[233,1651,1274],{"class":497},[233,1653,1654,1656,1658,1660,1662],{"class":235,"line":296},[233,1655,1571],{"class":493},[233,1657,498],{"class":497},[233,1659,1343],{"class":497},[233,1661,1525],{"class":243},[233,1663,507],{"class":497},[233,1665,1666],{"class":235,"line":378},[233,1667,1315],{"class":497},[233,1669,1670,1672,1674],{"class":235,"line":463},[233,1671,1528],{"class":497},[233,1673,1556],{"class":493},[233,1675,1485],{"class":497},[11,1677,1678],{},"index.jsは以下の様に記述します。",[224,1680,1683],{"className":1257,"code":1681,"filename":1682,"language":1260,"meta":229,"style":229},"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",[37,1684,1685,1716,1760,1764,1781,1785,1803,1821,1836,1861,1865,1881,1898,1909,1952,1956,1975,1983,1990],{"__ignoreMap":229},[233,1686,1687,1690,1693,1696,1698,1701,1704,1706,1709,1711,1714],{"class":235,"line":236},[233,1688,1689],{"class":517},"window",[233,1691,1692],{"class":497},".",[233,1694,1695],{"class":517},"axios ",[233,1697,1380],{"class":497},[233,1699,1700],{"class":250}," require",[233,1702,1703],{"class":517},"(",[233,1705,1343],{"class":497},[233,1707,1708],{"class":243},"axios",[233,1710,1343],{"class":497},[233,1712,1713],{"class":517},")",[233,1715,1346],{"class":497},[233,1717,1718,1720,1722,1724,1726,1729,1731,1734,1736,1739,1741,1744,1746,1749,1751,1753,1756,1758],{"class":235,"line":247},[233,1719,1689],{"class":517},[233,1721,1692],{"class":497},[233,1723,1708],{"class":517},[233,1725,1692],{"class":497},[233,1727,1728],{"class":517},"defaults",[233,1730,1692],{"class":497},[233,1732,1733],{"class":517},"headers",[233,1735,1692],{"class":497},[233,1737,1738],{"class":517},"common[",[233,1740,1343],{"class":497},[233,1742,1743],{"class":243},"X-Requested-With",[233,1745,1343],{"class":497},[233,1747,1748],{"class":517},"] ",[233,1750,1380],{"class":497},[233,1752,501],{"class":497},[233,1754,1755],{"class":243},"XMLHttpRequest",[233,1757,1343],{"class":497},[233,1759,1346],{"class":497},[233,1761,1762],{"class":235,"line":257},[233,1763,261],{"emptyLinePlaceholder":260},[233,1765,1766,1768,1771,1773,1775,1777,1779],{"class":235,"line":264},[233,1767,1329],{"class":1267},[233,1769,1770],{"class":517}," Vue ",[233,1772,1335],{"class":1267},[233,1774,501],{"class":497},[233,1776,1206],{"class":243},[233,1778,1343],{"class":497},[233,1780,1346],{"class":497},[233,1782,1783],{"class":235,"line":273},[233,1784,261],{"emptyLinePlaceholder":260},[233,1786,1787,1789,1792,1794,1796,1799,1801],{"class":235,"line":281},[233,1788,1329],{"class":1267},[233,1790,1791],{"class":517}," Vuex ",[233,1793,1335],{"class":1267},[233,1795,501],{"class":497},[233,1797,1798],{"class":243},"vuex",[233,1800,1343],{"class":497},[233,1802,1346],{"class":497},[233,1804,1805,1807,1810,1812,1814,1817,1819],{"class":235,"line":289},[233,1806,1329],{"class":1267},[233,1808,1809],{"class":517}," index ",[233,1811,1335],{"class":1267},[233,1813,501],{"class":497},[233,1815,1816],{"class":243},".\u002Fstore",[233,1818,1343],{"class":497},[233,1820,1346],{"class":497},[233,1822,1823,1826,1828,1831,1834],{"class":235,"line":296},[233,1824,1825],{"class":517},"Vue",[233,1827,1692],{"class":497},[233,1829,1830],{"class":250},"use",[233,1832,1833],{"class":517},"(Vuex)",[233,1835,1346],{"class":497},[233,1837,1838,1840,1843,1845,1848,1851,1853,1856,1859],{"class":235,"line":378},[233,1839,1374],{"class":1373},[233,1841,1842],{"class":517}," store ",[233,1844,1380],{"class":497},[233,1846,1847],{"class":497}," new",[233,1849,1850],{"class":517}," Vuex",[233,1852,1692],{"class":497},[233,1854,1855],{"class":250},"Store",[233,1857,1858],{"class":517},"(index)",[233,1860,1346],{"class":497},[233,1862,1863],{"class":235,"line":463},[233,1864,261],{"emptyLinePlaceholder":260},[233,1866,1867,1869,1872,1874,1876,1879],{"class":235,"line":588},[233,1868,1329],{"class":1267},[233,1870,1871],{"class":517}," VueRouter ",[233,1873,1335],{"class":1267},[233,1875,501],{"class":497},[233,1877,1878],{"class":243},"vue-router",[233,1880,507],{"class":497},[233,1882,1883,1885,1887,1889,1891,1894,1896],{"class":235,"line":602},[233,1884,1329],{"class":1267},[233,1886,1377],{"class":517},[233,1888,1335],{"class":1267},[233,1890,501],{"class":497},[233,1892,1893],{"class":243},".\u002Frouter",[233,1895,1343],{"class":497},[233,1897,1346],{"class":497},[233,1899,1900,1902,1904,1906],{"class":235,"line":610},[233,1901,1825],{"class":517},[233,1903,1692],{"class":497},[233,1905,1830],{"class":250},[233,1907,1908],{"class":517},"(VueRouter)\n",[233,1910,1911,1913,1916,1918,1921,1924,1926,1929,1932,1934,1936,1939,1941,1943,1946,1949],{"class":235,"line":621},[233,1912,1374],{"class":1373},[233,1914,1915],{"class":517}," router ",[233,1917,1380],{"class":497},[233,1919,1920],{"class":497},"  new",[233,1922,1923],{"class":250}," VueRouter",[233,1925,1703],{"class":517},[233,1927,1928],{"class":497},"{",[233,1930,1931],{"class":493},"mode",[233,1933,498],{"class":497},[233,1935,501],{"class":497},[233,1937,1938],{"class":243},"history",[233,1940,1343],{"class":497},[233,1942,1402],{"class":497},[233,1944,1945],{"class":517},"routes",[233,1947,1948],{"class":497},"}",[233,1950,1951],{"class":517},")\n",[233,1953,1954],{"class":235,"line":632},[233,1955,261],{"emptyLinePlaceholder":260},[233,1957,1958,1960,1963,1965,1967,1970,1972],{"class":235,"line":639},[233,1959,1374],{"class":1373},[233,1961,1962],{"class":517}," app ",[233,1964,1380],{"class":497},[233,1966,1847],{"class":497},[233,1968,1969],{"class":250}," Vue",[233,1971,1703],{"class":517},[233,1973,1974],{"class":497},"{\n",[233,1976,1977,1980],{"class":235,"line":651},[233,1978,1979],{"class":517},"    store",[233,1981,1982],{"class":497},",\n",[233,1984,1985,1988],{"class":235,"line":659},[233,1986,1987],{"class":517},"    router",[233,1989,1982],{"class":497},[233,1991,1992,1994,1996,1998,2001,2003,2005,2008,2010,2012],{"class":235,"line":668},[233,1993,1948],{"class":497},[233,1995,1713],{"class":517},[233,1997,1692],{"class":497},[233,1999,2000],{"class":250},"$mount",[233,2002,1703],{"class":517},[233,2004,1515],{"class":497},[233,2006,2007],{"class":243},"#app",[233,2009,1515],{"class":497},[233,2011,1713],{"class":517},[233,2013,1346],{"class":497},[11,2015,2016,2017,2020],{},"axiosはLaravelインストール時に入っているのでそのまま利用しています。このファイルではVuexとVue-routerの連携を行い、最終的に",[37,2018,2019],{},"\u003Cdiv id=\"app\">\u003C\u002Fdiv>","にレンダリングされる様にします。",[11,2022,2023,2024,2027],{},"上記のファイルを作成した後、",[37,2025,2026],{},"webpac.mix.js","を修正します。",[79,2029,2030],{"id":2030},"mixファイルの作成",[11,2032,2033,2034,2037,2038,2041,2042,2045,2046,2049],{},"このようなVueのプロジェクトwebpackを使用しますがLaravelは簡単な記述で",[37,2035,2036],{},"resources","配下を簡単に",[37,2039,2040],{},"public\u002F","配下に出力してくれます。またvueやsassのコンパイル設定を",[37,2043,2044],{},"webpack.config.js","の様に書く必要がありません。どのファイルをコンパイル対象にするかなどは",[37,2047,2048],{},"webpack.mix.js","に記載します。以下の様に修正します。",[224,2051,2053],{"className":1257,"code":2052,"filename":2044,"language":1260,"meta":229,"style":229},"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",[37,2054,2055,2079,2103,2107,2121,2130,2139,2170,2175,2180,2188],{"__ignoreMap":229},[233,2056,2057,2059,2062,2064,2066,2068,2070,2073,2075,2077],{"class":235,"line":236},[233,2058,1374],{"class":1373},[233,2060,2061],{"class":517}," mix ",[233,2063,1380],{"class":497},[233,2065,1700],{"class":250},[233,2067,1703],{"class":517},[233,2069,1343],{"class":497},[233,2071,2072],{"class":243},"laravel-mix",[233,2074,1343],{"class":497},[233,2076,1713],{"class":517},[233,2078,1346],{"class":497},[233,2080,2081,2083,2086,2088,2090,2092,2094,2097,2099,2101],{"class":235,"line":247},[233,2082,1374],{"class":1373},[233,2084,2085],{"class":517}," path ",[233,2087,1380],{"class":497},[233,2089,1700],{"class":250},[233,2091,1703],{"class":517},[233,2093,1343],{"class":497},[233,2095,2096],{"class":243},"path",[233,2098,1343],{"class":497},[233,2100,1713],{"class":517},[233,2102,1346],{"class":497},[233,2104,2105],{"class":235,"line":257},[233,2106,261],{"emptyLinePlaceholder":260},[233,2108,2109,2112,2114,2117,2119],{"class":235,"line":264},[233,2110,2111],{"class":517},"mix",[233,2113,1692],{"class":497},[233,2115,2116],{"class":250},"webpackConfig",[233,2118,1703],{"class":517},[233,2120,1974],{"class":497},[233,2122,2123,2126,2128],{"class":235,"line":273},[233,2124,2125],{"class":493},"    resolve",[233,2127,498],{"class":497},[233,2129,1274],{"class":497},[233,2131,2132,2135,2137],{"class":235,"line":281},[233,2133,2134],{"class":493},"      alias",[233,2136,498],{"class":497},[233,2138,1274],{"class":497},[233,2140,2141,2144,2146,2148,2150,2152,2154,2157,2159,2161,2164,2166,2168],{"class":235,"line":289},[233,2142,2143],{"class":497},"        '",[233,2145,1463],{"class":493},[233,2147,1343],{"class":497},[233,2149,498],{"class":497},[233,2151,1391],{"class":517},[233,2153,1692],{"class":497},[233,2155,2156],{"class":250},"resolve",[233,2158,1703],{"class":517},[233,2160,1343],{"class":497},[233,2162,2163],{"class":243},".\u002Fresources\u002Fjs",[233,2165,1343],{"class":497},[233,2167,1713],{"class":517},[233,2169,1982],{"class":497},[233,2171,2172],{"class":235,"line":296},[233,2173,2174],{"class":497},"      }\n",[233,2176,2177],{"class":235,"line":378},[233,2178,2179],{"class":497},"    }\n",[233,2181,2182,2184,2186],{"class":235,"line":463},[233,2183,1948],{"class":497},[233,2185,1713],{"class":517},[233,2187,1346],{"class":497},[233,2189,2190,2192,2194,2196,2198,2200,2203,2205,2207,2209,2212,2214,2216,2218,2220,2223],{"class":235,"line":588},[233,2191,2111],{"class":517},[233,2193,1692],{"class":497},[233,2195,1260],{"class":250},[233,2197,1703],{"class":517},[233,2199,1343],{"class":497},[233,2201,2202],{"class":243},"resources\u002Fjs\u002Fadmin\u002Findex.js",[233,2204,1343],{"class":497},[233,2206,1402],{"class":497},[233,2208,501],{"class":497},[233,2210,2211],{"class":243},"public\u002Fjs\u002Fadmin",[233,2213,1343],{"class":497},[233,2215,1713],{"class":517},[233,2217,1692],{"class":497},[233,2219,1206],{"class":250},[233,2221,2222],{"class":517},"()",[233,2224,1346],{"class":497},[11,2226,2227,2230,2231,2233],{},[37,2228,2229],{},"mix.webpackConfig","はのように",[37,2232,2044],{},"に書く様な他の細かいwebpackの設定を定義でき、ここではエイリアスを定義しています。",[11,2235,2236,2239,2240,2242,2243,2245],{},[37,2237,2238],{},"'~js': path.resolve('.\u002Fresources\u002Fjs')","という記述は",[37,2241,1463],{},"というパスの記載があった場合は「",[37,2244,1244],{},"までの絶対ルート」であるとwebpackに支持しています。この様にエイリアスを定義することで相対パスによるファイルの参照がなくなります。router.jsで以下の様に定義していましたが、",[224,2247,2249],{"className":1257,"code":2248,"filename":1322,"language":1260,"meta":229,"style":229},"import Home from '~js\u002Fvue\u002Fpage\u002FHome.vue';\nimport Profile from '~js\u002Fvue\u002Fpage\u002FProfile.vue';\n",[37,2250,2251,2267],{"__ignoreMap":229},[233,2252,2253,2255,2257,2259,2261,2263,2265],{"class":235,"line":236},[233,2254,1329],{"class":1267},[233,2256,1332],{"class":517},[233,2258,1335],{"class":1267},[233,2260,501],{"class":497},[233,2262,1340],{"class":243},[233,2264,1343],{"class":497},[233,2266,1346],{"class":497},[233,2268,2269,2271,2273,2275,2277,2279,2281],{"class":235,"line":247},[233,2270,1329],{"class":1267},[233,2272,1353],{"class":517},[233,2274,1335],{"class":1267},[233,2276,501],{"class":497},[233,2278,1360],{"class":243},[233,2280,1343],{"class":497},[233,2282,1346],{"class":497},[11,2284,2285],{},"もしエイリアスが使えない場合は",[224,2287,2289],{"className":1257,"code":2288,"filename":1322,"language":1260,"meta":229,"style":229},"import Home from '..\u002Fvue\u002Fpage\u002FHome.vue';\nimport Profile from '..\u002Fvue\u002Fpage\u002FProfile.vue';\n",[37,2290,2291,2308],{"__ignoreMap":229},[233,2292,2293,2295,2297,2299,2301,2304,2306],{"class":235,"line":236},[233,2294,1329],{"class":1267},[233,2296,1332],{"class":517},[233,2298,1335],{"class":1267},[233,2300,501],{"class":497},[233,2302,2303],{"class":243},"..\u002Fvue\u002Fpage\u002FHome.vue",[233,2305,1343],{"class":497},[233,2307,1346],{"class":497},[233,2309,2310,2312,2314,2316,2318,2321,2323],{"class":235,"line":247},[233,2311,1329],{"class":1267},[233,2313,1353],{"class":517},[233,2315,1335],{"class":1267},[233,2317,501],{"class":497},[233,2319,2320],{"class":243},"..\u002Fvue\u002Fpage\u002FProfile.vue",[233,2322,1343],{"class":497},[233,2324,1346],{"class":497},[11,2326,2327,2328,2330],{},"このように相対パスになってしまい、もし構成を変えようとした時に相対関係の修正が必要となります。その対策としてエイリアスを定めておくと今後の管理がしやすいです。",[37,2329,2238],{},"はjsディレクトリを参照する様にしています。",[11,2332,2333,2336,2337,2339,2340,2343],{},[37,2334,2335],{},"mix.js('resources\u002Fjs\u002Fadmin\u002Findex.js', 'public\u002Fjs\u002Fadmin').vue();"," にて",[37,2338,1682],{},"をソースとして",[37,2341,2342],{},"'public\u002Fjs\u002Fadmin'","配下に出力し、vueコンパイルを行う様に定義してます。",[11,2345,2346,2347,2350,2351,2354],{},"問題なければ",[37,2348,2349],{},"npm run prod","をプロジェクト ルートでコマンドを叩いてコンパイルを行います。完了後に",[37,2352,2353],{},"public\u002Fjs\u002Fadmin\u002Findex.js","というものが出力されていることを確認してください。",[79,2356,2357],{"id":2357},"反映の確認",[11,2359,2360,2361,2364,2365,2367,2368,2370],{},"jsファイルが出力されましたら",[37,2362,2363],{},"http:\u002F\u002Flocalhost:8005\u002Fsystem","にアクセスします。以下の様に表示され、",[37,2366,1525],{},"というリンクをクリックした際にURLが変わり、",[37,2369,1596],{},"に書かれた内容が表示されればSPAはできています。",[181,2372],{":src":2373,":center":184},"'vue_laravel_app\u002Fhomevue.png'",[11,2375,2376,2378,2379,2382],{},[37,2377,1525],{},"というリンクをクリック後 or ",[37,2380,2381],{},"http:\u002F\u002Flocalhost:8005\u002Fsystem\u002Fprofile","にアクセスしますと以下の様になります。",[181,2384],{":src":2385,":center":184},"'vue_laravel_app\u002Fprofilevue.png'",[11,2387,2388],{},"これでSPAが設定できたことを確認できました。",[72,2390,2392],{"id":2391},"次回は","次回は..",[11,2394,2395],{},"今回は環境構築とLaravel、Vue SPAのセットアップまでとなります。来週はweb apiを通じたSPAでの認証と簡単なプロフィールの更新機能を作成してみます。",[2397,2398,2399],"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 .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":229,"searchDepth":257,"depth":257,"links":2401},[2402,2410,2415,2416,2423],{"id":74,"depth":247,"text":74,"children":2403},[2404,2409],{"id":81,"depth":257,"text":81,"children":2405},[2406,2407,2408],{"id":88,"depth":264,"text":88},{"id":122,"depth":264,"text":122},{"id":162,"depth":264,"text":162},{"id":176,"depth":257,"text":176},{"id":190,"depth":247,"text":190,"children":2411},[2412,2413,2414],{"id":323,"depth":257,"text":324},{"id":387,"depth":257,"text":387},{"id":476,"depth":257,"text":477},{"id":987,"depth":247,"text":988},{"id":1007,"depth":247,"text":1008,"children":2417},[2418,2419,2420,2421,2422],{"id":1014,"depth":257,"text":1015},{"id":1196,"depth":257,"text":1196},{"id":1237,"depth":257,"text":1238},{"id":2030,"depth":257,"text":2030},{"id":2357,"depth":257,"text":2357},{"id":2391,"depth":247,"text":2392},[2425],"devstack","2022-03-02","アプリの解説とシリーズの概要","md",{},"\u002Fseries\u002Fvue-laravel-app-1",{"title":6,"description":2427},"vue_laravel_app","Vue SPA x Laravelでつくる実務パチモンアプリ","series\u002Fvue-laravel-app-1",[969,1027,1260,1206],"vue_laravel_app\u002Fseries.png",null,"QS_7_9yqTbYmh1Gt9XAVtEnkXBXd1wzlct80a3xdm_U",1780987143989]