Laravelを使って、簡単なSNSを作ろうのコーナーです!
完成形はこんな感じです〜〜
自信がない方、作り方だけ知れればいいよーっていうかたはこちらをご覧ください!
日本語でまなぼ!twitter風SNSアプリ開発!(MVCフレームワーク使用)
上記の記事が日本語だけで全部解説していて大雑把に理解することを目的としていることに対して、当記事はコードをバリバリ書いてちゃっかり作っちゃお〜っていうことを目的としています。
対象者
- PHPはわかるけど、Laravelは初心者もしくは中級者
- 開発環境はだいたい自分で構築できる人
注意点
当記事は開発環境の構築だとか、初期ページを表示させるとか、設定をちゃんとするとかその辺は結構飛ばしています。それよりも、Laravelを使ってどうやってSNSを作るのか、に重きを置いています。開発環境の構築等でつまづく人はたくさんいらっしゃるかとは思いますが、そのようなかたは一番最後にSNSの作り方を書いているので、作り方だけでも理解してもらえたらなと思います。
あと、bladeの中にcssを書いていますが、普通はこんな書き方しません。
bladeを見るだけでどんなcssをかけているかが分かりやすいかな〜と思ったのでこうしています。
では、早速始めていきましょう〜!
こんなの楽勝やわ〜っていうところは随時飛ばしていってください!
目次
- 0.はじめに外観を知ろう
- 1.準備
- 1-1. 開発環境の構築
- 1-2. Composerのインストール
- 1-3. Laravelプロジェクトを作成する
- 2.Laravelでコードを書き始める前の儀式
- 2-1. キャッシュの権限の変更の儀式
- 2-2. .envの設定
- 2-3. composer installとnpm installを実行
- 2-4. 初期ページが表示されるか確認
- 3.実装していきますよ!
- 3-1. 一瞬でログイン機能をつける
- 3-2. Tweetモデルとtweetsテーブルを作成する
- 3-3. ルーティングを設定する
- 3-4. コントローラを作成する
- 3-5. ビューを作成する
- 3-6.コントローラで、ツイートを保存する
- 3-7. 保存されたツイートをビューで表示する
- 工夫できること(やり方は説明しませんが...)
- ユーザーにアイコンとか名前とか設定してもらう
- ツイートした瞬間にビューにツイート内容を反映させる
- データが大量になった時にやばい
終わりに
0. はじめに外観を知ろう
【必要なテーブル】
- usersテーブル
- tweetsテーブル
【必要なページ】
- ログインページ(Laravel標準のを使います)
- 会員登録ページ(Laravel標準のを使います)
- タイムライン(作ります!)
もっと詳細はこちら(現在作成中です)
1. 準備
準備を始めます〜
1-1. 開発環境の構築
DockerでもXAMPPでもvagrantでも好きな物を使って環境構築してください!
DockerとXAMPPなら質問に答えられると思うので、QiitaでもtwitterのDMでもなんでもしてください〜
phpとデータベース(mysqlなど)とサーバー(nginxなど)があればいけます!
1-2. composerのインストール
PHPやったことある人ならすでにcomposerはインストールしていると信じてます。。。
この辺を元にcomposerをインストールしてみてください!
困ったらQiitaかTwitterで質問してくれてOKです〜(2回目)
最終的に、composer -v
でそれっぽくなったらいけてます!
1-3. Laravelプロジェクトを作成する
プロジェクトの作成には2種類あります。
- Laravel本体をインストールして、Laravelコマンドを使えるようにしてから、プロジェクトを作成する方法
- Laravel本体というよりは、composerでLaravelのプロジェクトを作成する方法
今回は後者でやっていきます。(1も2もほとんど変わりません。)
プロジェクトのディレクトリに移動したら、以下のコマンドを実行してください。(「sns」の部分が任意のプロジェクト名です。ご自身で 名前をつけてもらってもOKです。)
$ composer create-project --prefer-dist laravel/laravel sns
snsというディレクトリが出てきて、その中にLaravelの一式がインストールされています。
2. Laravelでコードを書き始める前の儀式
Laravelでプロジェクト作成後に必ずやらないといけないことがあるんです!
今から、Laravelの儀式を執り行います〜〜
2-1. キャッシュの権限の変更の儀式
コマンドプロンプトもしくはターミナルで以下のコマンドを実行しましょう!
$ chmod -R 777 bootstrap/cache
$ chmod -R 777 storage
2-2. .envの設定
.env
のデータベース接続の部分を設定していきます〜
デフォルトではこうなってると思います。
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret
それを、それぞれ開発環境に合わせて設定してください!
僕の場合は、DB_HOST=mysql
に設定しないと、connection refusedのエラーが出ました
2-3. composer installとnpm installを実行
この2つのコマンドを実行すれば、必要なものが全部インストールされます!
(composer installはすでに実行されてるかも)
$ composer install
$ npm install
npm installしていたらこんなエラーが出てきました
Additional dependencies must be installed. This will only take a moment.
Running: npm install vue-template-compiler --save-dev --production=false
なので、仕方なくnpm install vue-template-compiler --save-dev --production=false
を実行。
2-4. 初期ページが表示されるか確認
このページが無事に出ればオッケーです〜〜!
3. 実装していきますよ!
3-1. 一瞬でログイン機能をつける
$ php artisan migrate
$ php artisan make:auth
デフォルトでは、会員登録もしくはログインをした後はhttp://localhost/home
に遷移します。
あとで、会員登録もしくはログインした後の、遷移先を変えます〜。あとで!
そして、先ほどのphp artisan migrate
でUserテーブルが作成されました。
何が作られたのか知りたい場合は、マイグレーションファイル(/database/migrations/2014_10_12_000000_create_users_table.php)を参考にしてください!
そして、今回はツイートを送信した後に、ツイートを保存するテーブルが必要です!
なので、tweetsテーブルを作りましょう〜
3-2. Tweetモデルとtweetsテーブルを作成する
コマンド一発でとりあえずモデルとマイグレーションファイルを作成します。
php artisan make:Model Tweet --migration
まずは、マイグレーションファイルからサクッと作っていきます。
場所はここdatabase/migrations/2019_02_28_032953_create_tweets_table.php
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateTweetsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('tweets', function (Blueprint $table) {
$table->bigIncrements('id');
$table->integer('user_id'); // <--- 追加
$table->text('tweet'); // <--- 追加
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('tweets');
}
}
どのユーザーがツイートをしたかというのを知るために、user_id
カラムを追加しました。
そして、ツイート内容を保存するために、tweet
カラムを追加しました。
次にTweetモデル(app/Tweet.php)をちょっと変更します。
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Tweet extends Model
{
protected $fillable = [ // <--- 追加
'user_id', 'tweet',
];
}
※$fillableに設定されている値のみテーブルに保存することができます。
ここまできたら、マイグレーションファイルを反映させましょう!
$ php artisan migrate
3-3. ルーティングを設定する
ルーティングは、このURLにアクセスしたらこのページを表示するとか、このページにPOSTリクエストを送ったらどのコントローラで処理をするとかを決めるやつです。
Laravelはroute/web.phpでルーティングを決めています。
下の二行を追加しましょう。
<?php
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::get('/', function () {
return view('welcome');
});
Auth::routes();
Route::get('/home', 'HomeController@index')->name('home');
Route::get('/timeline', 'Auth\TimelineController@showTimelinePage'); // <--- 追加
Route::post('/timeline', 'Auth\TimelineController@postTweet'); // <--- 追加
Route::get('/timeline', 'Auth\TimelineController@showTimelinePage');
は/timelineにアクセスされたら、TimelineコントローラのshoeTimelinePageという関数で処理をするよ!っていう意味です。
Route::post('/timeline', 'Auth\TimelineController@postTweet');
は/timelineにpostリクエストされたら、TimelineコントローラのpostTweetという関数で処理をするよ!という意味になります。
Auth\
を入れたのは、一応ログイン(auth)をしていないと見れないページですよ!っていう意味を込めてます。(ツイッターのタイムラインってログインしてなくても見れたっけ)
つまり、これからTimelineControllerを作っていかないといけませんね!!
じゃあ、作りましょう!
3-4. コントローラを作成する
今回はSNSということでTImelineを実装していきます。
まずは、Timelineコントローラを作成しましょう!
以下のコマンドを実行してください。
$ php artisan make:controller Auth/TimelineController
そしたら、app/Http/Controllers/Auth/TimelineCOntroller.php
ができています。
まだ生まれたての赤ん坊です。
<?php
namespace App\Http\Controllers\Auth;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class TimelineController extends Controller
{
//
}
ここに、ルーティングの時に設定した、showTimelinePage
とpostTweet
を作成していきます。
<?php
namespace App\Http\Controllers\Auth;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class TimelineController extends Controller
{
public function showTimelinePage()
{
return view('auth.timeline'); // resource/views/auth/timeline.blade.phpを表示する
}
public function postTweet(Request $request) //ここはあとで実装します。(Requestはpostリクエストを取得するためのものです。)
{
}
}
3-5. ビューを作成する
resource/views/auth/timeline.blade.php
を作成します。
ご存知の通り、ビューは~~~.blade.phpというファイル名になります。
今までの流れを説明すると、
ユーザーが/timeline
にアクセスする ----> Auth\TimelineコントローラのshowTimelinePageが呼ばれる(web.php) ----> resources/views/auth/timeline.blade.phpを表示する
っていう感じです。
ビューはあまり説明しません!
ビューの雛形だけ用意しておきました。wraperクラスの中に色々書いていきます。
<!DOCTYPE HTML>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>SNSを作ってみよう!</title>
<link href="{{ mix('css/app.css') }}" rel="stylesheet" type="text/css">
</head>
<body>
<div class="wrapper">
<!-- ここにビューを書いていきます!! -->
</div>
<script src="{{ mix('js/app.js') }}"></script>
</body>
</html>
色々書いていったいったん出来上がったのがこちらです。
<!DOCTYPE HTML>
<html lang="ja" style="height:100%;">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>SNSを作ってみよう!</title>
<link href="{{ mix('css/app.css') }}" rel="stylesheet" type="text/css">
</head>
<body style="height:100%; background-color: #E6ECF0;">
<div class="wrapper" style="margin: 0 auto; width: 50%; height: 100%; background-color: white;">
<form action="/timeline" method="post">
{{ csrf_field() }}
<div style="background-color: #E8F4FA; text-align: center;">
<input type="text" name="tweet" style="margin: 1rem; padding: 0 1rem; width: 70%; border-radius: 6px; border: 1px solid #ccc; height: 2.3rem;" placeholder="今どうしてる?">
<button type="submit" style="background-color: #2695E0; color: white; border-radius: 10px; padding: 0.5rem;">ツイート</button>
</div>
</form>
</div>
<script src="{{ mix('js/app.js') }}"></script>
</body>
</html>
localhost/timeline
にアクセスしてみてください!
見た目はこんな感じ
それっぽい!!!
ほぼHTMLなので、そこは頑張ってくださいとしか言いようがないのですが、Laravelのビューで一応formの部分だけ説明しておきます。
先ほどweb.phpで、/timeline
にpostリクエストをすると、TimelineコントローラのpostTweerアクションにいくという処理をしました。ここのformでは、/timelineにpostリクエストをしています。
<form action="/timeline" method="post">
そして、Laravelでpostリクエストをするときは、formの中に{{ csrf_field() }}
を含めないとエラーが出ます。これは、クロスサイトリクエストフォージェリっていう攻撃から守るための暗号です!なので、必要!
3-6.コントローラで、ツイートを保存する
もう一回Timelineコントローラに戻り、postTweetを実装していきます。
postTweetでは、送られてきたツイート内容をゲットします!
<?php
namespace App\Http\Controllers\Auth;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth; // <--- 追加
use \App\Tweet; // <--- 追加
class TimelineController extends Controller
{
public function showTimelinePage()
{
return view('auth.timeline');
}
public function postTweet(Request $request) // <--- 色々変更
{
$validator = $request->validate([ // これだけでバリデーションできるLaravelすごい!
'tweet' => ['required', 'string', 'max:280'], // 必須・文字であること・280文字まで(ツイッターに合わせた)というバリデーションをします(ビューでも軽く説明します。)
]);
Tweet::create([ // tweetテーブルに入れるよーっていう合図
'user_id' => Auth::user()->id, // Auth::user()は、現在ログインしている人(つまりツイートしたユーザー)
'tweet' => $request->tweet, // ツイート内容
]);
return back(); // リクエスト送ったページに戻る(つまり、/timelineにリダイレクトする)
}
}
コメントで説明した通りです。これ以上の、説明はないかと...。
$validator = ...
のところで、設定したバリデーションに引っかかったら、それ以降は処理せずにビューにエラーを返します。
さて、色々ツイートしてみてください!
うまく保存されてるかな!?!?
3-7. 保存されたツイートをビューで表示する
今は、データベースには保存されますが、タイムラインに表示されないですよね!ってことで、表示していきます〜
次は、TimelineControllerのshowTimelinePageをいじっていきます。
<?php
namespace App\Http\Controllers\Auth;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;
use \App\Tweet;
class TimelineController extends Controller
{
public function showTimelinePage()
{
$tweets = Tweet::latest()->get(); // <--- 追加
return view('auth.timeline', compact('tweets')); // <--- 変更
}
public function postTweet(Request $request)
{
$validator = $request->validate([
'tweet' => ['required', 'string', 'max:280'],
]);
Tweet::create([
'user_id' => Auth::user()->id,
'tweet' => $request->tweet,
]);
return back();
}
}
$tweets = Tweet::latest()->get();
でツイートテーブルに保存されたツイートを新着順で取得し、compact('tweets')
とすることで、$tweetsをビューに送っています。
では、ビューはどうなるかというと、、、
<!DOCTYPE HTML>
<html lang="ja" style="height:100%;">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>SNSを作ってみよう!</title>
<link href="{{ mix('css/app.css') }}" rel="stylesheet" type="text/css">
</head>
<body style="height:100%; background-color: #E6ECF0;">
<div class="wrapper" style="margin: 0 auto; width: 50%; height: 100%; background-color: white;">
<form action="/timeline" method="post">
{{ csrf_field() }}
<div style="background-color: #E8F4FA; text-align: center;">
<input type="text" name="tweet" style="margin: 1rem; padding: 0 1rem; width: 70%; border-radius: 6px; border: 1px solid #ccc; height: 2.3rem;" placeholder="今どうしてる?">
<button type="submit" style="background-color: #2695E0; color: white; border-radius: 10px; padding: 0.5rem;">ツイート</button>
</div>
@if($errors->first('tweet')) <!-- 追加 -->
<p style="font-size: 0.7rem; color: red; padding: 0 2rem;">※{{$errors->first('tweet')}}</p>
@endif
</form>
<div class="tweet-wrapper"> <!-- この辺追加 -->
@foreach($tweets as $tweet)
<div style="padding:2rem; border-top: solid 1px #E6ECF0; border-bottom: solid 1px #E6ECF0;">
<div>{{ $tweet->tweet }}</div>
</div>
@endforeach
</div>
</div>
<script src="{{ mix('js/app.js') }}"></script>
</body>
</html>
まず、バリデーションは、コントローラ$validatorの部分で引っかかったら、ビューの$errors->first('tweet')}
で表示します。if文で、エラーがない場合は表示しないようにしています。
あとは、ツイートをforeach文で回せば、新着順でツイート内容が全部表示されます。
工夫できること(やり方は説明しませんが...)
## ユーザーにアイコンとか名前とか設定してもらう
アイコンとか名前とかをusersテーブルに保存しておけば、もっとツイッターっぽくなります!
ツイートした瞬間にビューにツイート内容を反映させる
今は、ツイート->テーブルに保存->timelineにリダイレクト->あ、ツイートしたやつが表示された
ですが、
こちらも可能です。
ツイート->JavaScriptでビューに追加(append)->テーブルに保存
ツイート内容を先にスライドダウンがなんかで表示させた方が、気持ちいいっすねえ
データが大量になった時にやばい
そうなんです。今ツイートテーブルをそのまま表示しているので、データが大量になった場合に超重くなります。
そんな時は、Laravelのpaginate
とjscroll.jsなどの無限スクロールを実装すれば、
スクロールされた時にコンテンツがローディングされるので、重くなる心配ありません。
(ツイッターとかも、「もっとツイートをみる」)みたいなのありますよね、あれみたいなイメージです!
終わりに
コードを説明していったらわかりにくいかもしれないですね...!
いつでも質問お待ちしております〜
こうしたらもっといいんじゃないっすかっていうのもお待ちしております〜