20210411のlaravelに関する記事は7件です。

Laravel+React でpusherを使う

バージョンは laravel 6 react 16.14. 0 npm 7.5.3 laravelのconfig\app.phpの config\app.php App\Providers\AppServiceProvider::class, App\Providers\AuthServiceProvider::class, App\Providers\BroadcastServiceProvider::class, <- コメントアウト解除 App\Providers\EventServiceProvider::class, App\Providers\RouteServiceProvider::class, .envの BROADCAST_DRIVER=pusher に変更 resources/js/bootstrap.js /*全てコメントアウト*/ import Echo from 'laravel-echo'; window.Pusher = require('pusher-js'); window.Echo = new Echo({ broadcaster: 'pusher', key: process.env.MIX_PUSHER_APP_KEY, cluster: process.env.MIX_PUSHER_APP_CLUSTER, encrypted: true }); 自分のlaravelのフォルダ直下で以下のコマンドを実行 ターミナル $ composer require pusher/pusher-php-server pusherに登録してください https://pusher.com/ create-appを押してアプリを作成 この画面になったら ・アプリ名 ・フロントまたはバックエンドの使用している言語を選択してCreate appを押す 作成したアプリのApp Keysを見る app_id key secret clusterをコピー .envで /.env PUSHER_APP_ID= PUSHER_APP_KEY= PUSHER_APP_SECRET= PUSHER_APP_CLUSTER= ここにコピー ターミナルで $ php artisan make:event PostCreated ここから個人の開発によって違いますがなるべくわかりやすく説明します app/Events/PostCreated. <?php namespace App\Events; use App\Post; <-自分はここを追記 use App\User; <-自分はここを追記 use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\PresenceChannel; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Contracts\Broadcasting\ShouldBroadcast; use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Queue\SerializesModels; class PostCreated implements ShouldBroadcast { use Dispatchable, InteractsWithSockets, SerializesModels; public $post; public $user; public function __construct(Post $post, User $user) { $this->post = $post; $this->user = $user; } public function broadcastOn() { return [ new PrivateChannel('new-post'), new PrivateChannel('App.User.'.$this->post->user->id), ]; } public function broadcastWith() { return [ 'post' => array_merge($this->post->toArray(), [ 'user' => $this->post->user, ]), 'user' => $this->user, ]; } } use App\Postで自分が欲しい情報をとってきています broadcastOnにPrivateChannel('new-post')があると思います これはreact側で呼び出すための鍵です 使用しているControllerに飛びます app\Http\Controllers <?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Post; use App\Events\PostCreated; <-追記 public function create(Request $request,Post $post) { $createdPost = $request->user()->posts()->create([ 'body' => $request->body, ]); broadcast(new PostCreated($createdPost, $request->user()))->toOthers(); <-これを追記 return response()->json($post->with('user')->find($createdPost->id)); } routes/channels.php /*追記*/ Broadcast::channel('new-post', function ($user) { return Auth::check(); }); reactで resources/js/app.js useEffect(() => { window.Echo.private('new-post').listen('PostCreated', e => { if (window.Laravel.user.following.includes(e.post.user_id)) { setPosts([e.post, ...posts]) } }) }, []) 自分はtwitterアプリを作ったのでメッセージが届いたら表示するために setPosts()で更新をかけています pusherはかなりハマりやすいと思います 公式ドキュメントhttps://readouble.com/laravel/5.8/ja/broadcasting.html
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初学者がLaravelを学び始める2(ルーティング)

サーバーを起動させる php artisan serve このコマンドによってサーバーを起動させることができます。 ルート情報を追加する(htmlを表示する) 次にルーティングを設定していきます。 仮にweb.phpに以下のルート情報を記述した場合は web.php Route::get('/', function () { return view('welcome'); }); 省略 Route::get('hello',function () { return '<html><body><h1>おはよう</h1><p>これは見本です!!</p></body></html>'; }); http ://localhost:8000/helloにアクセスすると web.phpで記述したhtmlを表示することができる。(おはよう これは見本です!!が表示される)これが追加したルート情報による表示である。 ヒアドキュメントを使う ヒアドキュメントはPHPで長文テキストを記述するときに使われるものです。演算子を使って、リスト内に直接記述されたテキストをまとめて変数などに代入できます。 Route::get('/', function () { return view('welcome'); }); 省略 $html = <<<EOF <html> <head> <title>Hello</title> <style> body {font-size:16pt; color:#999; } h1 { font-size:100pt; text-align:right; color:#eee; margin:-40px 0px -50px 0px; } </style> </head> <body> <h1>Hello</h1> <p>This is sample page.</p> <p>これは、サンプルで作ったページです。</p> </body> </html> EOF; Route::get('hello',function () use ($html) { return $html; }); /helloにアクセスするとある程度デザインされた形で表示されます。 こんな感じです↓ このようにヒアドキュメントを使ってhtmlのソースコードをきちんと用意できればroute::getだけである程度のウェブページを作ることはできます。 もちろんこれはこういったこともできるということであって本格的なウェブページを作る場合には別の方法が用意されています。 大切なことはroute::getの役割としてreturnする関数を用意すればそのままウェブページが表示されるという仕組みを理解することです。 ルートパラメーターの利用 route::getでは、アクセスするときにパラメーターを設定して値を渡すことができる. 書き方としては以下のようになる Route::get('/〇〇/{パラメーター}',function($受け取り引数){.....}); 第一引数のアドレス部分に{パラメーター}という形でパラメーターを渡しています。 これによって{パラメーター}に指定されたテキスト部分がパラメーターとして取り出せるようになります。 第二引数の関数ではパラメーターの値を受け取る変数として引数を用意しておきます。 ここは{パラメーター}と同じ名前である必要はありません。{パラメーター}で指定したパラメーターはそのまま関数の引数に渡されます。 パラメーターは複数用意することもでき、関数の引数を複数用意することによって値を受け取ることができます。 パラメーターを利用する 以下のコードに修正する。 今回はヒアドキュメント内に変数を埋め込んで使用するためRoute::getの第二引数に用意するクロージャ(無名関数)ないにヒアドキュメントを移動している。 Route::get('hello/{msg}',function ($msg) { $html = <<<EOF <html> <head> <title>Hello</title> <style> body {font-size:16pt; color:#999; } h1 { font-size:100pt; text-align:right; color:#eee; margin:-40px 0px -50px 0px; } </style> </head> <body> <h1>Hello</h1> <p>{$msg}</p> <p>これは、サンプルで作ったページです。</p> </body> </html> EOF; return $html; }); この状態でhttp://localhost:8000/hello/this_is_test にアクセスした場合 「this_is_test」の部分がパラメーターとして取り出されwebページに表示される。 解説していくと第一引数'hello/{msg}'とhello/の後に{msg}というパラメーターがあり、第二引数のグロージャの引数に$msgがあるため{msg}の値が$msg引数に渡されることになる。 今回は一つのパラメーターだけを用意したが複数になってもやることは同じです。 Route::get('hello/{id}{pass}'),function($id,$pass){省略}); このように書いた場合$idと$passの二つのパラメーターを引数として利用できるようになる。 必須パラメーターと任意パラメーター 今までのパラメーターは基本的に必須パラメーターとなっています。必須パラメーターは指定せずにアクセスするとエラーになってしまいます。用意されているルートパラメーターは必ずつけてアクセスしなければなりません。 パラメーターをつけなくともアクセスするためには任意パラメータを使います。 任意パラメータとは名の通り「任意につけて利用できるパラメータ」です。 第一引数のパラメータの末尾に?をつけることで使用できます。 第二引数の関数では、値が渡される仮引数にデフォルト値を指定し引数が渡されなくとも処理ができるようにしておきます。 今までのコードを任意パラメータを使って一行目を以下のように書き換えると パラメータをつけずにアクセスしたためデフォルトのno message.が表示されています。 Route::get('hello/{msg?}',function ($msg='no message.') { $html = <<<EOF <html> <head> <title>Hello</title> <style> body {font-size:16pt; color:#999; } h1 { font-size:100pt; text-align:right; color:#eee; margin:-40px 0px -50px 0px; } </style> </head> <body> <h1>Hello</h1> <p>{$msg}</p> <p>これは、サンプルで作ったページです。</p> </body> </html> EOF; return $html; });
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【備忘録】intervention/imageを使って画像のリサイズとストレージへの保存

バージョン PHP7.3 Laravel7.30 ライブラリのインストール $ composer require intervention/image 実装 プロバイダ登録 config/app.php 'providers' => [ // 中略 App\Providers\RouteServiceProvider::class, Illuminate\View\ViewServiceProvider::class, // 追加 ], 'aliases' => [ 'InterventionImage' => Intervention\Image\Facades\Image::class, ] /var/www/laravel $ php artisan config:cache 保存 横幅500pxで保存してみる public function store(Request $request) { if (Storage::missing('public/resized')) { Storage::makeDirectory('public/resized'); } $file = $request->file('test_image'); $response = $this->putResizedFile($file, storage_path('app/public/resized/test_image.' . $file->extension()), 500); return response($response, 200); } /** * @param $fileData = リソース * @param string $tempPath = 保存先 * @param int $width サムネイルのサイズ * @return \Intervention\Image\Image */ protected function putResizedFile($fileData, $tempPath, int $width) { return \InterventionImage::make($fileData)->resize($width, null, function ($constraint) { $constraint->aspectRatio(); })->save($tempPath); } 削除 Storageファサードを使う Storage::delete('public/resized/test_image.png'); 注意点 storage/app/public直下にresizedディレクトリがない場合、勝手にディレクトリを作るようなことはなくエラーが出るためStorage::missing()でチェックしている。 storage/laravel.log "Can't write image data to path (/var/www/laravel/storage/app/public/resized/test_image.png)" この点、Storage::put('public/resized/test_image.png')の場合は勝手に作ってくれることと比較。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【備忘録】Laravelでリポジトリパターンを実装する

事前確認 PHP7.3 Laravel7.30 DockerによるLaravel+Vueの環境構築済み 実装 /var/www/laravel $ mkdir -p app/{Repositories,Services}/User $ touch app/Repositories/Repository.php \ > app/Repositories/User/{UserRepositoryInterface,UserRepository}.php \ > app/Services/Service.php > app/Services/User/UserService.php app/Repositories/Repository.php <?php namespace App\Repositories; class Repository { protected $builder; public function getQuery() { return $this->builder->query(); } } app/Repositories/User/UserRepositoryInterface.php <?php namespace App\Repositories\User; interface UserRepositoryInterface { public function findById(string $id); } app/Repositories/User/UserRepository.php <?php namespace App\Repositories\User; use App\Repositories\Repository; use App\Models\User; class UserRepository extends Repository implements UserRepositoryInterface { public function __construct(User $user) { $this->builder = $user; } public function findById(string $id) { return $this->getQuery()->find($id); } } app/Services/Service.php <?php namespace App\Services; class Service { } app/Services/User/UserService.php <?php namespace App\Services\User; use App\Services\Service; use App\Repositories\User\UserRepositoryInterface; class UserService extends Service { protected $userRepository; public function __construct(UserRepositoryInterface $userRepository) { $this->userRepository = $userRepository; } public function findById(string $id) { return $this->userRepository->findById($id); } } app/Models/User.php + protected $primaryKey = 'login_id'; + public $incrementing = false; ServiceProviderの登録 /var/www/laravel $ php artisan make:provider RepositoryServiceProvider app/Providers/RepositoryServiceProvider.php <?php namespace App\Providers; use App\Repositories\User\UserRepository; use App\Repositories\User\UserRepositoryInterface; // use App\Repositories\Admin\AdminRepository; // use App\Repositories\Admin\AdminRepositoryInterface; use Illuminate\Support\ServiceProvider; class RepositoryServiceProvider extends ServiceProvider { /** * Register services. * * @return void */ public function register() { $this->app->bind( UserRepositoryInterface::class, UserRepository::class, ); // 以下の形式で随時追加 // $this->app->bind( // AdminRepositoryInterface::class, // AdminRepository::class, //); } /** * Bootstrap services. * * @return void */ public function boot() { // } } config/app.php 'providers' => [ // 中略  App\Providers\AppServiceProvider::class, App\Providers\AuthServiceProvider::class, // App\Providers\BroadcastServiceProvider::class, App\Providers\EventServiceProvider::class, App\Providers\RouteServiceProvider::class, App\Providers\RepositoryServiceProvider::class, // 追加 // 以下略 /var/www/laravel /var/www/laravel # php artisan config:cache Controllerから呼び出してみる /var/www/laravel $ php artisan make:controller UserController --resource routes/api.php Route::group(['middleware' => 'api'], function () { Route::get('/user/{id}', 'UserController@show')->name('user.show'); }); app/Http/Controllers/UserController.php <?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Services\User\UserService; class UserController extends Controller { protected $userService; public function __construct(UserService $userService) { $this->userService = $userService; } // 中略 /** * Display the specified resource. * * @param string $id * */ public function show(string $id) { $user = $this->userService->findById($id); return response($user, 200); } // 以下略 Adminモデルでも同じように実装する トレイト 管理者の認証機能がある場合は、あるテーブルに対する追加、更新、削除の操作が管理者か一般ユーザーのいずれによるものか判別したい場合の実装。 今回は呼び出さず実装だけ /var/www/laravel $ php artisan make:migration add_column_users database/migrations/2021_04_01_170631_add_column_users.php public function up() { Schema::table('users', function (Blueprint $table) { $table->string('created_by')->nullable()->after('remember_token'); $table->string('updated_by')->nullable()->after('created_by'); $table->string('deleted_by')->nullable()->after('updated_by'); $table ->foreign('created_by') ->references('login_id') ->on('admins') ->onDelete('no action') ->onUpdate('cascade'); $table ->foreign('updated_by') ->references('login_id') ->on('admins') ->onDelete('no action') ->onUpdate('cascade'); $table ->foreign('deleted_by') ->references('login_id') ->on('admins') ->onDelete('no action') ->onUpdate('cascade'); }); } public function down() { Schema::table('users', function (Blueprint $table) { $table->dropForeign('users_deleted_by_foreign'); $table->dropForeign('users_updated_by_foreign'); $table->dropForeign('users_created_by_foreign'); $table->dropColumn('deleted_by'); $table->dropColumn('updated_by'); $table->dropColumn('created_by'); }); } /var/www/laravel $ mkdir app/Models/Traits $ touch app/Models/Traits/{HandledByUser,HandledByAdmin}.php app/Models/Traits/HandledByUser.php <?php namespace App\Models\Traits; use Illuminate\Support\Carbon; trait HandledByUser { public function createByUser(array $attributes) { $this->fill($attributes); $this->save(); return $this; } public function updateByUser(array $attributes) { $this->fill($attributes); $this->save(); return $this; } /** * @return void */ public function deleteByUser() { $this->deleted_by = null; $this->deleted_at = Carbon::now(); $this->save(); } } app/Models/Traits/HandledByAdmin.php <?php namespace App\Models\Traits; use App\Models\Admin; use Illuminate\Support\Carbon; trait HandledByAdmin { /** * @param array $attributes * @param Admin $admin * @return mixed */ public function createByAdmin(array $attributes, Admin $admin) { $this->fill($attributes); $this->created_by = $admin->login_id; $this->updated_by = $admin->login_id; $this->save(); return $this; } /** * @param array $attributes * @param Admin $admin * @return mixed */ public function updateByAdmin(array $attributes, Admin $admin) { $this->fill($attributes); $this->created_by = $admin->login_id; $this->updated_by = $admin->login_id; $this->save(); return $this; } /** * @param Admin $admin * @return void */ public function deleteByAdmin(Admin $admin) { $this->deleted_by = $admin->login_id; $this->deleted_at = Carbon::now(); $this->save(); } } app/Models/User.php <?php namespace App\Models; use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Illuminate\Database\Eloquent\SoftDeletes; use App\Models\Traits\HandledByUser; use App\Models\Traits\HandledByAdmin; class User extends Authenticatable { use Notifiable; use SoftDeletes; use HandledByUser; use HandledByAdmin; protected $primaryKey = 'login_id'; public $incrementing = false; /** * The attributes that are mass assignable. * * @var array */ protected $fillable = [ 'name', 'email', 'login_id', 'password', 'created_by', 'updated_by', 'deleted_by', ]; // 以下略
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【備忘録】Laravel + Vueで管理者認証機能を実装する

バージョン確認 PHP7.3 Laravel7.30 vue2.6.12 Dockerを使ってローカル環境構築済み 一般ユーザーの認証機能は実装済み 手順 一般ユーザーはusersテーブル、管理者画面はadminsテーブルを使う 管理者はlogin_idとpasswordでログイン、ログアウトできるところまでを実装 便宜上管理者の新規登録機能も実装し、実務ではマスター管理などに応用する app.tsに、常に管理者の認証状態をvuexで保持するよう記述する コンポーネント化とcssによるスタイリングは割愛 Laravel側の実装 /var/www/laravel $ php artisan make:model Models/Admin -m database/migrations/2021_03_31_120856_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->id(); $table->string('name'); $table->string('login_id'); $table->string('email'); $table->timestamp('email_verified_at')->nullable(); $table->string('password'); $table->rememberToken(); $table->timestamps(); $table->softDeletes(); $table->unique(['email', 'deleted_at'], 'users_email_deleted_at_unique'); $table->unique(['login_id', 'deleted_at'], 'users_login_id_deleted_at_unique'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('admins'); } } /var/www/laravel $ php artisan migrate config/auth.php 'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], // 追加 '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, ], // 'users' => [ // 'driver' => 'database', // 'table' => 'users', // ], ], 'passwords' => [ 'users' => [ 'provider' => 'users', 'table' => 'password_resets', 'expire' => 60, 'throttle' => 60, ], // 追加 'admins' => [ 'provider' => 'admins', 'table' => 'password_resets', 'expire' => 60, 'throttle' => 60, ], ], app/Models/Admin.php <?php namespace App\Models; use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Illuminate\Database\Eloquent\SoftDeletes; class Admin extends Authenticatable { use Notifiable; use SoftDeletes; protected $table = 'admins'; /** * The attributes that are mass assignable. * * @var array */ protected $fillable = [ 'name', 'email', 'login_id', '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', ]; } app/Http/Middleware/Authenticate.php <?php namespace App\Http\Middleware; use Illuminate\Auth\Middleware\Authenticate as Middleware; use Illuminate\Support\Facades\Route; // 追加 class Authenticate extends Middleware { protected function redirectTo($request) { if (!$request->expectsJson()) { // 変更 if (Route::is('admin.*')) { return route('admin.login'); } else { return route('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()) {     // 変更 if ($guard === 'user') { return redirect()->route('currentUser'); } elseif ($guard === 'admin') { return redirect()->route('currentAdmin'); } } return $next($request); } } /var/www/laravel $ mkdir -p app/Http/Controllers/Admin/Auth $ cp app/Http/Controllers/Auth/{RegisterController,LoginController}.php app/Http/Controllers/Admin/Auth/ routes/api.php Route::group(['middleware' => 'api'], function () { Route::get('/current_user', function () { return Auth::user(); })->name('currentUser'); Route::namespace('Auth')->group(function() { Route::post('/register', 'RegisterController@register')->name('register'); Route::post('/login', 'LoginController@login')->name('login'); Route::middleware('auth')->group(function() { Route::post('/logout', 'LoginController@logout')->name('logout'); }); }); // 追加 Route::get('/current_admin', function () { return Auth::guard('admin')->user(); })->name('currentAdmin'); Route::namespace('Admin')->prefix('admin')->name('admin.')->group(function () { Route::post('register', 'Auth\RegisterController@register')->name('register'); Route::post('login', 'Auth\LoginController@login')->name('login'); Route::post('logout', 'Auth\LoginController@logout')->name('logout'); }); Route::middleware('auth')->group(function () { // 一般認証が必要なAPI }); Route::middleware('auth:admin')->group(function () { // 管理者認証が必要なAPI }); }); app/Http/Controllers/Admin/Auth/RegisterController.php <?php namespace App\Http\Controllers\Admin\Auth; use App\Http\Controllers\Controller; use App\Providers\RouteServiceProvider; use App\Models\Admin; use Illuminate\Foundation\Auth\RegistersUsers; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Validator; use Illuminate\Http\Request; use Illuminate\Validation\Rule; use Illuminate\Support\Facades\Auth; class RegisterController extends Controller { /* |-------------------------------------------------------------------------- | Register Controller |-------------------------------------------------------------------------- | | This controller handles the registration of new users as well as their | validation and creation. By default this controller uses a trait to | provide this functionality without requiring any additional code. | */ use RegistersUsers; protected function guard() { return Auth::guard('admin'); } // 中略 /** * Create a new controller instance. * * @return void */ public function __construct() { $this->middleware('guest:admin')->except('logout'); } /** * Get a validator for an incoming registration request. * 'users'を'admins'に変えるだけ * @param array $data * @return \Illuminate\Contracts\Validation\Validator */ protected function validator(array $data) { return Validator::make($data, [ 'name' => ['required', 'string', 'max:255'], 'login_id' => ['required', 'regex:/^[\w\-\._]+$/i', 'min:8', 'max:20', Rule::unique('admins', 'login_id')->whereNull('deleted_at')], 'email' => ['required', 'string', 'email', 'max:255', Rule::unique('admins', 'email')->whereNull('deleted_at')], 'password' => ['required', 'string', 'min:8', 'max:20', 'confirmed'], ]); } /** * @param Request $request * @param Admin $admin * @return Admin */ protected function registered(Request $request, $admin) { return $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 App\Models\Admin; use Illuminate\Support\Facades\Auth; class LoginController extends Controller { /* |-------------------------------------------------------------------------- | Login Controller |-------------------------------------------------------------------------- | | This controller handles authenticating users for the application and | redirecting them to your home screen. The controller uses a trait | to conveniently provide its functionality to your applications. | */ use AuthenticatesUsers; /** * Where to redirect users after login. * * @var string */ protected $redirectTo = RouteServiceProvider::HOME; protected function guard() { return Auth::guard('admin'); } /** * Create a new controller instance. * * @return void */ public function __construct() { $this->middleware('guest:admin')->except('logout'); } /** * @return string */ public function username() { return 'login_id'; } /** * @param Request * @param Admin $admin * @return Admin */ protected function authenticated(Request $request, $admin) { return $admin; } /** * @param Request $request */ protected function loggedOut(Request $request) { Auth::logout(); $request->session()->regenerate(); return response()->json(); } } Vue側の実装 ~/workspace/myapp/resources/ts $ cp types/Models/User.ts types/Models/Admin.ts \ > store/auth.ts store/admin.ts resources/ts/types/Models/Admin.ts - export type User = { + export type Admin = { id: number name: string [k: string]: any } | null resources/ts/store/admin.ts - import { User } from '@/types/Models/User' + import { Admin } from '@/types/Models/Admin' import { RegisterRequest, LoginRequest } from '@/types/Auth' import { UNPROCESSABLE_ENTITY } from '@/util' import router from '@/router' export type State = { - user: User + admin: Admin } const state = { - user: null, + admin: null, } const getters = { - isLogin: (state: State): boolean => !!state.user, - userId: (state: State): number | null => (state.user ? state.user.id : null), - userName: (state: State): string => (state.user ? state.user.name : ''), + isLogin: (state: State): boolean => !!state.admin, + adminId: (state: State): number | null => (state.admin ? state.admin.id : null), + adminName: (state: State): string => (state.admin ? state.admin.name : ''), } const mutations = { - setUser(state: State, user: User): void { - state.user = user + setAdmin(state: State, admin: Admin): void { + state.admin = admin }, } const actions = { async register(context, data: RegisterRequest): Promise<void> { await window.axios - .post('/api/register', data) - .then((response) => { - context.commit('setAdmin', response.data) + .post('/api/admin/register', data) + .then((response) => { + context.commit('setUser', response.data) context.commit( 'error/setErrors', { messages: null, status: null, }, { root: true } ) - router.push('/mypage') + router.push('/admin/mypage') }) .catch((err) => { if (err.response.status === UNPROCESSABLE_ENTITY) { context.commit( 'error/setErrors', { messages: err.response.data.errors, status: err.response.status, }, { root: true } ) } }) }, async login(context, data: LoginRequest): Promise<void> { await window.axios - .post('/api/login', data) - .then((response) => { + .post('/api/admin/login', data) + .then((response) => { - context.commit('setUser', response.data) + context.commit('setAdmin', response.data) context.commit( 'error/setErrors', { messages: null, status: null, }, { root: true } ) - router.push('/mypage') + router.push('/admin/mypage') }) .catch((err) => { if (err.response.status === UNPROCESSABLE_ENTITY) { context.commit( 'error/setErrors', { messages: err.response.data.errors, status: err.response.status, }, { root: true } ) } else { context.commit('error/setErrors', { messages: err.response, status: err.response.status, }, { root: true } ) } }) }, async logout(context): Promise<void> { await window.axios - .post('/api/logout') + .post('/api/admin/logout') .then((response) => { - context.commit('setUser', null) - router.push('/login') + context.commit('setAdmin', null) + router.push('/admin/login') }) .catch((err) => { context.commit( 'error/setErrors', { messages: err.response, status: err.response.status, }, { root: true } ) }) }, - async currentUser(context): Promise<void> { + async currentAdmin(context): Promise<void> { await window.axios - .get('/api/current_user') - .then((response) => { - const user = response.data || null - context.commit('setUser', user) + .get('/api/admin/current_admin') + .then((response) => { + const admin = response.data || null + context.commit('setAdmin', admin) }) .catch((err) => { context.commit( 'error/setErrors', { messages: err.response.data.errors, status: err.response.status, }, { root: true } ) }) }, } export default { namespaced: true, state, getters, mutations, actions, } resources/ts/store/index.ts import Vue from 'vue' import Vuex from 'vuex' import auth from './auth' + import admin from './admin' import error from './error' Vue.use(Vuex) const store = new Vuex.Store({ modules: { auth, + admin, error }, }) export default store resources/ts/app.ts import Vue from 'vue' // ルーティングの定義をインポートする import router from './router' // ルートコンポーネントをインポートする import App from './App.vue' import store from './store' import bootstrap from './bootstrap' bootstrap() const createApp = async () => { await store.dispatch('auth/currentUser') + await store.dispatch('admin/currentAdmin') new Vue({ el: '#app', router, store, render: (h) => h(App), }) } createApp() view ~/workspace/myapp/resources/ts $ mkdir -p layouts/admin pages/admin/mypage $ touch layouts/admin/{Default,MyPageLayout}.vue \ > pages/admin/mypage/Index.vue $ cp pages/{Register,Login}.vue pages/admin/ resources/ts/layouts/admin/Default.vue <template> <div> <header> <nav> <ul v-if="!isLogin"> <li><router-link tag="a" to="/admin/login">管理者ログイン</router-link></li> <li><router-link tag="a" to="/admin/register">管理者登録</router-link></li> </ul> <ul v-else> <li @click="logout">ログアウト</li> </ul> </nav> </header> <main> <div class="container"> <router-view /> </div> </main> </div> </template> <script lang="ts"> import { Vue, Component } from 'vue-property-decorator' import { mapGetters } from 'vuex' @Component({ computed: { ...mapGetters({ isLogin: 'admin/isLogin' }) } }) export default class AdminLayout extends Vue { async logout(): Promise<void> { await this.$store.dispatch('admin/logout') } } </script> resources/ts/layouts/admin/MyPageLayout.vue <template> <div> <header> <nav> <ul v-if="!isLogin"> <li><router-link tag="a" to="/admin/login">管理者ログイン</router-link></li> <li><router-link tag="a" to="/admin/register">管理者登録</router-link></li> </ul> <ul v-else> <li @click="logout">ログアウト</li> </ul> </nav> </header> <main> <div class="container"> <router-view :admin="admin" /> </div> </main> </div> </template> <script lang="ts"> import { Vue, Component } from 'vue-property-decorator' import { mapState, mapGetters } from 'vuex' @Component({ computed: { ...mapState({ admin: state => state.admin.admin }), ...mapGetters({ isLogin: 'admin/isLogin', }), } }) export default class AdminMyPageLayout extends Vue { async logout(): Promise<void> { await this.$store.dispatch('admin/logout') } } </script> resources/ts/pages/admin/Login.vue <template> <div> <h1>管理者ログイン</h1> <form @submit.prevent="login"> <div v-for="(formItem, index) in submitData" :key="index"> <p v-if="formItem.errorMessages">{{ formItem.errorMessages[0] }}</p> <input v-if="formItem.type==='text'" type="text" v-model="formItem.value" :placeholder="formItem.placeholder" @keyup="validation(formItem)" required > <input v-else-if="formItem.type==='password'" type="password" v-model="formItem.value" @keyup="validation(formItem)" :placeholder="formItem.placeholder" required > </div> <div> <button :disabled="disabled">ログイン</button> </div> </form> </div> </template> <script lang="ts"> import { Component, Mixins } from 'vue-property-decorator' import { Form } from '@/types/Form' import FormValidator from '@/mixins/formValidator' @Component export default class Login extends Mixins(FormValidator) { submitData: Form = { login_id: { name: 'login_id', type: 'text', value: '', rules: [(val: string) => !!val || 'ログインIDは必須です'], errorMessages: [], placeholder: 'login_id', }, password: { name: 'password', type: 'password', value: '', rules: [(val: string) => !!val || 'パスワードは必須です'], errorMessages: [], placeholder: 'at least 8 chars.', }, } async login(): Promise<void> { if (this.disabled) { Object.keys(this.submitData).forEach((key: string): void => { this.validation(this.submitData[key]) }) } else { const formData = new FormData() formData.append('login_id', this.submitData.login_id.value) formData.append('password', this.submitData.password.value) await this.$store.dispatch('admin/login', formData) } } } </script> resources/ts/pages/admin/Register.vue <template> <div class="container"> <h1>管理者登録</h1> <form @submit.prevent="register"> <div v-for="(formItem, index) in submitData" :key="index"> <p v-if="formItem.errorMessages">{{ formItem.errorMessages[0] }}</p> <input v-if="formItem.type==='text'" type="text" v-model="formItem.value" :placeholder="formItem.placeholder" @keyup="validation(formItem)" required > <input v-else-if="formItem.type==='email'" type="email" v-model="formItem.value" :placeholder="formItem.placeholder" @keyup="validation(formItem)" required > <input v-else-if="formItem.type==='password'" type="password" v-model="formItem.value" @keyup="validation(formItem)" :placeholder="formItem.placeholder" required > </div> <div> <button :disabled="disabled">管理者登録</button> </div> </form> </div> </template> <script lang="ts"> import { Component, Mixins} from 'vue-property-decorator' import { Form } from '@/types/Form' import FormValidator from '@/mixins/formValidator' @Component export default class Register extends Mixins(FormValidator) { submitData: Form = { name: { name: 'お名前', type: 'text', value: '', rules: [ (val: string) => !!val || '必須項目です', (val: string) => (val.length >= 2 && val.length <= 20) || '2文字以上20文字以下で設定してください', ], errorMessages: [], placeholder: 'your nickname', }, email: { name: 'メールアドレス', type: 'email', value: '', rules: [ (val: string) => !!val || '必須項目です', (val: string) => !!val.match(/^\w+[\w\-\._]*\w+@\w+[\w\-\._]*\.\w+[\w\-\._]*[A-Za-z]+$/i) || 'メールアドレスの形式が正しくありません' ], errorMessages: [], placeholder: 'sample@example.com', }, login_id: { name: 'ログインID', type: 'text', value: '', rules: [ (val: string) => !!val || '必須項目です', (val: string) => !!val.match(/^[\w\-\._]+$/g) || '半角英数字、「.」、「_」、「-」のみ使えます', (val: string) => val.length >= 8 && val.length <= 20 || '8文字以上20文字以下で入力してください' ], errorMessages: [], placeholder: 'login ID', }, password: { name: 'パスワード', type: 'password', value: '', rules: [ (val: string) => !!val || '必須項目です', (val: string) => !!val.match(/^(?=.*?[a-z])(?=.*?[A-Z])(?=.*?\d)[a-zA-Z\d]+$/) || '半角小文字、半角大文字、数字をそれぞれ1種類以上含めてください', (val: string) => (val.length >= 8 && val.length <= 20) || '8文字以上20文字以下で設定してください', ], errorMessages: [], placeholder: 'at leeast 8 chars.', }, password_confirmation: { name: 'パスワード(確認用)', type: 'password', value: '', rules: [ (val: string) => !!val || '必須項目です', (val: string) => this.submitData.password.value === val || 'パスワードが一致しません' ], errorMessages: [], placeholder: 'confirm', }, } async register(): Promise<void> { if (this.disabled) { Object.keys(this.submitData).forEach((key: string): void => { this.validation(this.submitData[key]) }) } else { const formData = new FormData() formData.append('name', this.submitData.name.value) formData.append('email', this.submitData.email.value) formData.append('login_id', this.submitData.login_id.value) formData.append('password', this.submitData.password.value) formData.append('password_confirmation', this.submitData.password_confirmation.value) await this.$store.dispatch('admin/register', formData) } } } </script> resources/ts/pages/admin/mypage/Index.vue <template> <div> <p v-if="!!admin">{{ admin.name }}さん、管理者画面へようこそ</p> </div> </template> <script lang="ts"> import { Vue, Component, Prop } from 'vue-property-decorator' import { Admin } from '@/types/Models/Admin' @Component export default class AdminMyPageIndex extends Vue { @Prop({ type: Object, required: true, default: null }) admin!: Admin } </script> resources/ts/router.ts import Vue from 'vue' import VueRouter from 'vue-router' // ページコンポーネントをインポートする import Index from './pages/Index.vue' // レイアウト import DefaultLayout from './layouts/Default.vue' import MyPageLayout from './layouts/MyPageLayout.vue' + import AdminDefaultLayout from './layouts/admin/Default.vue' + import AdminMyPageLayout from './layouts/admin/MyPageLayout.vue' // ユーザー画面 import Login from './pages/Login.vue' import Register from './pages/Register.vue' import MyPageIndex from '@/pages/mypage/Index.vue' // 管理者画面 + import AdminLogin from '@/pages/admin/Login.vue' + import AdminRegister from '@/pages/admin/Register.vue' + import AdminMyPageIndex from '@/pages/admin/mypage/Index.vue' import store from './store' // VueRouterプラグインを使用する Vue.use(VueRouter) // パスとコンポーネントのマッピング const routes = [ { path: '/', component: DefaultLayout, children: [ { path: '/', component: Index, }, { path: '/register', component: Register, beforeEnter (to, from, next) { if (store.getters['auth/isLogin']) { next('/') } else { next() } } }, { path: '/login', component: Login, beforeEnter (to, from, next) { if (store.getters['auth/isLogin']) { next('/') } else { next() } } }, ], }, { path: '/mypage', component: MyPageLayout, beforeEnter (to, from, next) { if (!store.getters['auth/isLogin']) { next('/login') } else { next() } }, children: [ { path: '/', component: MyPageIndex, } ], }, // ↓追加 { path: '/admin', component: AdminDefaultLayout, children: [ { path: '/', component: AdminRegister, beforeEnter (to, from, next) { if (store.getters['admin/isLogin']) { next('/admin/mypage') } else { next('/admin/login') } } }, { path: 'register', component: AdminRegister, beforeEnter (to, from, next) { if (store.getters['admin/isLogin']) { next('/admin/mypage') } else { next() } } }, { path: 'login', component: AdminLogin, beforeEnter (to, from, next) { if (store.getters['admin/isLogin']) { next('/admin/mypage') } else { next() } } }, ], }, { path: '/admin/mypage', component: AdminMyPageLayout, beforeEnter (to, from, next) { if (!store.getters['admin/isLogin']) { next('/admin/login') } else { next() } }, children: [ { path: '/', component: AdminMyPageIndex, } ], }, ] const router = new VueRouter({ mode: 'history', //URL中の#(ハッシュ)を消す routes }) export default router
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【PHP】<?= ?>の意味:大なり クエスチョン(はてな)イコール

Laravelのビューを見ていて<?= ?>という記述にでくわした。何をしているか解読するまでに時間がかかったのでそのメモ。 <?php ?>, <? ?>, '<?= ?>'の違いについても。 結論 先に結論から。それぞれhtmlファイルの中でPHPを記述するためのタグ。 <?php ?>や<?php echo ?>の省略形。 コード 共通 概要 <?php ?> PHPを記述する <? ?> <?php ?> XMLの記述と被るため使わない <?= ?> <?php echo ?> 記述したPHPを文字列として出力 (echo)する ▼Laravelのブレード内で使う場合 Laravelのブレード(.blade.php)を使っているなら以下は同じになる。(例として$variableという変数を文字列として表示する場合) <?= $variable ?> ↑↓同じ @php echo $variable @endphp <?= ?>の意味 <?php echo ?>の省略形。echoの後ろに変数を記述すると、その中身は出力される。 ▼使い方の例 変数$variableを文字列として表示する。 <?= $variable ?> 例2 cssをstyleタグの中に直接記述する cssをlinkタグではなく、styleタグの中に直接記述することで、レンダリングブロックを回避しページの読み込み速度を上げる。 <style type="text/css"> <?= file_get_contents(public_path('css/app.css')); ?> </style> ・file_get_contents(ファイルパス or URL) 指定したファイルやURLの内容を丸ごととってくる。PHPの関数。 ・public_path(public配下のファイルパス) public_path()はpublicディレクトリの完全なパスを返す。引数に配下のパスを入れると、publicディレクトリのパスに引数を追加した値を返す。Laravelのヘルパ。 <?php ?> と <? ?> <?php ?>の省略形として<? ?>と書くこともできるが、XMLの記述と被るため基本使わない。 参考リンク
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel API備忘録クエリ編(その③ リレーションシップ)

One to One AddressテーブルとUserテーブルにOne to Oneのリレーションを作る AddressModel public function user(){ return $this->belongsTo("App\User","user_id","id"); //2つ目と3つ目の引数は省略可 } 2つ目と3つ目は入れなくても、Laravelの規約上、 WHERE addresses.user_id = users.idみたいなWHERE句を 作ってくれるようだ。 でもってUserモデルにも追加。 UserModel public function address(){ return $this->hasOne("App\Address","user_id","id"); // } こっちはhasOne()を使い、第二引数はuser_idにする。 でもってマイグレーションにも追加。 $table->unsignedBigInteger("user_id"); $table->foreign("user_id")->references("id") ->on("users")->onDelete("cascade"); //どれが外部キーか、その外部キーはどのカラムを参照するか、 //どのテーブルを参照するか、デリート時にどうなるかを記述 ちなみにone to oneリレーションのseedingにはちょっとしたコツが要る。 userSeeder factory(App\User::class, 3)->create() ->each(function($user){ $user->address() ->save(factory(App\Address::class)->make()); }); こうすればUserとAddressの関係がone to oneのシーディングを 行ってくれる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む