- 投稿日:2020-02-20T21:52:04+09:00
Laravelでミニブログを作ろう #1
laravelの使い方を一通り習ったので、laravelを使って(競プロの勉強の合間に)ちょこちょこミニブログを作っていくことにします。
作成していく過程で最近学習したAWSだったりCircle CIだったりの復習をしていければと考えてます。
(果たして完成するまでに何ヶ月かかるのか)要件定義
アプリの概要
Markdown記法で記事が書けるミニブログ
主要な機能
- 記事の作成、編集、更新、削除、下書き(ユーザーは複数人登録できる)
- 画像投稿
- タグ付け機能(記事を作成したユーザー以外もタグ付けできる)
- いいね機能
- Markdownエディタ
- レスポンシブデザイン
- 管理人機能(ユーザー一覧、ユーザー詳細表示)
技術要件
- フロント
- Vue.js (いいね機能、タグ付け機能の実装のため)
- Scss
- バックエンド
- php
- laravel
- サーバー
- nginx
- DB
- mysql 5.7(ローカル)
- RDS(AWS)
- インフラ
- AWS
- ECS/ECR
- RDS(mysql)
- EC2
- S3
- VPC
- Route53
- ALB
- ACM
- Terraform(AWSをコード化)
- heroku(多分AWS版のアプリ完成した後、herokuに移行すると思います。お金無いので)
- CircleCI
その他Googleアナリティクスの導入
(非技術要件も定義できれば尚良いんですが...)
作成手順
環境設定(Dockerfile→Circle CI,PHPUnit(テスト),Terraform等)→laravelでアプリの中身を作り上げていく→デプロイ の流れ
laravelのインストール
rootディレクトリに移動してlaravelをインストールします
$ composer create-project laravel/laravel larablog─── laravel(ルートディレクトリ) ├── larablog ・・・今回のアプリ ├── app ├── bootstrap . . .次回からDockerfileを書いていきます
- 投稿日:2020-02-20T21:26:41+09:00
laravelのサービスコンテナの挙動に関するメモ
初めに
この記事は、laravelのサービスコンテナの挙動について説明します。
コードはLaravel 6.2(PHP 7.2.24)で動作を確認しています。サービスコンテナに値の生成方法を登録する
サービスコンテナの主要な役割の一つが、ある値の生成方法を登録することです。
bind()関数を用いてキーとなる文字列とその生成方法をサービスコンテナに登録することができます。>>> app()->bind('rand', function() {return rand();}) => nullmake()関数を用いることで、登録した生成方法を用いて値を生成できます。
>>> app()->make('rand') => 2114203198 >>> app()->make('rand') => 576409439makeには第二引数としてパラメタを渡すことができます。
パラメタはarrayである必要があります。
渡したパラメタはbindの第二引数の第二引数として渡されます。
(ちなみに第一引数$app
にはサービスコンテナ自身が渡されます。)>>> app()->bind('p', function($app, $param) {dump($param);return 1;}) [] => null >>> app()->make('p', ['hoge' => 'fuga']) array:1 [ "hoge" => "fuga" ] => 1makeWithという関数もありますが、これはmakeと同じ動作をします。 1
>>> app()->makeWith('rand') => 449067795 >>> app()->makeWith('p', ['hoge' => 'fuga']) array:1 [ "hoge" => "fuga" ] => 1bindの第二引数に文字列を指定することもできます 2
>>> app()->bind('another_rand', 'rand')これは以下の書き方とほぼ同じ挙動をします
app()->bind('another_rand', function($app, $param){$app->make('rand', $param);})値を何度も生成したくない場合はsingleton関数を使うことができます。
これを使うと値は一度だけ生成されてキャッシュされ、二回目以降はキャッシュされた値が返されます。>>> app()->singleton('singleton_rand', function() {return rand();}) => null >>> app()->make('singleton_rand') => 259982949 >>> app()->make('singleton_rand') => 259982949 >>> app()->make('singleton_rand') => 259982949サービスコンテナを使ったインスタンス作成
サービスコンテナの主要な役割のもう一つが、サービスコンテナを用いてインスタンスを作成する際にクラスの依存関係を自動的に解決することです。
そのあたりの挙動についてみていきます。makeに登録されていない文字列が与えられた場合、サービスコンテナはそれをクラス名とみなしてそれをnewしようと試みます。
>>> class Hoge{function __construct(){echo 'Hoge constructed';}} >>> app()->make(Hoge::class) Hoge constructed => Hoge {#2999}この際newしようとしたクラスのコンストラクタに引数が必要だった場合、サービスコンテナは以下のルールにのっとりその引数の解決を試みます。(上の条件が優先)3
1. makeのパラメタで引数名が指定されていればその値を使う
2. 引数にクラスが定義されていればそのクラスをサービスコンテナを用いて生成する。
3. 定義されていなければその引数名をキーとしてサービスコンテナを用いてその値を生成する(この場合コンテキストによる結合のみが参照される)順番に詳しく見ていきます。
もっとも直接的な指定としてmakeのパラメタを通じてコンストラクタの引数に与える値を指定できます
>>> class Hoge2{function __construct($i){echo "i=$i";}} >>> app()->make(Hoge2::class, ['i' => 1]); i=1 => Hoge2 {#3012}makeのパラメタで値が指定されていない場合、コンストラクタの引数のクラスを見てそのクラスをサービスコンテナを用いて生成します。
>>> class Hoge3{} >>> app()->bind(Hoge3::class, function() {echo 'Hoge3 is constructed by service container!'; return new Hoge3();}) >>> class Hoge4{function __construct(Hoge3 $hoge3){}} >>> app()->make(Hoge4::class) Hoge3 is constructed by service container! => Hoge4 {#3011}もし、クラスによって注入するクラスを分けたい場合、コンテクストによる結合を行うことができます。
これはwhen, needs, give関数を使って以下のように書けます>>> class Hoge5{} >>> class ExtendedHoge5 extends Hoge5{} >>> class Fuga{function __construct(Hoge5 $hoge){echo get_class($hoge) . ' given';}} >>> class Fuga2{function __construct(Hoge5 $hoge){echo get_class($hoge) . ' given';}} >>> app()->when(Fuga2::class)->needs(Hoge5::class)->give(function($app) {return $app->make(ExtendedHoge5::class);}) >>> app()->make(Fuga::class) Hoge5 given => Fuga {#3048} >>> app()->make(Fuga2::class) ExtendedHoge5 given => Fuga2 {#3015}このようにコンテキストによる結合を指定したFuga2だけHoge5ではなくExtendedHoge5が注入されています。
クラス名が指定されていない場合変数名をキーにして値のサービスコンテナによる生成が試みられます。
この場合コンテキストによる結合のみが参照されます。>>> class Hoge6{function __construct($i){echo $i;}} >>> app()->make(Hoge6::class) Illuminate/Contracts/Container/BindingResolutionException with message 'Unresolvable dependency resolving [Parameter #0 [ <required> $i ]] in class Hoge6' >>> app()->bind('$i', function(){return 1;}) => null >>> app()->make(Hoge6::class) Illuminate/Contracts/Container/BindingResolutionException with message 'Unresolvable dependency resolving [Parameter #0 [ <required> $i ]] in class Hoge6' >>> app()->when(Hoge6::class)->needs('$i')->give(1); => null >>> app()->make(Hoge6::class) 1 => Hoge6 {#3026}
- 投稿日:2020-02-20T19:17:44+09:00
laravelで別々のDBにあるテーブルから取得したデータをマージしたら、どのテーブルのデータか分からなくなったので、判別するための項目をデータに持たせる
やったこと
クエリビルダのselect文内で固定カラムを追加する。
コード
Table::select( id, created_at, DB::raw(“’table1’ as table_name”) );結果
取得してきた各データに’table_name’ => ‘talbe1’が追加される。
注意すること
カラムに別名を付ける時なら’カラム名 as 別名’のようにクォーテーション一つで済むが、今回の場合、table_nameもクォーテーションで囲む必要がある。
ほかに試したこと
データを取得後、foreachで回しながら各データに’table_name’ => ‘talbe1’を追加する 。
参考にしたQiitaの記事
「Laravel-Eloquent(Mysql)で、select結果に固定値カラムを追加する方法(使用例)」
- 投稿日:2020-02-20T17:01:57+09:00
Laravel6でマルチ認証を実装する(UserとAdminの階層を分ける)
6.x系の記事があんまり無いのとUserとAdminを階層で分けるやり方も無いので備忘録として書く
コードだけ見たいんや!!!というせっかちさんはこちらからどうぞ
Laravel6-Multi-Loginなぜ書いたか
- マルチ認証の記事は沢山あるけどどれもバージョンが古い(5.1 ~ 5.5とか)
- Userを階層分けしてる記事があんまりない(どれも似てる)
- 6x系の英語記事はあるけど、どれもflg管理だったりいまいち実務向けじゃなかった
- 6.8から$redirectToがRouteServiceProviderの定数になったので、対応した実装をしたかった
自分の備忘録のためやりたい事
こんな感じでControllerの中で
User
とAdmin
を分けたい(個人的にUserだけむき出しのAuthが好きじゃない)controllers ├── Admin │ ├── Auth │ │ ├── LoginController.php │ │ ├── RegisterController.php │ └── HomeController.php ├── User │ ├── Auth │ │ ├── LoginController.php │ │ ├── RegisterController.php │ └── HomeController.php └── Controller.phpこうする理由はUserのAuthだけ階層分けるのが、個人的になんかなぁ
前提
- Laravel6が使える
- LaravelのAuth認証を実装した事がある
今回はよくある
User
とAdmin
としましたが、名前をAdmin以外にしたかったらAdminの部分を変えれば基本的にどんな名前を付けてもOKです実装
Auth認証のコマンドは別記事を見てください(
早速投げやり)Migration
まずはMigrationとModelをartisanコマンドを使って作成していきましょう
今回はサボらずに実戦でよく用いられるModels
ディレクトリを用意します。php artisan make:model Models/Admin -m
元々用意されている
create_users_table.php
の中身をコピーして
作成したcreate_admins_table.php
に貼り付けましょう。database/migrations/2020_02_01_123456_create_admins_table.php<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreateAdminsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('admins', function (Blueprint $table) { $table->bigIncrements('id'); $table->string('name'); $table->string('email')->unique(); $table->timestamp('email_verified_at')->nullable(); $table->string('password'); $table->rememberToken(); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('admins'); } }Migrationを実行しておきましょう
php artisan migrate
これでテーブルの準備は出来ました。
Seeder
User
とAdmin
でログインの検証がしたいので、ターミナルからコマンドを叩いて、それぞれテストユーザーを予め作っておきましょうphp artisan make:seeder UsersTableSeeder php artisan make:seeder AdminsTableSeeder
User
database/seeders/UsersTableSeeder<?php use Illuminate\Database\Seeder; class UsersTableSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { DB::table('users')->insert([ 'name' => 'user', 'email' => 'user@example.com', 'password' => Hash::make('12345678'), 'remember_token' => Str::random(10), ]); } }
Admin
database/seeders/AdminsTableSeeder<?php use Illuminate\Database\Seeder; class AdminsTableSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { DB::table('admins')->insert([ 'name' => 'admin', 'email' => 'admin@example.com', 'password' => Hash::make('12345678'), 'remember_token' => Str::random(10), ]); } }同時に実行出来るように元々用意されていう
DatabaseSeeder.php
で設定します。database/seeders/DatabaseSeeder.php<?php use Illuminate\Database\Seeder; class DatabaseSeeder extends Seeder { /** * Seed the application's database. * * @return void */ public function run() { $this->call([ UsersTableSeeder::class, AdminsTableSeeder::class, ]); } }Seederを実行しておきましょう
php artisan db:seed
これでテストデータの準備は出来ました。
Model
先ほどのmigrationを作る工程のコマンドで
Models
というディレクトリが作成されているはずなので、
最初から用意されているUser.php
もModels
にぶち込んでおきましょう。
namespace
を変更するのを忘れずに!!app/Models/User.phpnamespace App\Models;別ファイルで
User.php
をuseしている箇所があるので、そちらも抜けなく変更しましょう。下記ファイルの
App\User
部分をApp\Models\User
に変更します。
- app/Http/Controllers/Auth/RegisterController.php
- config/auth.php
// App\User App\Models\Userでは管理者用の
Admin.php
も作成しておきましょう。app/Models/Admin.php<?php namespace App\Models; use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; class Admin extends Authenticatable { use Notifiable; /** * The attributes that are mass assignable. * * @var array */ protected $fillable = [ 'name', 'email', 'password', ]; /** * The attributes that should be hidden for arrays. * * @var array */ protected $hidden = [ 'password', 'remember_token', ]; /** * The attributes that should be cast to native types. * * @var array */ protected $casts = [ 'email_verified_at' => 'datetime', ]; }認証方式の追加(Guard)
それじゃAdminの認証方式を追加します。
一つずつ説明するのは大変(めんどくさい)なのでこの記事を読むと良いです。config/auth.php<?php return [ // デフォルトの認証をwebからuserに変更 'defaults' => [ 'guard' => 'user', 'passwords' => 'users', ], 'guards' => [ 'user' => [ 'driver' => 'session', 'provider' => 'users', ], // Admin用の認証を追加 'admin' => [ 'driver' => 'session', 'provider' => 'admins', ], 'api' => [ 'driver' => 'token', 'provider' => 'users', 'hash' => false, ] ], 'providers' => [ 'users' => [ 'driver' => 'eloquent', 'model' => App\Models\User::class, ], 'admins' => [ 'driver' => 'eloquent', 'model' => App\Models\Admin::class, ] ], 'passwords' => [ 'users' => [ 'provider' => 'users', 'table' => 'password_resets', 'expire' => 60, 'throttle' => 60, ], 'admins' => [ 'provider' => 'admins', 'table' => 'password_resets', 'expire' => 60, 'throttle' => 60, ] ], 'password_timeout' => 10800, ];Controller
さて下の構成にするためにまずはControllers配下に
Admin
とUser
ディレクトリを作成しましょう。controllers ├── Admin │ ├── Auth │ │ ├── LoginController.php │ │ ├── RegisterController.php │ └── HomeController.php ├── User │ ├── Auth │ │ ├── LoginController.php │ │ ├── RegisterController.php │ └── HomeController.php └── Controller.php作成できたら以下のコマンドをターミナルから入力してファイルを作成しましょう。
HomeControllerはLogin後の画面を出力するために使用します。
Admin
php artisan make:controller Admin/HomeController --resource
User
php artisan make:controller User/HomeController --resource
....完了したら元々あるAuthディレクトリをそれぞれの階層に設置してください!
Admin
app/Http/Controllers/Admin/HomeController.php<?php namespace App\Http\Controllers\Admin; use App\Http\Controllers\Controller; use Illuminate\Http\Request; class HomeController extends Controller { public function __construct() { $this->middleware('auth:admin'); } public function index() { return view('admin.home'); } }
User
app/Http/Controllers/User/HomeController.php<?php namespace App\Http\Controllers\User; use App\Http\Controllers\Controller; use Illuminate\Http\Request; class HomeController extends Controller { public function __construct() { $this->middleware('auth:user'); } public function index() { return view('user.home'); } }Routing(ルーティング)
作成したControllerとパスを紐づける&Auth認証をそれぞれ指定します。
routes/web.php<?php // ユーザー Route::namespace('User')->prefix('user')->name('user.')->group(function () { // ログイン認証関連 Auth::routes([ 'register' => true, 'confirm' => false, 'reset' => false ]); // ログイン認証後 Route::middleware('auth:user')->group(function () { // TOPページ Route::resource('home', 'HomeController', ['only' => 'index']); }); }); // 管理者 Route::namespace('Admin')->prefix('admin')->name('admin.')->group(function () { // ログイン認証関連 Auth::routes([ 'register' => true, 'confirm' => false, 'reset' => false ]); // ログイン認証後 Route::middleware('auth:admin')->group(function () { // TOPページ Route::resource('home', 'HomeController', ['only' => 'index']); }); });Auth
Laravel6.8以降から$redirectToのプロパティが
RouteServiceProvider
の定数で管理されるようになりました!今回はRouteServiceProvider
の例を書いていきます!
RouteServiceProvider
ではデフォルトのリダイレクト先が
/home
になっている部分を変更して、なおかつAdmin
のリダイレクト先を指定する定数も追加します。Laravel6.8以下のバージョンの方はLoginControllerなどに用意されている
$redirectTo
プロパティの値を変更してください。app/Providers/RouteServiceProvider.php// Userのリダイレクト先 public const HOME = '/user/home'; // Adminのリダイレクト先 public const ADMIN_HOME = '/admin/home';Authenticate(未ログイン時の挙動)
未ログイン時にログイン認証が必要なページにアクセスした時のリダイレクト先を指定します。
app/Http/Middleware/Authenticate.php<?php namespace App\Http\Middleware; use Illuminate\Support\Facades\Route; use Illuminate\Auth\Middleware\Authenticate as Middleware; class Authenticate extends Middleware { protected $user_route = 'user.login'; protected $admin_route = 'admin.login'; protected function redirectTo($request) { // ルーティングに応じて未ログイン時のリダイレクト先を振り分ける if (!$request->expectsJson()) { if (Route::is('user.*')) { return route($this->user_route); } elseif (Route::is('admin.*')) { return route($this->admin_route); } } } }RedirectIfAuthenticated(ログイン時の挙動)
逆にログインしてる時に
/login
にアクセスしてきた時のリダイレクト先を指定します。app/Http/Middleware/RedirectIfAuthenticated.php<?php namespace App\Http\Middleware; use App\Providers\RouteServiceProvider; use Closure; use Illuminate\Support\Facades\Auth; class RedirectIfAuthenticated { /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @param string|null $guard * @return mixed */ public function handle($request, Closure $next, $guard = null) { if (Auth::guard($guard)->check() && $guard === 'user') { return redirect(RouteServiceProvider::HOME); } elseif (Auth::guard($guard)->check() && $guard === 'admin') { return redirect(RouteServiceProvider::ADMIN_HOME); } return $next($request); } }
User
ログイン
app/Http/Controllers/User/Auth/LoginController.php<?php namespace App\Http\Controllers\User\Auth; use App\Http\Controllers\Controller; use App\Providers\RouteServiceProvider; use Illuminate\Foundation\Auth\AuthenticatesUsers; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; class LoginController extends Controller { use AuthenticatesUsers; protected $redirectTo = RouteServiceProvider::HOME; public function __construct() { $this->middleware('guest:user')->except('logout'); } // Guardの認証方法を指定 protected function guard() { return Auth::guard('user'); } // ログイン画面 public function showLoginForm() { return view('user.auth.login'); } // ログアウト処理 public function logout(Request $request) { Auth::guard('user')->logout(); return $this->loggedOut($request); } // ログアウトした時のリダイレクト先 public function loggedOut(Request $request) { return redirect(route('user.login')); } }新規登録
app/Http/Controllers/User/Auth/RegisterController.php<?php namespace App\Http\Controllers\User\Auth; use App\Http\Controllers\Controller; use App\Providers\RouteServiceProvider; use Illuminate\Foundation\Auth\RegistersUsers; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Validator; use Illuminate\Http\Request; use Illuminate\Auth\Events\Registered; use Illuminate\Support\Facades\Auth; use App\Models\User; class RegisterController extends Controller { use RegistersUsers; protected $redirectTo = RouteServiceProvider::HOME; /** * Create a new controller instance. * * @return void */ public function __construct() { $this->middleware('guest:user'); } // Guardの認証方法を指定 protected function guard() { return Auth::guard('user'); } // 新規登録画面 public function showRegistrationForm() { return view('user.auth.register'); } // バリデーション protected function validator(array $data) { return Validator::make($data, [ 'name' => ['required', 'string', 'max:255'], 'email' => ['required', 'string', 'email', 'max:255', 'unique:users'], 'password' => ['required', 'string', 'min:8', 'confirmed'], ]); } // 登録処理 protected function create(array $data) { return User::create([ 'name' => $data['name'], 'email' => $data['email'], 'password' => Hash::make($data['password']), ]); } }
Admin
基本的に上の
user
の部分をadmin
に直すだけです。
なのでコメントは外します。ログイン
app/Http/Controllers/Admin/Auth/LoginController.php<?php namespace App\Http\Controllers\Admin\Auth; use App\Http\Controllers\Controller; use App\Providers\RouteServiceProvider; use Illuminate\Foundation\Auth\AuthenticatesUsers; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; class LoginController extends Controller { use AuthenticatesUsers; protected $redirectTo = RouteServiceProvider::ADMIN_HOME; public function __construct() { $this->middleware('guest:admin')->except('logout'); } protected function guard() { return Auth::guard('admin'); } public function showLoginForm() { return view('admin.auth.login'); } public function logout(Request $request) { Auth::guard('admin')->logout(); return $this->loggedOut($request); } public function loggedOut(Request $request) { return redirect(route('admin.login')); } }新規登録
app/Http/Controllers/User/Auth/RegisterController.php<?php namespace App\Http\Controllers\Admin\Auth; use App\Http\Controllers\Controller; use App\Providers\RouteServiceProvider; use Illuminate\Foundation\Auth\RegistersUsers; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Validator; use Illuminate\Http\Request; use Illuminate\Auth\Events\Registered; use Illuminate\Support\Facades\Auth; use App\Models\Admin; class RegisterController extends Controller { use RegistersUsers; protected $redirectTo = RouteServiceProvider::ADMIN_HOME; /** * Create a new controller instance. * * @return void */ public function __construct() { $this->middleware('guest:admin'); } protected function guard() { return Auth::guard('admin'); } public function showRegistrationForm() { return view('admin.auth.register'); } protected function validator(array $data) { return Validator::make($data, [ 'name' => ['required', 'string', 'max:255'], 'email' => ['required', 'string', 'email', 'max:255', 'unique:users'], 'password' => ['required', 'string', 'min:8', 'confirmed'], ]); } protected function create(array $data) { return Admin::create([ 'name' => $data['name'], 'email' => $data['email'], 'password' => Hash::make($data['password']), ]); } }View
長い。。。
もう一踏ん張りです。viewは以下の構成で実装します。
views ├── user │ ├── auth │ │ ├── login.blade.php │ │ └── register.blade.php │ └── home.blade.php │ ├── admin │ ├── auth │ │ ├── login.blade.php │ │ └── register.blade.php │ └── home.blade.php │ └── layouts ├── user │ └── app.blade.php │ └── admin └── app.blade.phplayouts
User
resources/views/layouts/user/app.blade.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', 'Laravel') }}</title> <!-- Scripts --> <script src="{{ asset('js/app.js') }}" defer></script> <!-- Fonts --> <link rel="dns-prefetch" href="//fonts.gstatic.com"> <link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet"> <!-- Styles --> <link href="{{ asset('css/app.css') }}" rel="stylesheet"> </head> <body> <div id="app"> <nav class="navbar navbar-expand-md navbar-light bg-white shadow-sm"> <div class="container"> <a class="navbar-brand" href="{{ url('/') }}"> {{ config('app.name', 'Laravel') }} </a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarSupportedContent"> <!-- Left Side Of Navbar --> <ul class="navbar-nav mr-auto"> </ul> <!-- Right Side Of Navbar --> <ul class="navbar-nav ml-auto"> <!-- Authentication Links --> @guest('user') <li class="nav-item"> <a class="nav-link" href="{{ route('user.login') }}">{{ __('Login') }}</a> </li> @if (Route::has('user.register')) <li class="nav-item"> <a class="nav-link" href="{{ route('user.register') }}">{{ __('Register') }}</a> </li> @endif @else <li class="nav-item dropdown"> <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre> {{ Auth::user()->name }} <span class="caret"></span> </a> <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown"> <a class="dropdown-item" href="{{ route('user.logout') }}" onclick="event.preventDefault(); document.getElementById('logout-form').submit();"> {{ __('Logout') }} </a> <form id="logout-form" action="{{ route('user.logout') }}" method="POST" style="display: none;"> @csrf </form> </div> </li> @endguest </ul> </div> </div> </nav> <main class="py-4"> @yield('content') </main> </div> </body> </html>
Admin
こっちも基本的に
user
をadmin
に変えてるだけresources/views/layouts/admin/app.blade.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', 'Laravel') }}</title> <!-- Scripts --> <script src="{{ asset('js/app.js') }}" defer></script> <!-- Fonts --> <link rel="dns-prefetch" href="//fonts.gstatic.com"> <link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet"> <!-- Styles --> <link href="{{ asset('css/app.css') }}" rel="stylesheet"> </head> <body> <div id="app"> <nav class="navbar navbar-expand-md navbar-light bg-white shadow-sm"> <div class="container"> <a class="navbar-brand" href="{{ url('/') }}"> {{ config('app.name', 'Laravel') }} </a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarSupportedContent"> <!-- Left Side Of Navbar --> <ul class="navbar-nav mr-auto"> </ul> <!-- Right Side Of Navbar --> <ul class="navbar-nav ml-auto"> <!-- Authentication Links --> @unless (Auth::guard('admin')->check()) <li class="nav-item"> <a class="nav-link" href="{{ route('admin.login') }}">{{ __('Login') }}</a> </li> @if (Route::has('admin.register')) <li class="nav-item"> <a class="nav-link" href="{{ route('admin.register') }}">{{ __('Register') }}</a> </li> @endif @else <li class="nav-item dropdown"> <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre> {{ Auth::user()->name }} <span class="caret"></span> </a> <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown"> <a class="dropdown-item" href="{{ route('admin.logout') }}" onclick="event.preventDefault(); document.getElementById('logout-form').submit();"> {{ __('Logout') }} </a> <form id="logout-form" action="{{ route('admin.logout') }}" method="POST" style="display: none;"> @csrf </form> </div> </li> @endunless </ul> </div> </div> </nav> <main class="py-4"> @yield('content') </main> </div> </body> </html>View(ログイン)
先ほどSeederで登録した情報でログインできます
User
resources/views/user/auth/login.blade.php@extends('layouts.user.app') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header">{{ __('Login') }}</div> <div class="card-body"> <form method="POST" action="{{ route('user.login') }}"> @csrf <div class="form-group row"> <label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label> <div class="col-md-6"> <input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus> @error('email') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> <div class="form-group row"> <label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label> <div class="col-md-6"> <input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="current-password"> @error('password') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> <div class="form-group row"> <div class="col-md-6 offset-md-4"> <div class="form-check"> <input class="form-check-input" type="checkbox" name="remember" id="remember" {{ old('remember') ? 'checked' : '' }}> <label class="form-check-label" for="remember"> {{ __('Remember Me') }} </label> </div> </div> </div> <div class="form-group row mb-0"> <div class="col-md-8 offset-md-4"> <button type="submit" class="btn btn-primary"> {{ __('Login') }} </button> </div> </div> </form> </div> </div> </div> </div> </div> @endsection
Admin
resources/views/admin/auth/login.blade.php@extends('layouts.admin.app') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header">{{ __('Login') }}</div> <div class="card-body"> <form method="POST" action="{{ route('admin.login') }}"> @csrf <div class="form-group row"> <label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label> <div class="col-md-6"> <input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus> @error('email') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> <div class="form-group row"> <label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label> <div class="col-md-6"> <input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="current-password"> @error('password') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> <div class="form-group row"> <div class="col-md-6 offset-md-4"> <div class="form-check"> <input class="form-check-input" type="checkbox" name="remember" id="remember" {{ old('remember') ? 'checked' : '' }}> <label class="form-check-label" for="remember"> {{ __('Remember Me') }} </label> </div> </div> </div> <div class="form-group row mb-0"> <div class="col-md-8 offset-md-4"> <button type="submit" class="btn btn-primary"> {{ __('Login') }} </button> </div> </div> </form> </div> </div> </div> </div> </div> @endsectionView(新規登録)
User
resources/views/user/auth/register.blade.php@extends('layouts.user.app') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header">{{ __('Register') }}</div> <div class="card-body"> <form method="POST" action="{{ route('user.register') }}"> @csrf <div class="form-group row"> <label for="name" class="col-md-4 col-form-label text-md-right">{{ __('Name') }}</label> <div class="col-md-6"> <input id="name" type="text" class="form-control @error('name') is-invalid @enderror" name="name" value="{{ old('name') }}" required autocomplete="name" autofocus> @error('name') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> <div class="form-group row"> <label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label> <div class="col-md-6"> <input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email"> @error('email') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> <div class="form-group row"> <label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label> <div class="col-md-6"> <input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="new-password"> @error('password') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> <div class="form-group row"> <label for="password-confirm" class="col-md-4 col-form-label text-md-right">{{ __('Confirm Password') }}</label> <div class="col-md-6"> <input id="password-confirm" type="password" class="form-control" name="password_confirmation" required autocomplete="new-password"> </div> </div> <div class="form-group row mb-0"> <div class="col-md-6 offset-md-4"> <button type="submit" class="btn btn-primary"> {{ __('Register') }} </button> </div> </div> </form> </div> </div> </div> </div> </div> @endsection
Admin
resources/views/admin/auth/register.blade.php@extends('layouts.admin.app') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header">{{ __('Register') }}</div> <div class="card-body"> <form method="POST" action="{{ route('admin.register') }}"> @csrf <div class="form-group row"> <label for="name" class="col-md-4 col-form-label text-md-right">{{ __('Name') }}</label> <div class="col-md-6"> <input id="name" type="text" class="form-control @error('name') is-invalid @enderror" name="name" value="{{ old('name') }}" required autocomplete="name" autofocus> @error('name') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> <div class="form-group row"> <label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label> <div class="col-md-6"> <input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email"> @error('email') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> <div class="form-group row"> <label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label> <div class="col-md-6"> <input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="new-password"> @error('password') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> <div class="form-group row"> <label for="password-confirm" class="col-md-4 col-form-label text-md-right">{{ __('Confirm Password') }}</label> <div class="col-md-6"> <input id="password-confirm" type="password" class="form-control" name="password_confirmation" required autocomplete="new-password"> </div> </div> <div class="form-group row mb-0"> <div class="col-md-6 offset-md-4"> <button type="submit" class="btn btn-primary"> {{ __('Register') }} </button> </div> </div> </form> </div> </div> </div> </div> </div> @endsectionView(ログイン後の画面)
User
resources/views/user/home.blade.php@extends('layouts.user.app') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header">Dashboard</div> <div class="card-body"> @if (session('status')) <div class="alert alert-success" role="alert"> {{ session('status') }} </div> @endif You are logged in! </div> </div> </div> </div> </div> @endsection
Admin
resources/views/admin/home.blade.php@extends('layouts.admin.app') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header">Dashboard</div> <div class="card-body"> @if (session('status')) <div class="alert alert-success" role="alert"> {{ session('status') }} </div> @endif You are logged in! </div> </div> </div> </div> </div> @endsection長かった。。。
終わり?
- 投稿日:2020-02-20T12:27:48+09:00
Laravel-SnappyでPDFを出力する際のページ番号の表示を制御する
はじめに
この記事ではLaravel-Snappyを利用してPDF出力処理を既に実装されていることを前提にしています。
Laravel-Snappyの導入方法については触れません。ページ番号の表示方法
オプションで表示する方法
Laravel-Snappyを使ってPDFにページ番号を表示したい場合、以下のようにPDFインスタンスを作成する際のオプションを指定することで簡単に実装することができます。
$pdf = PDF::loadView('pdf') ->setOption('footer-center', '[page] ページ')); // フッター中央 [page]でページ番号が自動で入りますただし、出力されるPDFの総ページ数が1ページの場合でも表示されてしまいます。
1ページの時はページ数を非表示にする場合はページの表示箇所を自前で実装する必要があるようです。
オプション指定の方法でどうにかできないか調べましたが見つかりませんでした。自前で表示する方法
ページ数の情報はPDFに読み込ませるHTMLのLocationオブジェクトから取得することができます。
Location.hrefには以下が格納されています。
page
が現在のページ番号
topage
が総ページ数file:///tmp/knp_snappy5e3b69aa559ad4.24507587.html?page=1§ion=&sitepage=1&title=&subsection=&frompage=1&subsubsection=&isodate=2020-02-06&topage=2&doctitle=&sitepages=2&webpage=&time=&date=この情報を用いて、ページ番号を表示する為にフッターを用意します。
footer.blade.php<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> </head> <body class="bg-white"> <div class="text-center"> <span id="pdfkit_page_current"></span> </div> <script type="text/javascript"> (function () { var pdfInfo = {}; var x = document.location.search.substring(1).split('&'); for (var i in x) { var z = x[i].split('=', 2); pdfInfo[z[0]] = unescape(z[1]); } var page = pdfInfo.page || 1; // ページ番号 var pageCount = pdfInfo.topage || 1; // 総ページ数 // 改ページがある場合のみページ番号を表示します if (pageCount > 1) { document.getElementById('pdfkit_page_current').textContent = page; } })(); </script> </body>用意したフッターをPDFに設定するにはオプションで指定します。
$pdf = PDF::loadView('pdf') ->setOption('footer-html', \View::make('footer')->render() // 用意したフッターを設定 ->setOption('margin-bottom', 10)); // フッターの余白を設定します以上になります。
- 投稿日:2020-02-20T11:52:45+09:00
【Laravel6】外部キー制約で少し詰まった General error: 3780 Referencing column.....
初めてのLARAVEL 5.6 : (31) RELATIONSHIPSで、
Userモデル
とArticleモデル
を関連付けするのに少し詰まったのでメモ。
外部キー自体の知識については外部キーについて(MySQL編)この記事が分かりやすかった。
Userモデル
の初期状態は、xxx_create_users_table.php$table->bigIncrements('id'); $table->string('name'); $table->string('email')->unique(); $table->timestamp('email_verified_at')->nullable(); $table->string('password'); $table->rememberToken(); $table->timestamps();
Articleモデル
の初期状態は、xxx_create_articles_table.php$table->bigIncrements('id'); $table->string('title'); $table->text('body'); $table->timestamps();
User
:Article
= 1 : n の関連付けをするので、User
はArticle
をたくさん持つ(hasMany)、Article
は1人のUser
に帰属する(belongsTo)である。Article.phppublic function user(){ return $this->belongsTo('App\User'); }User.phppublic function articles() { return $this->hasMany('App\Article'); }
Userモデル
とArticleモデル
を関連付けるため、Articleモデル
に user_id を追加し、Userモデル
の id と紐づける。
また、紐づけられたUserモデル
のデータが削除されたら、紐づいているArticleモデル
のデータも削除するようにする。xxx_create_articles_table.php$table->bigIncrements('id'); $table->unsignedInteger('user_id'); //追加 $table->string('title'); $table->text('body'); $table->timestamps(); $table->foreign('user_id') //外部キーの宣言 ->references('id') //参照先 ->on('users') //参照テーブル ->onDelete('cascade'); //参照テーブルカラムが消えたら同時に消すDBを全てロールバックし、再度全マイグレーションを実行するため、
php artisan migrate:refresh
を実行。よく見てみると、紐づけられている
Userモデル
のidの型はbigIncrements、紐づけるために追加したArticleモデル
のuser_idの型はunsignedIntegerとなっており、型が違う。
- bigIncrements => BIGINTを使用した自動増分(INCREMENT) ID
- unsignedInteger => 符号なし(UNSIGED)のINTを使用したID
よって、
Articleモデル
のuser_idの型をunsignedBigIntegerにすると良い。
migrateする際は、テーブルを捨てて作り直したいので、php artisan migrate:fresh
を実行する。符号なし(UNSIGNED)との型の整合性が気になるが、自動増分(INCREMENT)はもともと符号なしだから不整合は起きない!!
初めてのLARAVEL 5.6 は laravel6に対応しておらず、laravel6の
Userモデル
のidがbigintになっていたからこの問題が起こったのだろうーーー。
- 投稿日:2020-02-20T11:36:59+09:00
Laravel DB.com ” New!! チーム共有機能 ”
Laravel DB.com ってなに?
Laravel DB.com ってなに?前回の紹介記事です!
https://qiita.com/daisu_yamazaki/items/068595670bdc2b6fe3fcNew「チーム共有機能」
「シェアData 送信側」
1. [シェアData]作成
2. [シェアData]IDをコピー
「シェアData 受信側」
1.[シェアData]読み込み
2.「Read」ボタンをクリックしてデータを受信表示しましょう。
3.シェアデータの複製が完了しました。
受信側にデータが入りましたので、受信側もそのデータを活用できるようになります。
現在はベータ版機能として、「1データのみ共有可能」 になります。
それだけでも、便利になるはずですので、お試しいただければと思います。Laravel専用ツール!!
日本から世界へ!世界で使われるプロダクトになることを願っております。
是非、応援の程よろしくお願いいたしますm(_ _)m
ツイッターのフォローも興味があれば!!
Twitter: LaravelDB.com
LaravelDB.com解説ページ一覧
コード書かずに超スピード開発~(DEMO動画あり)~最新版『 Laravel DB.com 』
https://qiita.com/daisu_yamazaki/items/068595670bdc2b6fe3fc
LaravelDB.com 対応カラム一覧
https://qiita.com/daisu_yamazaki/items/92dc3cc599a264c3fb0f
LaravelDB.com テーブル命名ルール
https://qiita.com/daisu_yamazaki/items/1cb5987cc6d1008def82
LaravelDb.com integerの注意点
https://qiita.com/daisu_yamazaki/items/f2e6d58cfa20fa81fd54
LaravelDB.com Faker(テストデータ投入)
https://qiita.com/daisu_yamazaki/items/57669e8fa2c256d85c95
以上
- 投稿日:2020-02-20T11:36:59+09:00
【LaravelDB.com】New !! ”チーム共有機能 ” 早々に追加された訳
Laravel DB.com ってなに?
Laravel DB.com ってなに?前回の紹介記事です!
https://qiita.com/daisu_yamazaki/items/068595670bdc2b6fe3fcなぜ1週間程度で”Team共有機能”を実装したのか?
私は、G'sACADEMYや大学院/大学などで先生をしてます。
Team開発や、メンターとのオンラインMTG用にあった方が助かるとの希望をいくつか聞きました。更にTwitterでもアンケートを取り、微妙な数字でしたが、とりあえず学生の意見もあったのでシンプルな機能であれば実装しようと思いました。
「どうやったら?早く、シンプルに実装可能か?」を仕様検討していたのですが、一つの方法を昨日考えつきました。昨日から朝まで実装、不具合があったので、さっきの午前中までかかり完成(ベータ版としておきます)。
必要な気がしてたので、まずは実装して、これから改善ですね。スピードは時間を有意義に使うための重要なスキルですね。これで、少しでも、オンライン上でデータのやり取りができたら良いのですが。。。また、追加機能・改修等があれば記事を追加して行きたいと思います。New「チーム共有機能」
【シェアData 送信側】
1. [シェアData]作成
POINT:
この時点で共有データが作成されます。
見せたい相手にIDを渡しておいて、変更があれば「Create a [Share ID]」ボタンを押すと毎度データ更新されることを知っておきましょう! 「データ更新したので見てください!」って後から言えるってことですね。2. [シェアData]IDをコピー
【シェアData 受信側】
1.[シェアData]読み込み
相手は送られてきた[シェアData]IDを貼り付けます。
こちらのIDを知っていれば誰でも読み込めます!見て欲しい人に渡しましょう!
※LOGIN(Googleアカウントで)しないと見れないことは認識しておいてください。
2.「Read」ボタンをクリックしてデータを受信表示しましょう。
3.シェアデータの複製が完了しました。
受信側にデータが入りましたので、受信側もそのデータを活用できるようになります。
現在はベータ版機能として、「1データのみ共有可能」 になります。
それだけでも、便利になるはずですので、お試しいただければと思います。Laravel専用ツール!!
日本から世界へ!世界で使われるプロダクトになることを願っております。
是非、応援の程よろしくお願いいたしますm(_ _)m
ツイッターのフォローも興味があれば!!
Twitter: LaravelDB.com
LaravelDB.com解説ページ一覧
コード書かずに超スピード開発~(DEMO動画あり)~最新版『 Laravel DB.com 』
https://qiita.com/daisu_yamazaki/items/068595670bdc2b6fe3fc
LaravelDB.com 対応カラム一覧
https://qiita.com/daisu_yamazaki/items/92dc3cc599a264c3fb0f
LaravelDB.com テーブル命名ルール
https://qiita.com/daisu_yamazaki/items/1cb5987cc6d1008def82
LaravelDb.com integerの注意点
https://qiita.com/daisu_yamazaki/items/f2e6d58cfa20fa81fd54
LaravelDB.com Faker(テストデータ投入)
https://qiita.com/daisu_yamazaki/items/57669e8fa2c256d85c95
以上
- 投稿日:2020-02-20T09:41:23+09:00
VSCodeでLaravelの「.env」ファイルをDotEnvに関連付ける
概要
開発、ステージング、商用、etc.など
環境によって「.env」を増やしたいことがあると思います。
VSCodeを使用している場合はDotEnvプラグインを入れると関連付けされて、見やすくなります。
しかし、関連付け対象のデフォルト設定にないファイル、例えば「.env.xxxx」など関連付けされません。
ここでは、デフォルトにないファイルへの関連付け方法を、簡単ですが記載させていただきます。コマンドパレットを開く
まずVSCodeコマンドパレットを開きます
- メニューの表示 > コマンドパレット
(macは「shift + command + p」で開きます)以下のように入力します。
setting.jsonが開きます
編集可能なsetting.jsonを選びます。
VSCodeの設定により、画面左にデフォルト、画面右に編集可能なsetting.jsonのペインが開きます。
setting.jsonのfiles.associationsに関連付けを追記します。files.associations
"files.associations": {
ここに関連付けを書きます。
}例
"files.associations": { ".env.dev01": "dotenv", ".env.dev*": "dotenv", ".env.management*": "dotenv", ".env.production.aws*": "dotenv", ".env.development*": "dotenv", },以上です
補足:DotEnvのGitHub
- 投稿日:2020-02-20T09:10:15+09:00
【Laravel】php artisan migrateをしたときに出くわしがちなエラー【ただのDBエラー】
Laravel: php artisan migrateをしたときに出くわしがちなエラー
エラー例
外部キーの型が一致しない
外部キーを付けるフィールドの型と参照先フィールドの型が一致していないとデータベースエラーが起こります。
特にLaravel5.8以降では主キーの型はデフォルトでUNSIGNED BIGINTになっています。
外部キーの型がINTEGERのままだとこのエラーに遭遇します。Illuminate\Database\QueryException : SQLSTATE[HY000]: General error: 3780 Referencing column 〜対策
マイグレーションファイルの型を揃える。
/** * Run the migrations. * * @return void */ public function up() { Schema::create('photos', function (Blueprint $table) { $table->string('id')->primary(); // $table->unsignedInteger('user_id'); // user.idがINTEGERならこちら $table->bigInteger('user_id')->unsigned(); // user.idがUNSIGNED BIGINTならこちら $table->string('filename'); $table->timestamps(); $table->foreign('user_id')->references('id')->on('users'); }); }参考: Laravel 5.8 にて["SQLSTATE[HY000]: General error: 1215 Cannot add foreign key constraint"]エラー - Qiita
すでにテーブルが存在する
Illuminate\Database\QueryException : SQLSTATE[42S01]: Base table or view already exists: 1050 Table 'photos' already exists 〜中途半端にテーブルが残っているとよく出くわします。
php artisan migrate
に失敗すると中途半端にテーブルができることがあります。
(私は上記の「外部キーの型が一致しない」の事例でまさに中途半端なテーブルができました。)対策
「:fresh」をコマンドの最後に付けることで既存のテーブルを消して作り直すので回避できます。
※全テーブルの中身が消えるので十分に注意してくださいphp artisan migrate:fresh参考: Laravel の migrate で Base table or view already exists というときに、migrate:refresh した - Qiita
あるいは、直接テーブルを消してもよいでしょう。
- 投稿日:2020-02-20T08:04:02+09:00
TechCommitの企画で当たったTechpitの教材「Laravelで飲食店検索LINE Botを作ろう!」を終えての感想
はじめに
TechCommitの企画で当選したTechpitの「Laravelで飲食店検索LINE Botを作ろう!」を無事終わらせることが出来たので、感想等々残しておきます。
感想・学んだこと
感想
最初から飲食店Botを作っていくのではなく、環境構築→おうむ返しBot→飲食店Botという流れだったので段階的に理解できた。また、Laravelの開発に役立つ有用なライブラリやツールについても書いてある。外部APIの流れを理解したい人やLaravelを使って何かを作ってみたい人におすすめ出来る教材
学んだこと
- ngrokを使ってローカルで稼働しているサービスを外部に公開できる
- HTTPリクエストを行うのに便利なライブラリがGuzzle
- Flex Messageを使って、レイアウトを自由にカスタマイズできる。←特にFlex Message Simulatorが便利!
- ブラウザを使わないアクセスのルーティングはroutes/api.phpで管理
- おうむ返しBotの仕組み(LINE側で処理を行うためのチャネルをユーザーとLaravelの間に挟む)
- 飲食店検索Botの仕組み(ユーザーが入力→チャネルが受け取りLaravelにPOSTリクエスト→Laravel側でぐるなびAPIにGETリクエストを投げる→返ってきたレスポンスをLaravelが受け取る→チャネルにPOSTリクエストで渡し、飲食店表示される)