Laravel5の勉強と、以前から興味のあったAngularJSでコメントシステムを作ってみました。
参考サイト)
LaravelとAngularJSでコメントシステムを作ってみた
Create a Laravel and Angular Single Page Comment Application
Laravel5.3のインストール
2017年3月現在の最新は5.4のようですがハマるのが怖ったため5.3にしました。
PHPのバージョンは7.0.8です。
>composer create-project --prefer-dist laravel/laravel Comment_Application "5.3.*" ~略~ Writing lock file Generating autoload files > Illuminate\Foundation\ComposerScripts::postUpdate > php artisan optimize Generating optimized class loader The compiled class file has been removed. > php artisan key:generate Application key [base64:PXxr3jaeCdAB8tEHlU8uuLisrbPE801G06i1H9kRdRM=] set successfully.
Laravel環境設定
composer.jsonがあったので依存ライブラリを取得してみました。
>composer install Loading composer repositories with package information Installing dependencies (including require-dev) from lock file Nothing to install or update ~略~ `-- url-parse-lax@1.0.0 `-- prepend-http@1.0.4 npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@^1.0.0 (node_modules\chokidar\node_modules\fsevents): npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.1.1: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})
node.jsのパッケージをインストールします。
>npm install -g bower >npm install
AngularJSの取得
>bower init ~略~ (エンターですべてデフォルト値を選択しました) ? Looks good? Yes
>bower install angular --save #ついでにbootstrapも入れました。 >bower install bootstrap-sass-official --save ~略~ bower bootstrap-sass#^3.3.7 install bootstrap-sass#3.3.7 bower jquery#1.9.1 - 3 install jquery#3.1.1 bootstrap-sass#3.3.7 bower_components\bootstrap-sass └── jquery#3.1.1 jquery#3.1.1 bower_components\jquery
「–save」することでbower.jsonへ追加されます。
.envファイルにDB情報を設定します。
~略~
config/app.phpファイルの変更
'timezone' => 'UTC' → 'timezone' => 'Asia/Tokyo' 'locale' => 'en', → 'locale' => 'ja'
commentsテーブルの作成
下記コマンドにより database/migrations/XXXX_create_comments_table.php が作成されます。
>php artisan make:migration create_comments_table --create=comments
XXXX_create_comments_table.php にはあらかじめ主キーとタイムスタンプのカラムが記載されているのでtextカラムとauthorカラムを追加しました。
class CreateCommentsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('comments', function (Blueprint $table) { $table->increments('id'); $table->string('text'); $table->string('author'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('comments'); } }
DBのマイグレーション
前述のマイグレーションファイルを基にテーブルが作成されます。
>php artisan migrate Migration table created successfully. Migrated: 2014_10_12_000000_create_users_table Migrated: 2014_10_12_100000_create_password_resets_table Migrated: 2017_02_28_081528_create_comments_table
モデルの作成
app/Models配下にCommentテーブルのモデルを作成します。
php artisan make:model Models\Comment
モデルの$fillableプロパティでデータの保存を行いたいカラムを設定します。
class Comment extends Model { protected $fillable = array('author', 'text'); }
シーダーの作成
>php artisan make:seeder CommentTableSeeder
作成したシーダーへ初期データを記載します。
class CommentTableSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { DB::table('comments')->delete(); Comment::create(array( 'author' => '名無しさん', 'text' => '今日はいい天気ですね。気持ちが良いですね。' )); Comment::create(array( 'author' => '山田さん', 'text' => '久しぶり。山田です。' )); Comment::create(array( 'author' => '太郎さん', 'text' => 'Laravelって便利なんですね。はじめて知りました。' )); } }
DatabaseSeederにCommentTableSeederの呼び出しを追加します。
class DatabaseSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { Eloquent::unguard(); $this->call('CommentTableSeeder'); $this->command->info('Comment table seeded.'); } }
初期データの登録
>php artisan db:seed
コントローラーの作成
CommentControllerを作成します。
>php artisan make:controller CommentController
作成したコントローラーへindex, store, destroyを追加します。
class CommentController extends Controller { public function index() { return response()->json(Comment::get()); } public function store(Request $request) { Comment::create(array( 'author' => $request->input('author'), 'text' => $request->input('text') )); return response()->json(array('success' => true)); } public function destroy($id) { Comment::destroy($id); return response()->json(array('success' => true)); } }
ルーティングの設定
routes/web.php
Route::get('/', function () { // return view('welcome'); return View::make('index'); });
routes/api.php
Route::get('/user', function (Request $request) { return $request->user(); })->middleware('auth:api'); Route::resource('comments', 'CommentController', array('only' => array('index', 'store', 'destroy')));
確認します
>php artisan route:list +--------+----------+------------------------+------------------+------------------------------------------------+--------------+ | Domain | Method | URI | Name | Action | Middleware | +--------+----------+------------------------+------------------+------------------------------------------------+--------------+ | | GET|HEAD | / | | Closure | web | | | POST | api/comments | comments.store | App\Http\Controllers\CommentController@store | api | | | GET|HEAD | api/comments | comments.index | App\Http\Controllers\CommentController@index | api | | | DELETE | api/comments/{comment} | comments.destroy | App\Http\Controllers\CommentController@destroy | api | | | GET|HEAD | api/user | | Closure | api,auth:api | +--------+----------+------------------------+------------------+------------------------------------------------+--------------+
SCSSの設定
resources/assets/sass/app.scss
// Fonts @import url(https://fonts.googleapis.com/css?family=Raleway:300,400,600); // Variables @import "variables"; // Bootstrap // @import "node_modules/bootstrap-sass/assets/stylesheets/bootstrap"; @import "bower_components/bootstrap-sass/assets/stylesheets/bootstrap";
gulpfile.js
const elixir = require('laravel-elixir'); require('laravel-elixir-vue-2'); /* |-------------------------------------------------------------------------- | Elixir Asset Management |-------------------------------------------------------------------------- | | Elixir provides a clean, fluent API for defining some basic Gulp tasks | for your Laravel application. By default, we are compiling the Sass | file for your application as well as publishing vendor resources. | */ elixir((mix) => { mix.sass('app.scss') .copy('bower_components/bootstrap-sass/assets/fonts/bootstrap/**', 'public/fonts/bootstrap') .scripts([ "bower_components/jquery/dist/jquery.js", "bower_components/bootstrap-sass/assets/javascripts/bootstrap.min.js", "bower_components/angular/angular.js" ], 'public/js/app.js', './' ); //.webpack('app.js'); });
Angularの各ファイルを作成
public/js/services/commentService.js
angular.module('commentService', []) .factory('Comment', function($http) { return { // すべてのコメントを取得する get : function() { return $http.get('/api/comments'); }, // コメントを保存する。 save : function(commentData) { return $http({ method: 'POST', url: '/api/comments', headers: { 'Content-Type' : 'application/x-www-form-urlencoded' }, data: $.param(commentData) }); }, // コメントを削除する destroy : function(id) { return $http.delete('/api/comments/' + id); } } });
public/js/controllers/mainCtrl.js
angular.module('mainCtrl', []) // コメントサービスをコントローラーに挿入する .controller('mainController', function($scope, $http, Comment) { // コメントフォームのデータを保持するオブジェクト $scope.commentData = {}; // ロードアイコンの表示 $scope.loading = true; // すべてのコメントを取得して、 $scope.comments にわたす Comment.get() .then(function onSuccess(response) { $scope.comments = response.data; $scope.loading = false; }); // .success(function(data) { // $scope.comments = data; // $scope.loading = false; // }); // 送信データのコントローラー $scope.submitComment = function() { $scope.loading = true; // コメントを保存する。 Comment.save($scope.commentData) .then(function onSuccess(response) { Comment.get() .then(function onSuccess(responseData) { $scope.comments = responseData.data; $scope.loading = false; }); }, function onError(response) { console.log(response.data); }); // .success(function(data) { // // // 成功した場合、コメントリストをリフレッシュする // Comment.get() // .success(function(getData) { // $scope.comments = getData; // $scope.loading = false; // }); // // }) // .error(function(data) { // console.log(data); // }); }; // コメント削除のコントローラー $scope.deleteComment = function(id) { $scope.loading = true; Comment.destroy(id) .then(function onSuccess(response) { Comment.get() .then(function onSuccess(responseData) { $scope.comments = responseData.data; $scope.loading = false; }); }); // .success(function(data) { // // // 成功した場合、コメントリストをリフレッシュする // Comment.get() // .success(function(getData) { // $scope.comments = getData; // $scope.loading = false; // }); // // }); }; });
public/js/app/app.js
#Angularのモジュールを登録しています。gulpで作成したファイルと同名で紛らわしいですが別物です。
var commentApp = angular.module('commentApp', ['mainCtrl', 'commentService']);
gulpを実行してscss→css(app.css)の作成と、jqueryとbootstrap、angularを結合したjsファイルを作成します。
>gulp [02:16:36] Using gulpfile D:\XXX\gulpfile.js [02:16:36] Starting 'all'... [02:16:36] Starting 'sass'... [02:16:37] Finished 'sass' after 1.06 s [02:16:37] Starting 'copy'... [02:16:37] Finished 'copy' after 62 ms [02:16:37] Starting 'scripts'... [02:16:37] Finished 'scripts' after 300 ms [02:16:37] Finished 'all' after 1.43 s [02:16:37] Starting 'default'... ┌───────────────┬──────────────────────────┬─────────────────────────────────────────────────────────────────────┬────────────────────────┐ │ Task │ Summary │ Source Files │ Destination │ ├───────────────┼──────────────────────────┼─────────────────────────────────────────────────────────────────────┼────────────────────────┤ │ mix.sass() │ 1. Compiling Sass │ resources\assets\sass\app.scss │ public\css\app.css │ │ │ 2. Autoprefixing CSS │ │ │ │ │ 3. Concatenating Files │ │ │ │ │ 4. Writing Source Maps │ │ │ │ │ 5. Saving to Destination │ │ │ ├───────────────┼──────────────────────────┼─────────────────────────────────────────────────────────────────────┼────────────────────────┤ │ mix.copy() │ 1. Saving to Destination │ bower_components/bootstrap-sass/assets/fonts/bootstrap/**/**/* │ public/fonts/bootstrap │ ├───────────────┼──────────────────────────┼─────────────────────────────────────────────────────────────────────┼────────────────────────┤ │ mix.scripts() │ 1. Concatenating Files │ bower_components\jquery\dist\jquery.js │ public/js/app.js │ │ │ 2. Writing Source Maps │ bower_components\bootstrap-sass\assets\javascripts\bootstrap.min.js │ │ │ │ 3. Saving to Destination │ bower_components\angular\angular.js │ │ └───────────────┴──────────────────────────┴─────────────────────────────────────────────────────────────────────┴────────────────────────┘ [02:16:37] Finished 'default' after 16 ms
viewの作成
resources/views/index.blade.php
<!doctype html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>Laravel と Angular の コメントシステム</title> {{--<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.0/css/bootstrap.min.css">--}} <link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css"> <link rel="stylesheet" href="/css/app.css"> <script src="/js/app.js"></script> <style> body { padding-top:30px; } form { padding-bottom:20px; } .comment { padding-bottom:20px; } </style> {{--<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>--}} {{--<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.8/angular.min.js"></script>--}} <script src="js/controllers/mainCtrl.js"></script> <script src="js/services/commentService.js"></script> <script src="js/app/app.js"></script> </head> <body class="container" ng-app="commentApp" ng-controller="mainController"> <div class="col-md-8 col-md-offset-2"> <div class="page-header"> <h2>Laravel と Angular の コメントシステム</h2> </div> <form ng-submit="submitComment()"> <div class="form-group"> <input type="text" class="form-control input-sm" name="author" ng-model="commentData.author" placeholder="名前"> </div> <div class="form-group"> <input type="text" class="form-control input-lg" name="comment" ng-model="commentData.text" placeholder="コメントをどうぞ"> </div> <div class="form-group text-right"> <button type="submit" class="btn btn-primary btn-lg">コメントする</button> </div> </form> <p class="text-center" ng-show="loading"><span class="fa fa-meh-o fa-5x fa-spin"></span></p> <div class="comment" ng-hide="loading" ng-repeat="comment in comments"> <h3>コメント #@{{ comment.id }} <small>by @{{ comment.author }}</h3> <p>@{{ comment.text }}</p> <p><a href="#" ng-click="deleteComment(comment.id)" class="text-muted">削除</a></p> </div> </div> </body> </html>