- 投稿日:2020-08-05T20:16:20+09:00
Laravel+Vue.jsで作成したSPAサイトでOGP対応
はじめに
個人開発でLaravel5.5とVue.jsを使用して作成したSPAサイトがあり、長らく放置していたのですが、最近勉強を兼ねて少しリファクタリングをしようかなと思いました。
上記が作成したサイト(掲示板)になります。
治すべき部分はたくさんあるのですが、まず気になったのがTwitterカードの表示です。
特に、掲示板のスレッドURLをツイートした際に、デフォルトの情報が表示されてしまうのが見た目の部分で致命的でした。
まずやったこと
最初にapiでスレッドの情報を読み込んだ後のタイミングで
querySelector
を使用して動的にmetaタグを変更しました。//APIでデータ取得後 document.title = this.thread_header[0].title + ' | LoveLiveBBS'; document.querySelector("meta[property='og:title']").setAttribute('content', this.thread_header[0].title + ' | LoveLiveBBS'); document.querySelector("meta[property='description']").setAttribute('content', this.thread_response[0]['writing']); document.querySelector("meta[property='og:description']").setAttribute('content', this.thread_response[0]['writing']);当たり前ですが、これでは書き換わる前のmetaが読み込まれてしまうので結果は変わりませんでした。。。
今回は
/thread/[thread_id]
ページのみの修正ということで、その為だけにわざわざプリレンダリングやSSRはしたくないなと思い、以下のような対応を行いました。Laravelのbladeファイルでの対応
/thread/[thread_id]
にアクセスされたときにLaravel側でデータを取得してmetaタグに設定する方法をとりました。bladeファイルの作成
修正前は初回アクセス時にのみ使用する
spa.blade.php
のみが存在していました。
スレッドページ用のthread.blade.php
を作成しました。resources/views/threadPage.blade.php<!DOCTYPE html> <html lang="ja"> <head> //コントローラーで作成したデータを表示 <title>{{ $title }}</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <meta name="csrf-token" content="{!!csrf_token()!!}"> <link href="https://fonts.googleapis.com/css?family=Kosugi+Maru&display=swap&subset=japanese" rel="stylesheet"> //コントローラーで作成したデータを表示 <meta name="description" content="{{ $description }}"> <meta property="og:url" content="https://lovelivebbs.jp" /> //コントローラーで作成したデータを表示 <meta property="og:title" content="{{ $title }}" /> <meta property="og:type" content="website"> //コントローラーで作成したデータを表示 <meta property="og:description" content="{{ $description }}" /> <meta name="twitter:card" content="summary" /> <meta name="twitter:site" content="@lovelivebbs" /> //コントローラーで作成したデータを表示 <meta property="og:site_name" content="{{ $title }}" /> <meta property="og:locale" content="ja_JP" /> </head> <body> <div id="app"> <app></app> </div> <script src="{{ mix('js/app.js') }}"></script> </body> </html>コントローラーにスレッドページ用のメソッドを追加
spa.blade.php
を返す役割だけのコントローラーにthreadPage
メソッドを追加しました。app/Http/Controllers/SpaController.phpnamespace App\Http\Controllers; use Illuminate\Http\Request; use App\Response; use App\Thread; use Exception; use Illuminate\Support\Facades\Log; class SpaController extends Controller { public function index() { return view('spa'); } /** * スレッドページのみmetaタグをbladeファイルで設定 * @param $id * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View */ public function threadPage($id) { $meta = [ 'title' => 'LoveLive!BBS!', 'description' => '当サイトはラブライブシリーズ専用掲示板です。' ]; try { $threadDetail = Thread::where('id', $id)->first(); if (is_null($threadDetail)) { return view('threadPage', $meta); } else { //取得したデータをmetaタグに設定 $response1 = Response::where('thread_id', $id)->first(); $meta['title'] = $threadDetail->title . ' | LoveLive!BBS!'; $meta['description'] = $response1->writing; return view('threadPage', $meta); } } catch (Exception $exception) { Log::error('thread page exception'); return view('spa'); } } }DBから取得した値をまとめてbladeファイルに渡しています。
ルーティングの追加
routes/web.php+ Route::get('/thread/{id}', 'SpaController@threadPage'); Route::get('/{any}', 'SpaController@index')->where('any', '.*');結果
上記の変更を加えた後に、Twitterにスレッドのリンクを張ってみました。
無事、スレッドタイトルと内容が反映されました!???
こうなると次はOGPの画像生成をしたくなりますね。あまり良いやり方ではないかもしれませんが、なんとかなりました。
もっといい方法があったら教えてください。後このサイトですが、残念ながら全然使われてないのでラブライブが好きな方はぜひ書き込みだけでもしてみてください。。。(切実)
ラブライブ専用掲示板
ラブライブ専用掲示板:ABOUTページよろしくお願いします。?
- 投稿日:2020-08-05T20:05:58+09:00
都内の行きつけの居酒屋新規開拓サービスをリリースしたらダダ滑りしたので反省会・考察します【Laravel】
行きつけの居酒屋を新規開拓できるWEBサービスを作ったので振り返ります
初投稿です。
酔いどれ(@yoido_re)です。この記事は先月7月末にプレリリースした個人開発のサービス、東京歩くマップについて、リリースから約1週間が経ったのでリリースまでの道のりから現状について皆さんに宣伝も兼ねてお話します。
東京歩くマップ - TokyoAlc.Map 行きつけの居酒屋を新規開拓しよう!
https://alcmap.tokyo/リリースした東京歩くマップとは
タイトルにも掲げている通り、東京歩くマップとは、東京都内に限定した行きつけの居酒屋を新規開拓するために、ユーザ同士が行きつけの居酒屋を共有し合うユーザ投稿型のサービスです。
背景
なぜ作ったの?
東京歩くマップのトップにも掲げています。
東京歩くマップ(東京Alc.マップ)とは
東京にある星の数ほどの居酒屋から自分好みの居酒屋を見つけるのは至難の技
一度気に入った居酒屋を見つけるとその居酒屋ばかりに固執してしまい、バリエーションが増やせない
レビューサイトで人気のお店には並んでまでして行こうとは思わない
そんな面倒くさがりな酒呑み達のためだけの行きつけの居酒屋共有サービス食べ○グや他の口コミサイトとの差別化
以下2点です。
1点目。
★1の居酒屋を掲載したところで価値がないと考え、そもそも行きつけでない居酒屋はわざわざ掲載しない。本当に行きつけの居酒屋のみを掲載します。2点目。
食べログのように、★星の数(評価)が高い=行きつけとは限らない。ウマいからと言って行きつけの居酒屋になるとは限らず、行きつけとなった背景には店主・店員さんとの素っ気ない会話、お店の入りやすさ等、別のパラメータが存在するのでは?と考察しました。最終的に行き着いたのは、その居酒屋へ行くリピート頻度(画像右上)が高いほど行きつけの居酒屋という定義をしました。
食べログや他の口コミサイトにはない、新たな指標値を見つけサービスの差別化ができたぞ!!!とサービス開発に火が付きました!サービス思想
行きつけの居酒屋を探す方法を考察
行きつけの居酒屋を探す方法として、簡単に思いつくものに以下の手段が挙げられます。
- WEBで探す
- 街をブラブラして偶然入った店を気に入る
- テレビの紹介
- アプリ(食べログ)で評価の良い店を探す
- 友人に聞いて勧めてもらう
上記についてツイッターで主要3つのものについてアンケートを取ると意外な結果になりました。
(全部でアンケート取ればよかったと反省)Twitterの結果から、美味しいお店をアプリで探している人と違い、酒呑み達にとってはその場でサクッと調べたお店に行きたがる傾向にあるのでは?と考察しました。
そこでアプリは断念。WEBサービスとして、酒呑みが集まるプラットフォームを構築しようと発案しました。
加えてTwitterアカウントを用いて投稿者を非匿名にすることで、友人に聞いたかの如く信憑性の高い行きつけの居酒屋を知ることができるのでは??とTwitterアカウントとの連携を採用。ターゲティング(ペルソナ)
個人サービスにおいては、ブルーオーシャンでターゲットが狭ければ狭いほどウケると信じてます。そこで自分も該当する以下の3つをターゲットにします。
- 都内在住
- Twitterやってる人
- 行きつけのお店がない人・行きつけのお店にハマりすぎて新規開拓できない人
SEOキーワードは以下を想定
行きつけ + 居酒屋 +(駅名 | 都内 | 新規開拓)サービス開発
システム構築
バックエンド
- Amazon Lightsail
- Laravel 7.2.x
- PHP 7.3.x
- MySQL
- (Amazon S3はサービスが普及したら利用予定)
- GoogleMapAPI
- TwitterAPI
- 最寄り駅API
フロントエンド
- BootStrap
- Semantic UI
スマホユーザが圧倒的に多いと想定し、スマホサイズに重きを置いたレスポンシブデザインにしました。
実装機能
限りなくスモールスタートです。
機能性≠ユーザの流入数、機能性=再帰率です。どんなに素晴らしい機能を実装してもユーザの目に触れなければ意味がないので、まずはコンセプトに沿った必要最低限の機能を実装しました。
実装機能 概要 Twitter認証 できるだけユーザ登録型のサービスは作りたくないのですが、非匿名での紹介が行きつけの居酒屋への信憑性につながると考えました 居酒屋の投稿機能 Twitter認証でユーザ登録したユーザのみ、居酒屋を登録可能 居酒屋の照会機能 誰でも照会可能 コメント機能&写真投稿機能 活気が出れば良いなと思い、簡単だったので設けました
未実装機能 理由 投稿の編集・削除 必須ではないと判断、ユーザが増えたら実装 マイページ ユーザが増えないうちに実装しても意味がない 投稿画面
GoogleMapAPIを採用し、居酒屋の名前を正規化します。更に住所情報等のメタデータも同時に取得し、最寄駅を算出。
これにより、ユーザの入力を最小限に収めました。照会画面
反省
ユーザが増えない
リリースしてまだまだ浅いので焦るには早いかも知れませんが、以下2点を原因と考察。(≠真の原因)
SEOが弱い
検索流入が0。検索にすらヒットしていない・・・
東京メトロが運営しているトーキョーウォーキングマップとサービス名が類似しているのも反省。自ら検索妨害されに行ってしまいました・・・そもそも”行きつけ”のキーワードで検索する人ってどのくらいいるんだろう・・?
マーケティングできていない
現状、身内・Twitterのフォロワーにしか宣伝できていません。投稿が増えたら広告も考えているのですが、投稿の少ないうちに広告を打っても閲覧者には響かないです。
投稿が増えない
恐らく、作ったサービスの質は悪くない。実際に回りの人に使ってもらうとそこそこの評価は貰えています。
それはあくまでも閲覧者側の目線。投稿者側にメリットがないじゃん!!って意見も多々ありますが、そもそもログインされた形跡がなく、投稿画面にすら飛んでいない!!WHY!!!
投稿したくなるようなサービスでない?
投稿者側に現在の機能では何のメリットもない。投稿者-閲覧者の関係がWin-Winでないとサービスは成り立ちません。そこを全く考えていませんでした。
投稿に対していいね機能を設けるのも一つの手ですが、ユーザが少ないうちにその手は有効なのでしょうか・・・・良かった点
採用した技術はほとんど初めての利用です。初めてのPHPフレームワーク(Laravel)、初めてのLightsail。十分に使いこなせたと実感しています。
5月から着手し、普段は仕事をしつつ、空いた時間のみで3ヶ月という期間サボりなしでリリースできたのは良かったと思います。
(実稼働は1人月ほど?)開発着手前に、最低限必要な機能と2次開発機能を振り分けていた点、リリース時期やマイルストーンを決め打ちしていたことから順調にリリースまで迎えられました。
もともとWEBデザイン大好き人間なので満足行くデザインに仕上がりました。
結果にはつながっていない話ですが、エンジニアは技術に没頭しがちでビジネス思考を忘れがちになります。今回は技術よりも如何にユーザ獲得に繋げられるか、ユーザビリティ向上するにはどうするか、常に意識していました。意識だけしていました。
もちろん結果には(略まとめ
今の所順調に失敗していますが、考案したサービスを無事リリースできたという点で非常に達成感を感じていると同時に自信につなげることができています。
また、実際にユーザが限りなく少ないわけではなく再帰率も高いことから、如何に継続して投稿を続けられるかがサービス継続の決め手になると思っています。
最後に再度、宣伝になりますが、東京歩くマップ、非常に有益な居酒屋情報が投稿されていますので、新規開拓したい方、サービス応援してくださる方は、ぜひ閲覧・投稿よろしくお願いいたします。
東京歩くマップ - TokyoAlc.Map 行きつけの居酒屋を新規開拓しよう!
https://alcmap.tokyo/長々とご視聴ありがとうございました。
普段はLaravelや日常のブログも書いてます。(走り出したたこ焼きの開発)
Twitterもぜひ。(@yoido_re)
- 投稿日:2020-08-05T19:33:48+09:00
[Laravel]環境変数、サーバーから見るか?ファイルから見るか?
Laravelをお使いのみなさんは.envで環境変数を設定しているかと思いますが、もちろんサーバーの環境変数や実行時にコマンドで環境変数を指定することも可能です。
さて、.envとサーバー環境変数で同じキーを指定した場合にLaravelはどちらの値を優先するか、という記事です。
Laravelでの環境変数
Laravelではphpdotenvというライブラリを利用して環境変数を取得しています。
https://github.com/vlucas/phpdotenv以下で各種Adapterが登録されます。
※Laravel v7系
※phpdotenv v4系Illuminate\Support\Envpublic static function getRepository() { if (static::$repository === null) { $adapters = array_merge( [new EnvConstAdapter, new ServerConstAdapter], static::$putenv ? [new PutenvAdapter] : [] ); static::$repository = RepositoryBuilder::create() ->withReaders($adapters) ->withWriters($adapters) ->immutable() ->make(); } return static::$repository; }ここで
EnvConstAdapter
,ServerConstAdapter
,PutenvAdapter
の順に登録しています。Adapter
先程登録されていた各種Adapterは以下のような仕組みになっています。
EnvConstAdapter
PHPのスーパーグローバル変数
$_ENV
から値の取得・登録をします。
$_ENV
は現在のスクリプトに渡された環境変数を取得します。ServerConstAdapter
PHPのスーパーグローバル変数
$_SERVER
から値の取得・登録をします。
$_SERVER
はサーバーに定義された環境変数を取得します。PutenvAdapter
PHPの組み込み関数
getenv
とputenv
を利用して値の取得・登録をします。環境変数の取得
env('string')
が呼び出される先は以下のように先程登録したAdapterを順に参照して、定義されている場合に値を返します。
定義がなければnullが返されます。Dotenv\Repository\AdapterRepositoryprotected function getInternal($name) { foreach ($this->readers as $reader) { $result = $reader->get($name); if ($result->isDefined()) { return $result->get(); } } return null; }結局優先順位はどうなってるの?
.envファイルが登録される際の挙動を見るとわかります。
Dotenv\Repository\AbstractRepositorypublic function set($name, $value = null) { if (!is_string($name)) { throw new InvalidArgumentException('Expected name to be a string.'); } // Don't overwrite existing environment variables if we're immutable // Ruby's dotenv does this with `ENV[key] ||= value`. if ($this->immutable && $this->get($name) !== null && $this->loaded->get($name)->isEmpty()) { return; } $this->setInternal($name, $value); $this->loaded->set($name, ''); }ここのガート節で
if ($this->immutable && $this->get($name) !== null && $this->loaded->get($name)->isEmpty()) { return; }とありますね。
つまり、EnvConstAdapter
,ServerConstAdapter
,PutenvAdapter
のどれらにも未定義の場合は値を新たに追加しています。なので優先される値は
- 実行時の環境変数
- サーバー環境変数
- .envファイルの変数
になるかと思います。(違ったらご指摘ください)
- 投稿日:2020-08-05T18:12:23+09:00
[Laravel]Composerエラー 「Do not run Composer as root/super user!〜」
Composerを使ってライブラリを使ってインストールをしたとことろ以下のエラーが出ました。
Do not run Composer as root/super user! See https://getcomposer.org/root for detailscomposerを使ったインストールをrootユーザーでしようとしたため、エラーが出たみたいです。
なぜrootユーザーでインストールは好ましくないかについては以下を参照してください。
https://getcomposer.org/doc/faqs/how-to-install-untrusted-packages-safely.mdroot以外のユーザーを作成します。Linuxのコマンドの
useradd
でアカウントを設定します。// rootユーザーになる $ sudo su - // ユーザーを追加 $ useradd -m 名前 // 追加したユーザーに移動 $ su 名前 名前$ composer コマンドこれでroot以外のユーザーになったため、composerでインストールができるようになりました
- 投稿日:2020-08-05T17:23:27+09:00
プロフィールの更新履歴を保存する仕組みを作るにはどのようにしたら良いのか。手順をまとめてみた。(自分用)
はじめに
※ Profile画面を作っていますので、profile(自分用)として色々記載しています。
実際のアプリケーション開発はある機能が1つのテーブルを使うだけで完結することは、ほとんどありません。
複数のテーブルを使ってシステムを実現します。
リレーショナルデータベースにはこのようなテーブル同士の関連を扱う機能が備わっており、
Eloquentで扱うことができます。
編集履歴は「Profileをいつ変更したか」「日付と時刻を記録し、参照することができる」機能のことです。編集画面でデータを更新するタイミングで histories というテーブルにデータを登録し、
編集画面でその一覧を見られるように実装します。では、流れに沿って記述していきます。
1.編集履歴テーブルの作成と関連付け
Migrationファイルの雛形を作成します。
$ php artisan make:migration create_histories_tableMigrationファイルを次のように編集いたします。
<?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateHistoriesTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('histories', function (Blueprint $table) { $table->increments('id'); $table->integer('profile_id'); $table->string('edited_at'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('histories'); } } ?>Migrationを実行します。
$ php artisan migrateModelの雛形を作成します。
$ php artisan make:model Historyapp/History.php で History Modelを下記のように実装します。
<?php namespace App; use Illuminate\Database\Eloquent\Model; class History extends Model { protected $guarded = array('id'); public static $rules = array( 'profile_id' => 'required', 'edited_at' => 'required', ); } ?>Profile Modelとの関連を定義するために、app/Profile.php へ以下の内容を追記します。
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Profile extends Model { protected $guarded = array('id'); public static $rules = array( 'name' => 'required', 'gender' => 'required', 'hobby' => 'required', 'introduction' => 'required', ); // Profileモデルに関連付けを行う public function histories() { return $this->hasMany('App\History'); } } ?>Profileモデルに関連付けを定義することで、
Profileモデルから $profile->histories() のような記述で簡単にアクセスすることができます。
2.編集履歴の記録と参照
ProfileController の update Actionで、Profile Model を保存するタイミングで、
同時にHistory Model にも編集履歴を追加するように実装します。update Action を次のように変更。
<?php public function update (Request $request) { // validationをかける $this->validate($request, Profile::$rules); // Profile Model から データを取得する $profile = Profile::find($request->id); // 送信されてきたフォームデータを格納する $profile_form = $request->all(); unset($profile_form['_token']); // 該当するデータを上書きして保存する $profile->fill($profile_form)->save(); retern redirect('admin/profile'); } ?>3.Viewを実装する
resources/views/admin/profile/edit.blade.php に以下を記述します。
{{-- layouts/profile.blade.phpを読み込む --}} @extends('layouts.profile') {{-- profile.blade.phpの@yield('title')にProfileの編集'を埋め込む --}} @section('title', 'Profileの編集') {{-- profile.blade.phpの@yield('content')に以下のタグを埋め込む --}} @section('content') <div class="container"> <div class="row"> <div class="col-md-8 mx-auto"> <h2>MyProfile編集画面</h2> <form action="{{ action('Admin\ProfileController@update') }}" method="post" enctype="multipart/form-data"> @if (count($errors) > 0) <!-- errors内に個数があるならば配列中の個数を返す --> <ul> @foreach($errors->all() as $e) <!-- $errors内をループし、その中身を$eで表示する --> <li>{{ $e }}</li> @endforeach </ul> @endif <div class="form-group row"> <label class="col-md-2">氏名</label> <div class="col-md-10"> <textarea class="form-control" name="name" rows="1">{{ old('body') }}</textarea> </div> </div> <div class="form-group row"> <label class="col-md-2">性別</label> <div class="col-md-10"> <input type="radio" name="gender" value="male">男性 <input type="radio" name="gender" value="male">女性 </div> </div> <div class="form-group row"> <label class="col-md-2">趣味</label> <div class="col-md-10"> <textarea class="form-control" name="hobby" rows="10">{{ old('body') }}</textarea> </div> </div> <div class="form-group row"> <label class="col-md-2">自己紹介欄</label> <div class="col-md-10"> <textarea class="form-control" name="introduction" rows="12">{{ old('body') }}</textarea> </div> </div> <input type="hidden" name="id" value="{{ $profile_form->id }}"> {{ csrf_field() }} <input type="submit" class="btn btn-primary" value="更新"> </form> </div> </div> </div> @endsection次に Routing を実装する
routes/web.php に追加するRouting設定です。<?php Route::get('profile/edit', 'Admin\ProfileController@edit'); Route::post('profile/edit', 'Admin\ProfileController@update'); ?>編集画面に実際にブラウザでアクセスしてみましょう。
一覧画面にある表の1番右側に、編集リンクがあります。
このリンクから編集画面に遷移し、実際にデータが編集できるか試してみましょう。4.データの削除
データの削除は、データの更新と同じくモデルを取り出してから削除を指示します。
5.Controller を実装する
ProfileController に delete Action を追加してください。
<?php public function delete(Request $requsest) { // 該当する Profile Modelを取得 $profile = Profile::find($request->id); // 削除する $profile->delete(); return redirect('admin/profile'); } ?>次はデータの削除について考えていきます。
一般的に削除に対応するアクション名はdeleteになります。データをセーブするときは、$profile->save(); で save メソッドを利用しましたが、
データの場合はdelete()メソッドを使います。6.Routing を実装する
routes/web.php に追加する Routing 設定です。
Route::get('profile/delete', 'Admin\ProfileController@delete');削除機能は画面を持たず、id で指定されたモデルをすぐに削除します。
コントローラの最後の画面で一覧画面にリダイレクトしているため、
削除機能を持っていません。そのため、ビューテンプレートは不要となる。
今回は以上です!
完全自分用のノートになってしましたすいません。
それでは
明日は今日以上のパフォーマンスを٩( ᐛ )و
- 投稿日:2020-08-05T17:03:44+09:00
MAMPでLaravelのルートの設定をどこにするか
Laravelのルートを最初はLaravelで作成したアプリ名のディレクトリにしていました。一番上のディレクトリ。
そこに設定していると、LaravelmのRouteでURLを設定してもNOT FOUNDになってパニック。
実は
public
にドキュメントルートを設定しないといけないよう。
Desktop/blog/public
という感じ。
The requested URL /blog/1 was not found on this server.
のようなエラーが出てきたら確認してみよう!
- 投稿日:2020-08-05T14:26:49+09:00
【Laravel】ルート定義で使う「get」「post」「put」「putch」「delete」を使い分けるためにすること
まずは、ルート定義の「HTTP動詞」についておさらい。
Laravelでは、複数のHTTP動詞に対応したルートを登録することができます。例えば、
routes/web.php
にroutes/web.phpRoute::get('/', 'SampleController@get'); Route::post('/', 'SampleController@post'); Route::put('/', 'SampleController@put'); Route::patch('/', 'SampleController@patch'); Route::delete('/', 'SampleController@delete');と、このような感じで、同じURIでも
get
,post
,put
,patch
,delete
の複数のHTTP動詞に振り分けてルートを定義することが可能になります。上記の例では、いずれのルートも
/
がマッチするURIとなっていますが、Route::
の次にくるHTTP動詞によって、適応されるルートが異なってきます。GET動詞が付くルートにアクセスするには
GET動詞が付くルートにアクセスするには、普通にブラウザからURLを入力すると、GETリクエストでのアクセスとなりますので、GET動詞の付くルートが適応されます。フォームを使用しなければ、だいたいGETリクエストのアクセスなので想像しやすいと思います。普通にaタグのリンクで遷移するのもGETリクエストです。
POST動詞が付くルートにアクセスするには
POST動詞が付くルートにアクセスするには、HTMLフォームの
method
要素にPOST
を入力して送信してください。<form action="/foo/bar" method="POST"> <input type="text" name="hitokoto"> <input type="hidden" name="_token" value="{{ csrf_token() }}"> </form>上記のようにフォームタグの
method
の値がPOST
になっています。これによってPOST動詞が定義されているルートが適応されます。GET&POST動詞以外のHTTP動詞が付くルートにアクセスするには
HTMLフォームは
method
要素に、put
,patch
,delete
をサポートしていません。つまり、フォームタグのmethod
にput
,patch
,delete
を入力することができません。なので、フォームに_method
隠しフィールドを追加して、疑似的にput
,patch
,delete
のリクエストを表現します。<form action="/foo/bar" method="POST"> <input type="text" name="hitokoto"> <input type="hidden" name="_method" value="PUT"> <input type="hidden" name="_token" value="{{ csrf_token() }}"> </form>ちなみに、上記コードをさらにシンプルにすることもできます。
_method
フィールドを、@method
Bladeディレクティブに置き換えても良いでしょう。<form action="/foo/bar" method="POST"> <input type="text" name="hitokoto"> @method('PUT') @csrf </form>少しすっきりしたコードになりました(=゚ω゚)ノ
- 投稿日:2020-08-05T12:55:30+09:00
ラズパイで部屋の見える化してみる、その2
はじめに
前回から大分時間が空いてしまいましたが、社内新卒LTで発表したラズパイとLaravelを使ったセンサーデータの可視化の続きを書きます。
前回のラズパイで部屋の見える化してみる、その1ではセンサーからデータの取得とcsvへの保存を行いました。
今回はLravelでAPI作ってデータベースにデータを保存までを書きます。
LaravelやMVCモデルについて詳しく解説というより、やったことを整理して残しておくためのメモ書きのつもりです。手順
前回の記事を参照すると
- センサーからデータの取得&処理
- 液晶に表示
- csvに保存
- クラウドにデータを投げる
- DBに保存
- webページ上で表示
となっているので今回は4~5を実装していきます。
環境構築
研修でLaravelというものを初めて触ったのでLaravelでバックエンドを作っていきます。
基本的にVSCode の Remote - SSH機能を使ってConoha VPS上でLaravelの開発環境を用意する の説明どおりでLaravelの環境をセットアップし、マイグレーションまでできると思います。作成したインスタンスのIPアドレスに接続するとLaravelのwelcomeページが表示されます。
データベースの設定
マイグレーション
温湿度センサーのデータを格納するためのDBを用意します。直接DBを叩くのではなくLaravelのmigration機能を使ってテーブルを作成します。
まず、次のコマンドでマイグレーションファイルを作成します。php artisan make:migration create_sensors_table --create=sensors
database/migrations/
にマイグレーションファイルが生成されるのでpublic function up()
の中に以下のように追加します。Schema::create('sensors', function (Blueprint $table) { $table->increments('id'); $table->float('temperature'); $table->float('pressure'); $table->float('humidity'); $table->integer('sensor_id'); $table->timestamps(); });
php artisan migrate
コマンドでマイグレーションが実行されます。
MySQLにログインして確認してみます。MariaDB [(none)]> use laravel; Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Database changed MariaDB [laravel]> show tables; +-------------------+ | Tables_in_laravel | +-------------------+ | failed_jobs | | migrations | | sensors | | users | +-------------------+ 4 rows in set (0.00 sec) MariaDB [laravel]> desc sensors; +-------------+------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------------+------------------+------+-----+---------+----------------+ | id | int(10) unsigned | NO | PRI | NULL | auto_increment | | temperature | double(8,2) | NO | | NULL | | | pressure | double(8,2) | NO | | NULL | | | humidity | double(8,2) | NO | | NULL | | | sensor_id | int(11) | NO | | NULL | | | created_at | timestamp | YES | | NULL | | | updated_at | timestamp | YES | | NULL | | +-------------+------------------+------+-----+---------+----------------+ 7 rows in set (0.00 sec)Sensorsテーブルとカラムが追加されていることが確認できます。
モデルの作成
テーブルが作成されたのでLaravelからDBへアクセスできるようModelクラスを作成します。
以下のコマンドでモデルを作成します。php artisan make:Sensorappディレクトリに
Sensor.php
が作られるので、ファイルの中身をSensorsテーブルに合わせて次のように編集します。<?php namespace App; use Illuminate\Database\Eloquent\Model; class Sensor extends Model { protected $fillable = [ 'temperature', 'pressure', 'humidity', 'sensor_id', ]; }これでDBと連携してデータを保存できるようになりました。
LaravelではModelクラスを呼び出すことでDBへデータを挿入したり取り出したりすることができます。
初めて触ったときはこの辺の動きがよく分からず、とりあえずコピペして動かしたら動いた状態でした。データを受け取るためのAPIを作る
今回はラズパイで収集したセンサーデータを受け取ってDBへ格納するだけなので、POSTで値を受け取る実装にします。
コントローラの作成
実際にデータを受け取ってモデルを操作するコントローラを作成します。
php artisan make:controller SensorController
app/Http/Controllers/SensorController.php
が作成されます。
このファイルにリクエストの処理を書きます。<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Sensor; class SensorController extends Controller { public function store(Request $request) { $sensor = Sensor::create([ 'temperature' => $request->input('temperature') , 'pressure' => $request->input('pressure'), 'humidity' => $request->input('humidity'), 'sensor_id' => $request->input('sensor_id'), ]); return response()->json($sensor, 201); } }先程作ったModelクラスを
use App\Sensor;
で呼び出しています。
Sensor::create
でDBへ指定した値を保存しています。
理解してしまえばシンプルで扱いやすいなと思いました。ルーティングを設定
APIのルーティングを設定します。
LaravelではAPIのルーティングはroutes/api.php
に書きます。<?php use Illuminate\Http\Request; use Illuminate\Support\Facades\Route; Route::middleware('api')->group(function () { Route::post('sensors', 'SensorController@store'); });ルーティングではリクエストのメソッド、URI、呼び出すコントローラーを指定します。
ここではメソッドがpost、URIはsensors、コントローラーはSensorController@storeを指定しています。
実際にAPIを叩くときはIPアドレス/api/sensors
にPOSTすることでapp/Http/Controllers/SensorController.php
のstoreメソッドが実行されます。
php artisan route:list
コマンドでルーティングを確認することができます。php artisan route:list +--------+----------+-------------+------+---------------------------------------------+------------+ | Domain | Method | URI | Name | Action | Middleware | +--------+----------+-------------+------+---------------------------------------------+------------+ | | GET|HEAD | / | | Closure | web | | | POST | api/sensors | | App\Http\Controllers\SensorController@store | api | +--------+----------+-------------+------+---------------------------------------------+------------+POSTしてみる
ここまででAPIはできたのでラズパイからPOSTできるか確認してみます。
確認のために簡単なプログラムを書きました。import time import random import json import urllib.request url = `http://YourIPAddress/api/sensors` def post_data(temp, press, hum): data = { 'temperature' : temp, 'pressure' : press, 'humidity' : hum, 'sensor_id' : 6, } headers = { 'Content-Type': 'application/json', } req = urllib.request.Request(url, json.dumps(data).encode(), headers) try: urllib.request.urlopen(req) except urllib.error.URLError as e: print(e.reason) while True: temp = random.uniform(10, 30) press = random.uniform(980, 1010) hum = random.uniform(50, 80) post_data(temp, press, hum) print("temp={0}, press={1}, hum={2}".format(temp, press, hum)) time.sleep(1)POSTリクエストはurllibを使って投げるようにします。
ここではPythonのrandom関数を使ってセンサーの値を擬似的に生成して1秒間隔で送信するようにしました。
MySQLにログインして実際にPOSTできたことを確認します。ここまで実装できると、前回の記事のセンサーデータの取得と組み合わせたらいい感じに表示できそうな気がしてきますね!
終わりに
今回はCONOHA上にインスタンスを立ててLaravelでAPIを作成からデータをPOSTしてDBへ保存するところまでを実装しました。
本当はデータの表示まで書きたかったのですが思ったより長くなったので次回に書こうと思います。
フロントエンド?知らない子ですね。。。参考にした記事
- 投稿日:2020-08-05T11:32:38+09:00
xampp使用時にTNTSearchをインストールしようとしてエラー
状況
xamppを使用してLaravel7でアプリを作成し、Scout全文検索に使うTNTSearchをコンポーザーからインストールしようとしたら、エラーが出た。
XAMPP for Windowsのバージョンは 7.3.10コマンドプロンプトPC >> composer require teamtnt/tntsearch Using version ^2.3 for teamtnt/tntsearch ./composer.json has been updated Loading composer repositories with package information Updating dependencies (including require-dev) Your requirements could not be resolved to an installable set of packages. Problem 1 - Installation request for teamtnt/tntsearch ^2.3 -> satisfiable by teamtnt/tntsearch[v2.3.0]. - teamtnt/tntsearch v2.3.0 requires ext-sqlite3 * -> the requested PHP extension sqlite3 is missing from your system. To enable extensions, verify that they are enabled in your .ini files: - C:\xampp\php\php.ini You can also run `php --ini` inside terminal to see which files are used by PHP in CLI mode. Installation failed, reverting ./composer.json to its original content解決法
C:\xampp\php\php.iniの
- extension=pdo_sqlite (927行目)
- extension=sqlite3 (938行目)
のコメントアウトを外すと、いけた。
php.ini自体は重要な設定ファイルなので、編集前にphp.iniのバックアップファイルの作成を行うことをお勧めします。
参考
- 投稿日:2020-08-05T09:57:42+09:00
SkyWayによるビデオ・音声通話の技術概要
この記事は「マイスター・ギルド:暑中見舞!夏のアドベントカレンダー2020」3日目の記事です。
初めに...
コロナの流行が始まったとき、「Stay Home」対策で自宅に閉じ込められたとき、ITワーカーの私たちは特に自宅からリモートで仕事をすることが可能でラッキーでした。
残念ながら、私たちのほとんどはリモートでの作業に慣れておらず、最初は上司、同僚、お客さん等とリモート通信が特に困難でした。
特殊なツールを使用しても改善されましたが、同じオフィスで作業するほど自然ではありません。
弊社のMeister Guildでもその新しい作業環境に答えるツールを探して色々なツールを使ってみた:
- Zoom:ヴァーチャル背景を使える
- Discord:完全に無料
- Remo:ヴァーチャルルームに入れる、共有ホワイトボードもある
- Spatial Chat:距離によると声の高さが変わる
ビデオ会議ができるツールはほかにもあります:
各ツールが得点と弱点を持つけど「これ!」ってなるツールがなかったので「私たちの理想なビデオ会議のツール作れるかな?」と思って調査することになりました。
ビデオ会議のツール作りの調査
そのようなツールを一から開発するのはとても大変な仕事になるので、開発をスピードアップするWebRTCフレームワークを探しました。
日本製で無料プランあり、NTTコミュニケーションズが作成したWebRTCフレームワークを見つけました:
ユーザー認証をテストするために、認証付きのLaravelアプリケーションを作成し、ユーザーのメールをbase64でエンコーディングしてPeerIDとして使用しました。SkyWayとは
ホームページによるとSkyWayは:
ビデオ・音声通話の機能をアプリケーションに簡単に実装できる、
マルチプラットフォームなSDK & フルマネージドなAPIサービスです。無料プランで下記のSDKを使える:
- Javascript SDK
- iOS SDK
- Android SDK
- WebRTC Gateway
- APIキー認証有料プランで録音SDKと管理APIも使えるんですが例えば録音も録画も普通のMedia Capture and Streams API (Media Streams)でできる。
Javascriptサンプル:
ビデオ会議
P2Pビデオ通話
P2Pテキスト通話
録音と録画
P2Pビデオ通話
通話の相手は一人です。
基本
CDNからSDKをインポートする:
headタグ内<script src="https://cdn.webrtc.ecl.ntt.com/skyway-latest.js"></script>カメラ映像を表示するvideo要素を追加する:
bodyタグ内<video id="my-video" width="400px" autoplay muted playsinline></video>カメラ映像・マイク音声を取得する:
bodyタグ下部のscriptタグ内let localStream; // カメラ映像取得 navigator.mediaDevices.getUserMedia({video: true, audio: true}) .then( stream => { // 成功時にvideo要素にカメラ映像をセットし、再生 const videoElm = document.getElementById('my-video') videoElm.srcObject = stream; videoElm.play(); // 着信時に相手にカメラ映像を返せるように、グローバル変数に保存しておく localStream = stream; }).catch( error => { // 失敗時にはエラーログを出力 console.error('mediaDevice.getUserMedia() error:', error); return; });通話の相手のは「peer」と呼ばれてる。
通話出来るように自分のPeerオブジェクトを作成して相手のPeerオブジェクトと繋がる。Peerオブジェクトの作成
Peerオブジェクトを作成するときに引数のIDを渡さない場合はランダムなIDが生成される:
scriptタグ内const peer = new Peer({ key: '<SkyWayのAPIキー>', debug: 3 });PeerオブジェクトのIDは
peer.id
で取得できる。またはメールアドレスなどからIDを生成できる。
例えばLaravelのコントローラーでbase64
にエンコード:app/Http/Controllers/Controller.phppublic function index() { $user = Auth::user(); return view('videochat',['user'=>['email'=>rtrim(base64_encode($user->email),"=")]]); }ページでPeerオブジェクトに渡す:
scriptタグ内const peer = new Peer('{{$user['email']}}',{ key: '<SkyWayのAPIキー>', debug: 3 });発信
相手のカメラ映像を表示するvideo要素を追加する:
bodyタグ内<video id="their-video" width="400px" autoplay muted playsinline></video>相手へ発信してリスナーで接続することを待つ:
scriptタグ内// 発信処理 const mediaConnection = peer.call('<相手のPeerID>', localStream); setEventListener(mediaConnection);接続ができたときにビデオ要素を設定する:
scriptタグ内let remoteStream; // イベントリスナを設置する関数 const setEventListener = mediaConnection => { mediaConnection.on('stream', stream => { // video要素にカメラ映像をセットして再生 const videoElm = document.getElementById('their-video') videoElm.srcObject = stream; remoteStream = stream; videoElm.play(); }); }着信
相手側はPeerオブジェクトのcallイベントを待って着信の時ビデオ要素を設定する:
scriptタグ内//着信処理 peer.on('call', mediaConnection => { mediaConnection.answer(localStream); setEventListener(mediaConnection); });映像・音声はオン・オフ等
マイク音声オフ
ミュートする:
scriptタグ内localStream.getAudioTracks().forEach(track => track.enabled = false);音声オフ
相手の音声を削音する:
scriptタグ内remoteStream.getAudioTracks().forEach(track => track.enabled = false);※ 音全部消したいときマイク音声もオフしなければならない。
カメラ映像オフ
カメラの映像を消す:
scriptタグ内localStream.getVideoTracks().forEach(track => track.enabled = false);反響キャンセリング
反響を消す:
scriptタグ内localStream.getAudioTracks().forEach(track => { let constraints = track.getConstraints(); constraints.echoCancellation = true; track.applyConstraints(constraints); });ビデオ会議
ビデオ会議はビデオ通話との違いは2つ:
- 相手の数は一人以上になる
- roomオブジェクトで他のユーザーの存在(presence)が確認できる基本
CDNからSDKをインポートする:
headタグ内<script src="https://cdn.webrtc.ecl.ntt.com/skyway-latest.js"></script>カメラ映像を表示するvideo要素を追加する:
bodyタグ内<video id="js-local-stream"></video>カメラ映像・マイク音声を取得する:
bodyタグ下部のscriptタグ内// カメラ映像取得 const localStream = await navigator.mediaDevices .getUserMedia({ audio: true, video: true, }) .catch(console.error); // Render local stream const localVideo = document.getElementById('js-local-stream'); localVideo.muted = true; localVideo.srcObject = localStream; localVideo.playsInline = true; await localVideo.play().catch(console.error);相手達のカメラ映像を表示する要素を追加する:
bodyタグ内<div class="remote-streams" id="js-remote-streams"></div>Peerオブジェクトの作成
PeerオブジェクトのIDを生成する。
例えばLaravelのコントローラーでbase64
にエンコード:app/Http/Controllers/Controller.phppublic function index() { $user = Auth::user(); return view('videochat',['user'=>['email'=>rtrim(base64_encode($user->email),"=")]]); }ページでPeerオブジェクトに渡す:
scriptタグ内const peer = new Peer('{{$user['email']}}',{ key: '<SkyWayのAPIキー>', debug: 3 });roomオブジェクト
Peerオブジェクトが生成された後でroomに参加する:
scriptタグ内peer.on('open', () => { const room = peer.joinRoom('test', { mode: 'sfu', stream: localStream, }); });※ roomは2つのタイプがある:
'sfu'
(通信がサーバーを通す)と'mesh'
(通信が直接にPeerへ発信する)。roomのイベント
open
roomに入ったとき:
scriptタグ内room.once('open', () => { ... });close
roomを出たとき:
scriptタグ内room.once('close', () => { // テキスト通信を止める sendTrigger.removeEventListener('click', onClickSend); // 相手達のビデオストリームを止める Array.from(remoteVideos.children).forEach(remoteVideo => { remoteVideo.srcObject.getTracks().forEach(track => track.stop()); remoteVideo.srcObject = null; remoteVideo.remove(); }); });peerJoin
一人がroomに入ったとき:
scriptタグ内room.on('peerJoin', peerId => { ... });peerLeave
一人がroomを出たとき:
scriptタグ内room.on('peerLeave', peerId => { // ストリームを閉じてvideo要素を消す const remoteVideo = remoteVideos.querySelector( `[data-peer-id=${peerId}]` ); remoteVideo.srcObject.getTracks().forEach(track => track.stop()); remoteVideo.srcObject = null; remoteVideo.remove(); });stream
roomに入った一人のストリームを表示:
scriptタグ内room.on('stream', async stream => { // video要素を生成 const newVideo = document.createElement('video'); newVideo.srcObject = stream; newVideo.playsInline = true; // peerLeaveイベントのときにストリームを見つけるためにpeerIdを付ける newVideo.setAttribute('data-peer-id', stream.peerId); remoteVideos.append(newVideo); await newVideo.play().catch(console.error); });data
メッセージを着信したとき:
scriptタグ内room.on('data', ({ data, src }) => { // メッセージと発信者を表示 messages.textContent += `${src}: ${data}\n`; });テキスト発信
メッセージを発信するとき:
scriptタグ内sendTrigger.addEventListener('click', onClickSend); function onClickSend() { // websocketでroomの皆さんにメッセージを起こる room.send(localText.value); // メッセージと発信者を表示 messages.textContent += `${peer.id}: ${localText.value}\n`; // インプットを消す localText.value = ''; } });映像・音声はオン・オフ等
マイク音声オフ
ミュートする:
scriptタグ内localStream.getAudioTracks().forEach(track => track.enabled = !audioInStatus);音声オフ
相手の音声を削音する:
scriptタグ内remoteVideos.forEach(video => { video.srcObject.getAudioTracks().forEach(track => track.enabled = !audioOutStatus); });※ 音全部消したいときマイク音声もオフしなければならない。
カメラ映像オフ
カメラの映像を消す:
scriptタグ内localStream.getVideoTracks().forEach(track => track.enabled = false);反響キャンセリング
反響を消す:
scriptタグ内localStream.getAudioTracks().forEach(track => { let constraints = track.getConstraints(); constraints.echoCancellation = true; track.applyConstraints(constraints); });音声通話
navigator.mediaDevices.getUserMedia()
の引数でメディアのタイプ(画像・音声・両方)等を選択できる:scriptタグ内// カメラ映像取得 const localStream = await navigator.mediaDevices .getUserMedia({ audio: true, video: false, })画面共有
scriptタグ内const stream = await navigator.mediaDevices.getDisplayMedia({ video: true });このメディアストリームを使って画面共有機能が実現できる。
終わりに...
WebRTCを使用できるのでウェブアプリケーションの元に使用できるフレームワークと思いました。
その調査をして私の理想なリモートワークのツールについていっぱいなアイデアが生まれて社長が本気で開発始めようのは本気になって欲しいです。
参考
- 投稿日:2020-08-05T09:56:53+09:00
Laravel初心者に知って欲しいルーティングの見やすい書き方とあれこれ
ルーティング綺麗に書いた方が見やすくない?
Laravelでいう
routes/web.php
のこと
初学者のコード見てるともう少しすっきり書けるのになー
そもそも使い方があんまり分からないのかな?と思い記事にする事にしましたそもそもどういう仕組み
HTTP動詞
postだったりgetだったりresourceとかその時のHTTP動詞を指定
URLのパス
定義した文字にアクセスした時に適用されるルート
コントローラー名
app/Http/Controllers/の中で定義したいコントローラーを指定
メソッド名
上記で設定したコントローラー内のメソッドを指定
早速見やすい書き方講座
一般的な例としてユーザーが一般ユーザーと管理者で分かれており、それぞれにTOPページとCRUDページ(投稿・一覧・更新・削除・あと詳細)があるという構成です
良くある例
web.php// 一般ユーザー Route::group(['middleware' => 'auth:user'], function() { Route::get('home', 'User/HomeController@index')->name('user.home.index'); Route::get('post', 'User/PostController@index')->name('user.post.index'); Route::get('post/show/{id}', 'User/PostController@show')->name('user.post.show'); Route::get('post/create', 'User/PostController@create')->name('user.post.create'); Route::post('post/store', 'User/PostController@store')->name('user.post.store'); Route::get('post/edit/{id}', 'User/PostController@edit')->name('user.post.edit'); Route::post('post/update/{id}', 'User/PostController@update')->name('user.post.update'); Route::post('post/destroy/{id}', 'User/PostController@destroy')->name('user.post.destroy'); }); // 管理者ユーザー Route::group(['middleware' => 'auth:admin'], function() { Route::get('admin/home', 'Admin/HomeController@index')->name('admin.home.index'); Route::get('admin/post', 'Admin/PostController@index')->name('admin.post.index'); Route::get('admin/post/show/{id}', 'Admin/PostController@show')->name('admin.post.show'); Route::get('admin/post/create', 'Admin/PostController@create')->name('admin.post.create'); Route::post('admin/post/store', 'Admin/PostController@store')->name('admin.post.store'); Route::get('admin/post/edit/{id}', 'Admin/PostController@edit')->name('admin.post.edit'); Route::post('admin/post/update/{id}', 'Admin/PostController@update')->name('admin.post.update'); Route::post('admin/post/destroy/{id}', 'Admin/PostController@destroy')->name('admin.post.destroy'); });何だか全体的に記述が多いですよね...
これをもう少し見やすくしていきましょう。
今回提唱したい例
web.php// 一般ユーザー Route::middleware('auth:user')->namespace('User')->name('user.')->group(function() { Route::resource('home', 'HomeController', ['only' => 'index']); Route::resource('post', 'PostController'); }); // 管理者ユーザー Route::middleware('auth:admin')->namespace('Admin')->prefix('admin')->name('admin.')->group(function() { Route::resource('home', 'HomeController', ['only' => 'index']); Route::resource('post', 'PostController'); });かなりすっきりしました!
仕組みを解説していきます。ルートグループのメソッドチェーン
良く見られる配列で指定するルートグループの書き方は実はLaravel5.3までの書き方で、
それ以降は->
メソッドチェーンで定義するのが主流です!
古い例
web.phpRoute::group(['prefix' => 'admin', 'middleware' => 'auth:admin'], function () { // });
新しい例
web.phpRoute::prefix('admin')->middreware('auth:admin')->group(function () { // });名前空間
ルートグループの
namespace
を使用する事でそのグループ内に定義したルート全てに名前空間が適用されます。
そのためわざわざルート毎にAdmin/
と付ける必要がなくなります。
古い例
web.phpRoute::group(['middleware' => 'auth:admin'], function() { Route::get('admin/home', 'Admin/HomeController@index')->name('admin.home.index'); });
新しい例
web.phpRoute::middleware('auth:admin')->namespace('Admin')->group(function() { Route::get('admin/home', 'HomeController@index')->name('admin.home.index'); });ルートプレフィックス
ルートグループの
prefix
を使用する事でそのグループ内に定義したルート全てにURIを指定することができます。
そのためわざわざルート毎にURIのパスをadmin/
と付ける必要がなくなります。
古い例
web.phpRoute::group(['middleware' => 'auth:admin'], function() { Route::get('admin/home', 'Admin/HomeController@index')->name('admin.home.index'); });
新しい例
web.phpRoute::middleware('auth:admin')->namespace('Admin')->prefix('admin')->group(function() { Route::get('home', 'HomeController@index')->name('admin.home.index'); });ルート名
ルート名使ってますか??
意外と便利なので使うようにしましょう!説明はかずへいさんの記事に任せます!
Laravel使うならrouteには名前をつけたほうが良いルートグループの
name
を使用する事でそのグループ内に定義したルート全てにルート名を指定することができます。
そのためわざわざルート毎にnameをadmin.
と付ける必要がなくなります。
古い例
web.phpRoute::group(['middleware' => 'auth:admin'], function() { Route::get('admin/home', 'Admin/HomeController@index')->name('admin.home.index'); });
新しい例
web.phpRoute::middleware('auth:admin')->namespace('Admin')->prefix('admin')->name('admin.')->group(function() { Route::get('home', 'HomeController@index')->name('home.index'); });resource
多分初学者の方は一番ここに興味持たれたのではないでしょうか。
resourceはリソースコントローラーと呼ばれ、一般的なCRUD(投稿・一覧・更新・削除・あと詳細)操作を予めLaravel側が用意してくれるというものです。web.phpではHTTP動詞を
Route::resource
とする事で以下のメソッドを定義した扱いとなります。
- index(一覧)
- show(詳細)
- create(作成画面)
- store(作成処理)
- edit(編集画面)
- update(編集処理)
- destroy(削除処理)
そのためこの様に行を短縮出来たんですね!
古い例
web.phpRoute::get('admin/post', 'Admin/PostController@index')->name('admin.post.index'); Route::get('admin/post/show/{id}', 'Admin/PostController@show')->name('admin.post.show'); Route::get('admin/post/create', 'Admin/PostController@create')->name('admin.post.create'); Route::post('admin/post/store', 'Admin/PostController@store')->name('admin.post.store'); Route::get('admin/post/edit/{id}', 'Admin/PostController@edit')->name('admin.post.edit'); Route::post('admin/post/update/{id}', 'Admin/PostController@update')->name('admin.post.update'); Route::post('admin/post/destroy/{id}', 'Admin/PostController@destroy')->name('admin.post.destroy');
新しい例
web.phpRoute::resource('post', 'PostController');また
resource
はname()も勝手に付与してくれるため、その煩わしさもなくなります!
とっても便利ですね!最後に
今回は短く書くことで興味を引く作戦でしたが、短ければ良いコードというわけではない。
読みやすく、改修が容易に済むコードを心掛けましょう()