20200220のlaravelに関する記事は11件です。

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を書いていきます

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

laravelのサービスコンテナの挙動に関するメモ

初めに

この記事は、laravelのサービスコンテナの挙動について説明します。
コードはLaravel 6.2(PHP 7.2.24)で動作を確認しています。

サービスコンテナに値の生成方法を登録する

サービスコンテナの主要な役割の一つが、ある値の生成方法を登録することです。
bind()関数を用いてキーとなる文字列とその生成方法をサービスコンテナに登録することができます。

>>> app()->bind('rand', function() {return rand();})
=> null

make()関数を用いることで、登録した生成方法を用いて値を生成できます。

>>> app()->make('rand')
=> 2114203198
>>> app()->make('rand')
=> 576409439

makeには第二引数としてパラメタを渡すことができます。
パラメタはarrayである必要があります。
渡したパラメタはbindの第二引数の第二引数として渡されます。
(ちなみに第一引数$appにはサービスコンテナ自身が渡されます。)

>>> app()->bind('p', function($app, $param) {dump($param);return 1;})
[]
=> null
>>> app()->make('p', ['hoge' => 'fuga'])
array:1 [
  "hoge" => "fuga"
]
=> 1

makeWithという関数もありますが、これはmakeと同じ動作をします。 1

>>> app()->makeWith('rand')
=> 449067795
>>> app()->makeWith('p', ['hoge' => 'fuga'])
array:1 [
  "hoge" => "fuga"
]
=> 1

bindの第二引数に文字列を指定することもできます 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}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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結果に固定値カラムを追加する方法(使用例)」

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel6でマルチ認証を実装する(UserとAdminの階層を分ける)

6.x系の記事があんまり無いのとUserとAdminを階層で分けるやり方も無いので備忘録として書く

コードだけ見たいんや!!!というせっかちさんはこちらからどうぞ
Laravel6-Multi-Login

なぜ書いたか

  • マルチ認証の記事は沢山あるけどどれもバージョンが古い(5.1 ~ 5.5とか)
  • Userを階層分けしてる記事があんまりない(どれも似てる)
  • 6x系の英語記事はあるけど、どれもflg管理だったりいまいち実務向けじゃなかった
  • 6.8から$redirectToがRouteServiceProviderの定数になったので、対応した実装をしたかった
  • 自分の備忘録のため

やりたい事

こんな感じでControllerの中でUserAdminを分けたい(個人的にUserだけむき出しのAuthが好きじゃない)

controllers
   ├── Admin
   │   ├── Auth
   │   │   ├── LoginController.php
   │   │   ├── RegisterController.php
   │   └── HomeController.php
   ├── User
   │   ├── Auth
   │   │   ├── LoginController.php
   │   │   ├── RegisterController.php
   │   └── HomeController.php
   └── Controller.php

こうする理由はUserのAuthだけ階層分けるのが、個人的になんかなぁ

参考
Laravel5.5 Multi-Auth 分かりやすいフォルダ構成 備忘録

前提

  • Laravel6が使える
  • LaravelのAuth認証を実装した事がある

今回はよくあるUserAdminとしましたが、名前をAdmin以外にしたかったらAdminの部分を変えれば基本的にどんな名前を付けてもOKです

実装

Auth認証のコマンドは別記事を見てください(早速投げやり

Laravel6 ログイン機能を実装する

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

UserAdminでログインの検証がしたいので、ターミナルからコマンドを叩いて、それぞれテストユーザーを予め作っておきましょう

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.phpModelsにぶち込んでおきましょう。

namespaceを変更するのを忘れずに!!

app/Models/User.php
namespace 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の認証方式を追加します。
一つずつ説明するのは大変(めんどくさい)なのでこの記事を読むと良いです。

Laravel の Guard(認証) って実際何をやっているのじゃ?

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配下にAdminUserディレクトリを作成しましょう。

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の例を書いていきます!

参考
【Laravel6.8以降】認証関連のリダイレクト先の設定変更がDRYになったよ

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.php
layouts

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

こっちも基本的にuseradminに変えてるだけ

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(ログイン)

スクリーンショット 2020-02-20 16.51.54.png

先ほど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>
@endsection
View(新規登録)

スクリーンショット 2020-02-20 16.52.39.png

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>
@endsection

View(ログイン後の画面)

スクリーンショット 2020-02-20 16.53.24.png

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

長かった。。。

終わり?

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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&section=&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)); // フッターの余白を設定します

以上になります。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【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 の関連付けをするので、UserArticleをたくさん持つ(hasMany)、Articleは1人のUserに帰属する(belongsTo)である。

Article.php
public function user(){
        return $this->belongsTo('App\User');
    }
User.php
public 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を実行。

しかし、このままでは以下のようなエラーが出てしまう。
スクリーンショット 2020-02-20 11.36.59.png

よく見てみると、紐づけられている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モデルidbigintになっていたからこの問題が起こったのだろうーーー。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel DB.com ” New!! チーム共有機能 ”

Laravel DB.com ってなに?

Laravel DB.com ってなに?前回の紹介記事です!
https://qiita.com/daisu_yamazaki/items/068595670bdc2b6fe3fc

New「チーム共有機能」

シェア機能のことです。

「シェアData 送信側」

1. [シェアData]作成

2. [シェアData]IDをコピー

ここでコピーしたIDを相手に知らせます。

「シェアData 受信側」

1.[シェアData]読み込み

相手は送られてきた[シェアData]IDを貼り付けます。

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

以上

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【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をコピー

ここでコピーした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

以上

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

VSCodeでLaravelの「.env」ファイルをDotEnvに関連付ける

概要

開発、ステージング、商用、etc.など
環境によって「.env」を増やしたいことがあると思います。
VSCodeを使用している場合はDotEnvプラグインを入れると関連付けされて、見やすくなります。
しかし、関連付け対象のデフォルト設定にないファイル、例えば「.env.xxxx」など関連付けされません。
ここでは、デフォルトにないファイルへの関連付け方法を、簡単ですが記載させていただきます。

コマンドパレットを開く

まずVSCodeコマンドパレットを開きます
- メニューの表示 > コマンドパレット
(macは「shift + command + p」で開きます)

以下のように入力します。

コマンドパレットに、setting.jsonと入力します。
スクリーンショット 2020-02-20 9.15.56.png

setting.jsonが開きます

スクリーンショット 2020-02-20 9.51.05.png
編集可能な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

vscode-dotenvのGit

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【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

あるいは、直接テーブルを消してもよいでしょう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TechCommitの企画で当たったTechpitの教材「Laravelで飲食店検索LINE Botを作ろう!」を終えての感想

はじめに

TechCommitの企画で当選したTechpitの「Laravelで飲食店検索LINE Botを作ろう!」を無事終わらせることが出来たので、感想等々残しておきます。

EPxLDd0U4AAIUx4.jpeg

感想・学んだこと

感想

最初から飲食店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リクエストで渡し、飲食店表示される)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む