SPA環境のお勉強ってことで
https://qiita.com/minato-naka/items/9241d9c7a7433985056d
これ見ながらぼちぼちやってく🐱
https://github.com/yuukanehiro/Laravel-Vue.js-SPA-Study
もくじ
環境構築
docker │ ┝nginx │ ┗Dockerfile │ ┝php │ ┝php.ini │ ┗Dockerfile │ docker-compose.yml
./docker-compose.yml
version: '3' services: php: container_name: php build: ./docker/php volumes: - ./src:/var/www links: - db nginx: image: nginx container_name: nginx ports: - 80:80 volumes: - ./src:/var/www - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf depends_on: - php db: image: mysql:5.7 container_name: db environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: laravel MYSQL_USER: docker MYSQL_PASSWORD: docker TZ: 'Asia/Tokyo' command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci volumes: - ./docker/db/data:/var/lib/mysql - ./docker/db/my.cnf:/etc/mysql/conf.d/my.cnf - ./docker/db/sql:/docker-entrypoint-initdb.d ports: - 3306:3306
./docker/nginx/default.conf
server { listen 80; index index.php index.html; root /var/www/public; location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass php:9000; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_path_info; } }
./docker/php/php.ini
[Date] date.timezone = "Asia/Tokyo" [mbstring] mbstring.internal_encoding = "UTF-8" mbstring.language = "Japanese"
./docker/php/Dockerfile
FROM php:7.2-fpm COPY php.ini /usr/local/etc/php/ RUN apt-get update \ && apt-get install -y zlib1g-dev mariadb-client \ && docker-php-ext-install zip pdo_mysql #Composer install COPY --from=composer /usr/bin/composer /usr/bin/composer ENV COMPOSER_ALLOW_SUPERUSER 1 ENV COMPOSER_HOME /composer ENV PATH $PATH:/composer/vendor/bin WORKDIR /var/www RUN composer global require "laravel/installer"
Laravelインストール
Laravel-Vue.js-SPA-Study $ composer global require laravel/installer ravel-Vue.js-SPA-Study $ composer create-project --prefer-dist laravel/laravel src
起動させる
Laravel-Vue.js-SPA-Study $ docker-compose up -d
project docker │ ┝nginx │ ┝default.conf │ ┗Dockerfile │ ┝php │ ┝php.ini │ ┗Dockerfile │ docker-compose.yml │ src
起動確認
http://localhost/
MySQL接続確認
$ docker exec -it db-host bash root@e3e8d37b8b70:/# mysql -u root -p Enter password: 「root」と入力 Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 5 Server version: 5.7.32 MySQL Community Server (GPL) Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql>
laravel/uiインストール
src $ composer require laravel/ui src $ npm install && npm run dev
雛形をつくる
src $ php artisan ui vue
Vue Routerインストール
src $ npm install --save vue-router
ビルド
src $ npm run dev
/.gitignore
docker/db/data vendor
src/app/.ignore
/node_modules /public/hot /public/storage + /public/js + /public/css /storage/*.key /vendor .env .env.backup .phpunit.result.cache Homestead.json Homestead.yaml npm-debug.log yarn-error.log
js, cssフォルダを追記する
npm run devをするのが面倒くさい時は
src $ npm run watch
routes/web.php
// Route::get('/', function () { // return view('welcome'); // }); Route::get('/{any}', function () { return view('app'); })->where('any', '.*');
resources/views/app.brade.php
<!doctype html> <html lang="{{ str_replace('_', '-', app()->getLocale()) }}"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- CSRF Token --> <meta name="csrf-token" content="{{ csrf_token() }}"> <title>{{ config('app.name', 'Vue Laravel SPA') }}</title> <!-- Styles --> <link href="{{ mix('/css/app.css') }}" rel="stylesheet"> </head> <body> <div id="app"> </div> <!-- Scripts --> <script src="{{ mix('/js/app.js') }}" defer></script> </body> </html>
<div id="app"> + <example-component></example-component> </div>
追加する
http://localhost/
resources/js/compoonents/HeaderComponent.vue
<template> <div class="container-fluid bg-dark mb-3"> <div class="container"> <nav class="navbar navbar-dark"> <span class="navbar-brand mb-0 h1">Vue Laravel SPA</span> <div> <button class="btn btn-success">List</button> <button class="btn btn-success">ADD</button> </div> </nav> </div> </div> </template> <script> export default {} </script>
resources/views/app.brade.php
・・・ <div id="app"> <header-component></header-component> + <example-component></example-component> </div> ・・・
resources/js/compoonents/TaskListComponent.vue
<template> <div class="container"> <table class="table table-hover"> <thead class="thead-light"> <tr> <th scope="col">#</th> <th scope="col">Title</th> <th scope="col">Content</th> <th scope="col">Person In Charge</th> <th scope="col">Show</th> <th scope="col">Edit</th> <th scope="col">Delete</th> </tr> </thead> <tbody> <tr> <th scope="row">1</th> <td>Title1</td> <td>Content1</td> <td>Ichiro</td> <td> <button class="btn btn-primary">Show</button> </td> <td> <button class="btn btn-success">Edit</button> </td> <td> <button class="btn btn-danger">Delete</button> </td> </tr> <tr> <th scope="row">2</th> <td>Title2</td> <td>Content2</td> <td>Jiro</td> <td> <button class="btn btn-primary">Show</button> </td> <td> <button class="btn btn-success">Edit</button> </td> <td> <button class="btn btn-danger">Delete</button> </td> </tr> <tr> <th scope="row">3</th> <td>Title3</td> <td>Content3</td> <td>Saburo</td> <td> <button class="btn btn-primary">Show</button> </td> <td> <button class="btn btn-success">Edit</button> </td> <td> <button class="btn btn-danger">Delete</button> </td> </tr> </tbody> </table> </div> </template> <script> export default {} </script>
resources/js/app.js
+ import VueRouter from 'vue-router'; import HeaderComponent from "./components/HeaderComponent"; + import TaskListComponent from "./components/TaskListComponent"; window.Vue = require('vue'); + Vue.use(VueRouter); + + const router = new VueRouter({ + mode: 'history', + routes: [ + { + path: '/tasks', + name: 'task.list', + component: TaskListComponent + }, + ] + }); const app = new Vue({ el: '#app', + router });
ここが重要
routes: [ { path: '/tasks', name: 'task.list', component: TaskListComponent }, ]
- パス:/tasks
- 名前:task.list
- コンポーネント: TaskListComponent
resources/views/app.brade.php
・・・ <div id="app"> <header-component></header-component> <example-component></example-component> + <router-view></router-view> </div> ・・・
resources/js/compoonents/HeaderComponent.vue
<template> <div class="container-fluid bg-dark mb-3"> <div class="container"> <nav class="navbar navbar-dark"> <span class="navbar-brand mb-0 h1">Vue Laravel SPA</span> <div> + <router-link v-bind:to="{name: 'task.list'}"> <button class="btn btn-success">List</button> + </router-link> <button class="btn btn-success">ADD</button> </div> </nav> </div> </div> </template> <script> export default {} </script>
ボタンにリンクを追加する
http://localhost/tasks
タスク詳細コンポーネント
resources/js/compoonents/TaskShowComponent.vue
<template> <div class="container"> <div class="row justify-content-center"> <div class="col-sm-6"> <form> <div class="form-group row border-bottom"> <label for="id" class="col-sm-3 col-form-label">ID</label> <input type="text" class="col-sm-9 form-control-plaintext" readonly id="id" v-bind:value="taskId"> </div> <div class="form-group row border-bottom"> <label for="title" class="col-sm-3 col-form-label">Title</label> <input type="text" class="col-sm-9 form-control-plaintext" readonly id="title" value="title title"> </div> <div class="form-group row border-bottom"> <label for="content" class="col-sm-3 col-form-label">Content</label> <input type="text" class="col-sm-9 form-control-plaintext" readonly id="content" value="content content"> </div> <div class="form-group row border-bottom"> <label for="person-in-charge" class="col-sm-3 col-form-label">Person In Charge</label> <input type="text" class="col-sm-9 form-control-plaintext" readonly id="person-in-charge" value="Ichiro"> </div> </form> </div> </div> </div> </template> <script> export default { props: { taskId: String } } </script>
+ export default { + props: { + taskId: String + } + }
taskIdで受け取る値を定義
ここでバインドしている
<input type="text" class="col-sm-9 form-control-plaintext" readonly id="id" v-bind:value="taskId">
resources/js/app.js
import VueRouter from 'vue-router'; import HeaderComponent from "./components/HeaderComponent"; import TaskListComponent from "./components/TaskListComponent"; + import TaskShowComponent from "./components/TaskShowComponent"; ・・・ const router = new VueRouter({ mode: 'history', routes: [ { path: '/tasks', name: 'task.list', component: TaskListComponent }, + { + path: '/tasks/:taskId', + name: 'task.show', + component: TaskShowComponent, + props: true + }, ] });
resources/js/compoonents/TaskListComponent.vue
+ <router-link v-bind:to="{name: 'task.show', params: {taskId: 1}}"> <button class="btn btn-primary">Show</button> + </router-link> + <router-link v-bind:to="{name: 'task.show', params: {taskId: 2}}"> <button class="btn btn-primary">Show</button> + </router-link> + <router-link v-bind:to="{name: 'task.show', params: {taskId: 3}}"> <button class="btn btn-primary">Show</button> + </router-link>
http://localhost/tasks/1
反映されなかったらcommand + Rで。
タスク登録コンポーネント
resources/js/components/TaskCreateComponent.vue
<template> <div class="container"> <div class="row justify-content-center"> <div class="col-sm-6"> <form> <div class="form-group row"> <label for="title" class="col-sm-3 col-form-label">Title</label> <input type="text" class="col-sm-9 form-control" id="title"> </div> <div class="form-group row"> <label for="content" class="col-sm-3 col-form-label">Content</label> <input type="text" class="col-sm-9 form-control" id="content"> </div> <div class="form-group row"> <label for="person-in-charge" class="col-sm-3 col-form-label">Person In Charge</label> <input type="text" class="col-sm-9 form-control" id="person-in-charge"> </div> <button type="submit" class="btn btn-primary">Submit</button> </form> </div> </div> </div> </template> <script> export default {} </script>
resources/js/app.js
import VueRouter from 'vue-router'; import HeaderComponent from "./components/HeaderComponent"; import TaskListComponent from "./components/TaskListComponent"; import TaskShowComponent from "./components/TaskShowComponent"; + import TaskCreateComponent from "./components/TaskCreateComponent"; ・・・ const router = new VueRouter({ mode: 'history', routes: [ { path: '/tasks', name: 'task.list', component: TaskListComponent }, { path: '/tasks/:taskId', name: 'task.show', component: TaskShowComponent, props: true }, + { + path: '/tasks/create', + name: 'task.create', + component: TaskCreateComponent + }, ] });
resources/js/compoonents/HeaderComponent.vue
+ <router-link v-bind:to="{name: 'task.create'}"> <button class="btn btn-success">ADD</button> + </router-link>
http://localhost/tasks/create
タスク編集コンポーネンット
resources/js/components/TaskEditComponent.vue
<template> <div class="container"> <div class="row justify-content-center"> <div class="col-sm-6"> <form> <div class="form-group row"> <label for="id" class="col-sm-3 col-form-label">ID</label> <input type="text" class="col-sm-9 form-control-plaintext" readonly id="id" v-bind:value="taskId"> </div> <div class="form-group row"> <label for="title" class="col-sm-3 col-form-label">Title</label> <input type="text" class="col-sm-9 form-control" id="title"> </div> <div class="form-group row"> <label for="content" class="col-sm-3 col-form-label">Content</label> <input type="text" class="col-sm-9 form-control" id="content"> </div> <div class="form-group row"> <label for="person-in-charge" class="col-sm-3 col-form-label">Person In Charge</label> <input type="text" class="col-sm-9 form-control" id="person-in-charge"> </div> <button type="submit" class="btn btn-primary">Submit</button> </form> </div> </div> </div> </template> <script> export default { props: { taskId: String } } </script>
resources/js/app.js
import VueRouter from 'vue-router'; import HeaderComponent from "./components/HeaderComponent"; import TaskListComponent from "./components/TaskListComponent"; import TaskShowComponent from "./components/TaskShowComponent"; import TaskCreateComponent from "./components/TaskCreateComponent"; + import TaskEditComponent from "./components/TaskEditComponent"; ・・・ const router = new VueRouter({ mode: 'history', routes: [ { path: '/tasks', name: 'task.list', component: TaskListComponent }, { path: '/tasks/:taskId', name: 'task.show', component: TaskShowComponent, props: true }, { path: '/tasks/create', name: 'task.create', component: TaskCreateComponent }, + { + path: '/tasks/:taskId/edit', + name: 'task.edit', + component: TaskEditComponent, + props: true + }, ] });
<template> <div class="container"> <table class="table table-hover"> <thead class="thead-light"> <tr> <th scope="col">#</th> <th scope="col">Title</th> <th scope="col">Content</th> <th scope="col">Person In Charge</th> <th scope="col">Show</th> <th scope="col">Edit</th> <th scope="col">Delete</th> </tr> </thead> <tbody> <tr> <th scope="row">1</th> <td>Title1</td> <td>Content1</td> <td>Ichiro</td> <td> <router-link v-bind:to="{name: 'task.show', params: {taskId: 1}}"> <button class="btn btn-primary">Show</button> </router-link> </td> <td> + <router-link v-bind:to="{name: 'task.edit', params: {taskId: 1}}"> <button class="btn btn-success">Edit</button> + </router-link> </td> <td> <button class="btn btn-danger">Delete</button> </td> </tr> <tr> <th scope="row">2</th> <td>Title2</td> <td>Content2</td> <td>Jiro</td> <td> <router-link v-bind:to="{name: 'task.show', params: {taskId: 2}}"> <button class="btn btn-primary">Show</button> </router-link> </td> <td> + <router-link v-bind:to="{name: 'task.edit', params: {taskId: 2}}"> <button class="btn btn-success">Edit</button> + </router-link> </td> <td> <button class="btn btn-danger">Delete</button> </td> </tr> <tr> <th scope="row">3</th> <td>Title3</td> <td>Content3</td> <td>Saburo</td> <td> <router-link v-bind:to="{name: 'task.show', params: {taskId: 3}}"> <button class="btn btn-primary">Show</button> </router-link> </td> <td> + <router-link v-bind:to="{name: 'task.edit', params: {taskId: 3}}"> <button class="btn btn-success">Edit</button> + </router-link> </td> <td> <button class="btn btn-danger">Delete</button> </td> </tr> </tbody> </table> </div> </template> <script> export default {} </script>
http://localhost/tasks/1/edit