20200824のPHPに関する記事は11件です。

Laravel ログインしていない状態でもURLを直接入れると見れてしまう現象を一行で解決させる方法(Middleware)

未ログインユーザーに記事投稿画面を表示しないようにするためには、

web.php
Route::resource('/articles', 'ArticleController')->except(['index'])->middleware('auth'); 

上記にmiddlewareを入れるだけでログインしていない状態であれば、

ログイン画面に遷移されます。

.
└──laravel
    └── app
        └── Http
            └── Middleware
                └── Authenticate.php
Authenticate
<?php

namespace App\Http\Middleware;

use Illuminate\Auth\Middleware\Authenticate as Middleware;

class Authenticate extends Middleware
{
    /**
     * Get the path the user should be redirected to when they are not authenticated.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return string
     */
    protected function redirectTo($request)
    {
        if (! $request->expectsJson()) {
            return route('login');
        }
    }
}

loginのところをいじれば遷移されるところを、アレンジすることができます。

以上です:relaxed:

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

GoogleDriveAPI を使用するための設定(認証情報の作成)手順

設定した手順の備忘録

プロジェクト作成

https://console.developers.google.com/apis/dashboard

  • Googleにログインし、上記URLにアクセス
  • 左上のメニューから、「IAMと管理 > リソースの管理」を押下
  • 「プロジェクトを作成」リンクを押下
  • プロジェクト名を入力し、(デフォルトで入力されている)「作成」押下

プロジェクトで使用するAPIを指定する

  • Google Cloudプラットフォームの画面上部からプロジェクトが選択されている状態で 「APIとサービス > ライブラリ」または「APIとサービス > API とサービスの有効化」を押下
  • Google Drive API を選択し、「有効する」を押下

認証情報の作成

  • リダイレクト後の画面(APIの概要画面)で「認証情報の作成」を押下 または Google Cloudプラットフォームの画面上部からプロジェクトを選択した状態で、「認証情報 > 認証情報の作成」を押下
  • 「OAuthクライアントID」を選択 または 「クライアントID」リンクを押下
    • 初回の場合は「OAuth クライアント ID を作成するには、まず同意画面でサービス名を設定する必要があります。」という文言と「同意画面を設定」というリンクが表示されるので、リンクから同意画面を設定
  • OAuth クライアント ID の作成画面で「ウェブアプリケーション」を選択
  • OAuthクライアントIDの名前を入力
  • 承認済みの JavaScript 生成元にサイトのURLを入力
  • 承認済みのリダイレクト URIに認証後リダイレクトさせたいURLを入力
  • 「作成」を押下し保存する

OAuth同意画面の設定

  • OAuth同意画面を開き、「外部」を選択
  • アプリケーション名を入力し、スコープの設定を行い保存
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP Quickstartを使用したGoogleDriveAPIの導入手順

GoogleDriveAPIを使用することがあったので備忘録

Quickstartを使用して接続の設定

Google Client Libraryのインストール

  • 下記をコマンドラインで入力
composer require google/apiclient:^2.0

認証情報をダウンロード

※すでにOAuth 2.0 クライアントの設定を済ませている場合
(未設定の場合は先にOAuth 2.0 クライアントの設定を行う)

  • Google Cloudプラットフォームから「認証情報」を押下し、認証情報画面を開く
  • OAuth 2.0 クライアント ID 一覧の中から、作成したIDの右端のダウンロードボタンを押下し、ファイルをダウンロードする

サンプルプログラムの作成

PHP Quickstart | Google Drive API | Google Developers
https://developers.google.com/drive/api/v3/quickstart/php

  • Google Drive API のドキュメント内から、PHP Quickstart のページを開く
  • Step 3: Set up the sample に記載されている quickstart.php をコピーする
  • OAuth 同意画面で設定したアプリケーション名をsetApplicationName()に指定、ダウンロードした認証情報のパスをsetAuthConfig()に指定する
quickstart.php
<?php
require __DIR__ . '/vendor/autoload.php';

if (php_sapi_name() != 'cli') {
    throw new Exception('This application must be run on the command line.');
}

/**
 * Returns an authorized API client.
 * @return Google_Client the authorized client object
 */
function getClient()
{
    $client = new Google_Client();
    $client->setApplicationName('Google Drive API PHP Quickstart'); // 設定したアプリケーション名を指定
    $client->setScopes(Google_Service_Drive::DRIVE_METADATA_READONLY);
    $client->setAuthConfig('credentials.json'); // ここに取得したJSONのパス
    $client->setAccessType('offline');
    $client->setPrompt('select_account consent');

    // Load previously authorized token from a file, if it exists.
    // The file token.json stores the user's access and refresh tokens, and is
    // created automatically when the authorization flow completes for the first
    // time.
    $tokenPath = 'token.json';
    if (file_exists($tokenPath)) {
        $accessToken = json_decode(file_get_contents($tokenPath), true);
        $client->setAccessToken($accessToken);
    }

サンプルプログラムの実行

  • 下記をコマンドラインから実行
php quickstart.php

接続の同意

  • サンプルプログラム実行後、URLが表示されるのでコピーし、ブラウザからアクセスする
  • アクセスすると、OAuth 2.0の同意画面が表示され、画面に従って許可を行う
    • アクセスし、「このアプリは確認されていません。」と表示された場合は、左下の「詳細」から「〇〇(安全ではないページ)に移動」から移動する
  • 許可を行い、「このコードをコピーし、アプリケーションに切り替えて貼り付けてください。」と書かれた下のコードをコピーする
  • コマンドライン上のEnter verification code: に貼り付け実行する
[hoge@hogehoge hoge]$ php quickstart.php
Open the following link in your browser:
https://accounts.google.com/o/oauth2/auth?response_type........
Enter verification code: 
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

プロパティの値の代入はアクセサメソッドを使う

クラスのプロパティに値を入れる

クラスでインスタンスを作り、プロパティに値を入れる場合は
インスタンス->プロパティ名 = 値;で実装可能。

CalBMI.php
class CalBMI {

  public $_weight; // 体重(kg)
  public $_height; // 身長(m)

  public function __construct($weight,$height){
      $this->_weight = $weight;
      $this->_height = $height;
  }

  public function getBMI(){
      return $this->_weight / ($this->_height * $this->_height);
  }

}
accessor1.php
require_once 'CalBMI.php';

$calBMI = new CalBMI(60,1.6);
echo $calBMI->getBMI(); //23.4375

しかし、ユーザーはいつも自分が想定した通りには動いてくれない。
なので、数字を入れるところに文字を入れる場合もある

accessor1.php
require_once 'CalBMI.php';

$calBMI = new CalBMI(60,'身長');
echo $calBMI->getBMI(); // Warning: A non-numeric value encountered

このような不祥事を防ぐため、クラスのプロパティは基本privateでユーザーが直接アクセスできないようにし、
アクセサメソッドを使ってアクセスさせる.

CalBMI.php
class CalBMI {

  private $_weight; // 体重(kg)
  private $_height; // 身長(m)

  public function __construct($weight,$height){
      $this->setWeight($weight);
      $this->setHeight($height);
  }

  public function setWeight($weight){
      $this->_weight = is_numeric($weight) ? $weight : 60; // 数字以外のものが入るとDefaultで60を体重に設定
  }

  public function setHeight($height){
      $this->_height = is_numeric($height) ? $height : 1.6; // 数字以外のものが入るとDefaultで1.6を身長に設定
  }

  public function getWeight(){
      return $this->_weight;
  }

  public function getHeight(){
      return $this->_height;
  }

  public function getBMI(){
      return $this->getWeight() / ($this->getHeight() * $this->getHeight());
  }

}
accessor2.php
require_once 'CalBMI.php';

$calBMI = new CalBMI(60,'身長');
echo $calBMI->getBMI(); // 23.4375

参考

独習PHP第3版

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

Laravelでroute一行のみでCRUDを定義する方法

記事投稿画面や記事登録処理などの記事関連機能のルーティングについて、ひとつひとつ自分で考えて設計することもできますが、

  • 一覧表示
  • 個別表示
  • 新規登録
  • 更新
  • 削除

といった、よく使われる機能のルーティングをひとまとめにしたメソッドがLaravelでは用意されています。

Route::resource('/articles', 'ArticleController'); 

と定義してあげるだけで一気にCRUDのuri等を設定してくれます。

+--------+-----------+-------------------------+------------------+------------------------------------------------------------------------+------------+
| Domain | Method    | URI                     | Name             | Action                                                                 | Middleware |
+--------+-----------+-------------------------+------------------+------------------------------------------------------------------------+------------+
|        | GET|HEAD  | /                       |                  | App\Http\Controllers\ArticleController@index                           | web        |
|        | POST      | articles                | articles.store   | App\Http\Controllers\ArticleController@store                           | web        |
|        | GET|HEAD  | articles                | articles.index   | App\Http\Controllers\ArticleController@index                           | web        |
|        | GET|HEAD  | articles/create         | articles.create  | App\Http\Controllers\ArticleController@create                          | web        |
|        | DELETE    | articles/{article}      | articles.destroy | App\Http\Controllers\ArticleController@destroy                         | web        |
|        | PUT|PATCH | articles/{article}      | articles.update  | App\Http\Controllers\ArticleController@update                          | web        |
|        | GET|HEAD  | articles/{article}      | articles.show    | App\Http\Controllers\ArticleController@show                            | web        |
|        | GET|HEAD  | articles/{article}/edit | articles.edit    | App\Http\Controllers\ArticleController

とても便利なので、是非使ってみてください。

以上です:relaxed:

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

PhpSpreadsheetでメモリ爆喰い または、変更してないセルの書き込みで落ちる場合の対処法

writeCellFormula, Cell , Coordinate 関係でエラー落ちする場合

どうやら、PhpSpreadsheetさんは式を解決してからファイルを保存してくれるらしい。
しかし、複雑な式を書いていると無限ループになったり、うまく計算できず落ちてしまうっぽい
Excel上でならうまく処理してくれる場合でも、だ。

そういう時(XlsxWriterに対して)は

$writer->setPreCalculateFormulas(false);

を加えて事前計算処理をスキップさせる事ができるようだ。

https://phpspreadsheet.readthedocs.io/en/latest/topics/reading-and-writing-to-file/
ここを読んでいて見つけた。

次同じようなハマり方をした同志の為になればと。

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

PMMPでチームデスマッチプラグインをサクッと作る

まえがき

ライブラリを使ってサーッとチームデスマッチつくります

作成するプラグインは、他のプラグインに一切干渉しないようにしています

完成品 はここに

作成するプラグインの内容

/createでゲームを作成し、/joinで参加

1キルごとにスコアが増えていき、設定した上限に達するか制限時間を超えたら、試合終了というもの

BossBarでタイマーを、Scoreboardで試合の状況を表示します

ライブラリのインストール

使用するライブラリはteam_game_system

team_game_systemの依存関係で

form_builder
slot_menu_system も導入します。

スコアボードのAPIで scoreboard_system を使用します
ボスバーのAPIで bossbar_system を使用します

コマンドプロンプトでpluginsフォルダに移動して

git clone https://github.com/MineDeepRock/team_game_system
git clone https://github.com/MineDeepRock/form_builder
git clone https://github.com/MineDeepRock/slot_menu_system
git clone https://github.com/MineDeepRock/scoreboard_system
git clone https://github.com/MineDeepRock/bossbar_system

これでOK

プロジェクトの作成

TeamDeathMatchというフォルダを作成します。

plugin.yml
name: TeamDeathMatch
main: TeamDeathMatch\Main
version: 1.0.0
api: 3.0.0

depend:
  - TeamGameSystem
  - FormBuilder
  - SlotMenuSystem

Composerでの補完

Composerで補完したい人だけ見てください

composer.json
{
  "name": "あなたの名前/TeamDeathMatch",
  "authors": [
    {
      "name": "あなたの名前"
    }
  ],
  "autoload": {
    "psr-4": {
      "": [
        "src/"
      ]
    }
  },
  "repositories": [
    {
      "type": "git",
      "name": "suinua/form_builder",
      "url": "https://github.com/MineDeepRock/form_builder"
    },
    {
      "type": "git",
      "name": "suinua/slot_menu_system",
      "url": "https://github.com/MineDeepRock/slot_menu_system"
    },
    {
      "type": "git",
      "name": "suinua/scoreboard_system",
      "url": "https://github.com/MineDeepRock/scoreboard_system"
    },
    {
      "type": "git",
      "name": "suinua/bossbar_system",
      "url": "https://github.com/MineDeepRock/bossbar_system"
    },
    {
      "type": "git",
      "name": "suinua/team_game_system",
      "url": "https://github.com/MineDeepRock/team_game_system"
    }
  ],
  "require": {
    "pocketmine/pocketmine-mp": "3.14.2",
    "suinua/form_builder": "dev-master",
    "suinua/slot_menu_system": "dev-master",
    "suinua/team_game_system": "dev-master",
    "suinua/scoreboard_system": "dev-master",
    "suinua/bossbar_system": "dev-master"
  }
}

コードを書く

Mainクラスの作成

src\tdm\Main.php
<?php

namespace tdm;

use pocketmine\event\Listener;
use pocketmine\plugin\PluginBase;

class Main extends PluginBase implements Listener
{
    public function onEnable() {
        $this->getServer()->getPluginManager()->registerEvents($this, $this);
    }
}

Formからゲームを作成する

ゲームを作成するフォームを作ります

下準備としてsrc/GameTypeList.phpを作成します

src\tdm\GameTypeList.php
<?php


namespace tdm;


use team_game_system\model\GameType;

class GameTypeList
{
    static function TeamDeathMatch(): GameType {
        return new GameType("TeamDeathMatch");
    }
}

src\tdm\CreateTeamDeathMatchFormを作成します

src\tdm\CreateTeamDeathMatchForm.php
<?php


namespace tdm;


use form_builder\models\custom_form_elements\Input;
use form_builder\models\custom_form_elements\Label;
use form_builder\models\CustomForm;
use pocketmine\Player;
use pocketmine\scheduler\ClosureTask;
use pocketmine\scheduler\TaskScheduler;
use pocketmine\utils\TextFormat;
use team_game_system\model\Game;
use team_game_system\model\Score;
use team_game_system\model\Team;
use team_game_system\TeamGameSystem;

class CreateTeamDeathMatchForm extends CustomForm
{

    private $scheduler;

    private $timeLimit;
    private $maxPlayersCount;
    private $maxScore;

    public function __construct(TaskScheduler $scheduler) {
        $this->scheduler = $scheduler;

        $this->maxScore = new Input("勝利判定スコア", "", "20");
        $this->maxPlayersCount = new Input("人数制限", "", "");
        $this->timeLimit = new Input("制限時間(秒)", "", "300");

        parent::__construct("", [
            new Label("無い場合は空白でお願いします"),
            $this->maxScore,
            $this->maxPlayersCount,
            $this->timeLimit,
        ]);
    }

    function onSubmit(Player $player): void {
        $maxScore = $this->maxScore->getResult();
        $maxScore = $maxScore === "" ? null : new Score(intval($maxScore));

        $maxPlayersCount = $this->maxPlayersCount->getResult();
        $maxPlayersCount = $maxPlayersCount === "" ? null : intval($maxPlayersCount);

        $timeLimit = $this->timeLimit->getResult();
        $timeLimit = $timeLimit === "" ? null : intval($timeLimit);

        //チーム
        $teams = [
            Team::asNew("Red", TextFormat::RED),
            Team::asNew("Blue", TextFormat::BLUE),
        ];

        //マップを選択(あとからMinecraft内でマップを登録します)
        $map = TeamGameSystem::selectMap("city", $teams);
        //ゲームを作成
        $game = Game::asNew(GameTypeList::TeamDeathMatch(), $map, $teams, $maxScore, $maxPlayersCount, $timeLimit);
        //ゲームを登録
        TeamGameSystem::registerGame($game);


        //試合がひらかれてから20秒後にスタートされるように
        $gameId = $game->getId();
        $this->scheduler->scheduleDelayedTask(new ClosureTask(function (int $tick) use ($gameId): void {
            TeamGameSystem::startGame($this->scheduler, $gameId);
        }), 20 * 20);
    }

    function onClickCloseButton(Player $player): void { }
}

Formからゲームに参加する

src/TeamDeathMatchListForm.php
<?php


namespace tdm;


use form_builder\models\simple_form_elements\SimpleFormButton;
use form_builder\models\SimpleForm;
use pocketmine\Player;
use pocketmine\utils\TextFormat;
use team_game_system\TeamGameSystem;

class TeamDeathMatchListForm extends SimpleForm
{

    public function __construct() {
        $buttons = [];

        foreach (TeamGameSystem::findGamesByType(GameTypeList::TeamDeathMatch()) as $game) {
            $gameId = $game->getId();
            $map = $game->getMap();

            $maxScore = $game->getMaxScore() === null ? "無し" : $game->getMaxScore()->getValue();

            $timeLimit = $game->getTimeLimit() === null ? "無し" : $game->getTimeLimit() . "秒";

            $participantsCount = count(TeamGameSystem::getGamePlayersData($gameId));
            $participants = $game->getMaxPlayersCount() === null ? $participantsCount : "{$participantsCount}/{$game->getMaxPlayersCount()}";

            $buttons[] = new SimpleFormButton(
                "ゲームモード:" . TextFormat::BOLD . strval($game->getType()) . TextFormat::RESET .
                ",マップ:" . TextFormat::BOLD . $map->getName() . TextFormat::RESET .
                "\n勝利判定:" . TextFormat::BOLD . $maxScore . TextFormat::RESET .
                ",時間制限:" . TextFormat::BOLD . $timeLimit . TextFormat::RESET .
                ",参加人数:" . TextFormat::BOLD . $participants . TextFormat::RESET,
                null,
                function (Player $player) use ($gameId) {
                    TeamGameSystem::joinGame($player, $gameId);
                }
            );
        }

        parent::__construct("チームデスマッチ一覧", "", $buttons);
    }

    function onClickCloseButton(Player $player): void { }
}

コマンドからフォームを呼び出す

plugin.ymlに以下を付け足します

plugin.yml
commands:
  create:
    usage: "/create"
    description: ""
  join:
    usage: "/join"
    description: ""

src/Main.phpを編集します

src/Main.php
<?php

namespace tdm;

use pocketmine\command\Command;
use pocketmine\command\CommandSender;
use pocketmine\event\Listener;
use pocketmine\Player;
use pocketmine\plugin\PluginBase;

class Main extends PluginBase implements Listener
{
    public function onCommand(CommandSender $sender, Command $command, string $label, array $args): bool {
        if ($sender instanceof Player) {
            switch ($label) {
                case "create":
                    $sender->sendForm(new CreateTeamDeathMatchForm($this->getScheduler()));
                    return true;
                case "join":
                    $sender->sendForm(new TeamDeathMatchListForm());
                    return true;
            }
        }

        return false;
    }
}

スコアボードの作成

src\tdm\TeamDeathMatchScoreboard.phpを作成します

src\tdm\TeamDeathMatchScoreboard.php
<?php


namespace tdm;


use pocketmine\Player;
use scoreboard_system\models\Score;
use scoreboard_system\models\Scoreboard;
use scoreboard_system\models\ScoreboardSlot;
use scoreboard_system\models\ScoreSortType;
use team_game_system\model\Game;

class TeamDeathMatchScoreboard extends Scoreboard
{
    private static function create(Game $game): Scoreboard {
        $slot = ScoreboardSlot::sideBar();
        $scores = [
            new Score($slot, "====TeamDeathMatch====", 0, 0),
            new Score($slot, "Map:" . $game->getMap()->getName(), 1, 1),
        ];

        $index = count($scores);
        foreach ($game->getTeams() as $team) {
            $scores[] = new Score($slot, $team->getTeamColorFormat() . $team->getName() . ":" . $team->getScore()->getValue(), $index, $index);
            $index++;
        }

        return parent::__create($slot, "Server Name", $scores, ScoreSortType::smallToLarge());
    }

    static function send(Player $player, Game $game) {
        $scoreboard = self::create($game);
        parent::__send($player, $scoreboard);
    }

    static function update(Player $player, Game $game) {
        $scoreboard = self::create($game);
        parent::__update($player, $scoreboard);
    }
}

ボスバーの下準備

src\tdm\BossBarTypeList.phpを作成します

src\tdm\BossBarTypeList.php
<?php


namespace tdm;


use bossbar_system\model\BossBarType;

class BossBarTypeList
{
    static function TeamDeathMatch(): BossBarType {
        return new BossBarType("TeamDeathMatch");
    }
}

ゲーム参加時にロビーにいた人に知らせる&途中参加の処理を書く

src\tdm\Main.phpを編集します

src\tdm\Main.php
    public function onJoinGame(PlayerJoinedGameEvent $event) {
        $player = $event->getPlayer();
        $gameId = $event->getGameId();
        $game = TeamGameSystem::getGame($event->getGameId());

        //チームデスマッチ以外のゲームに関するものだったら処理を行わない
        if (!$game->getType()->equals(GameTypeList::TeamDeathMatch())) return;

        $level = Server::getInstance()->getLevelByName("lobby");
        foreach ($level->getPlayers() as $lobbyPlayer) {
            $lobbyPlayer->sendMessage($player->getName() . "がチームデスマッチに参加しました");
        }

        //途中参加
        if ($game->isStarted()) {
            $playerData = TeamGameSystem::getPlayerData($player);
            //スポーン地点をセット
            TeamGameSystem::setSpawnPoint($player);

            //テレポート
            $player->teleport($player->getSpawn());

            //通知
            $player->sendTitle("チームデスマッチ スタート");
            $team = TeamGameSystem::getTeam($gameId, $playerData->getTeamId());
            $player->sendMessage("あなたは" . $team->getTeamColorFormat() . $team->getName() . TextFormat::RESET . "です");

            //Scoreboardのセット
            TeamDeathMatchScoreBoard::send($player, $game);
            //ボスバーをセット
            $bossBar = new BossBar($player, BossBarTypeList::TeamDeathMatch(), "TeamDeathMatch", 0);
            $bossBar->send();

            //アイテムをセット
            $player->getInventory()->setContents([
                ItemFactory::get(ItemIds::WOODEN_SWORD, 0, 1),
                ItemFactory::get(ItemIds::APPLE, 0, 10),
            ]);
        }
    }

試合開始時の処理を書く

src/Main.phpを編集します

src/Main.php
    public function onStartedGame(StartedGameEvent $event) {
        $gameId = $event->getGameId();
        $game = TeamGameSystem::getGame($gameId);
        //チームデスマッチ以外のゲームに関するものだったら処理を行わない
        if (!$game->getType()->equals(GameTypeList::TeamDeathMatch())) return;

        $playersData = TeamGameSystem::getGamePlayersData($gameId);

        foreach ($playersData as $playerData) {
            $player = $this->getServer()->getPlayer($playerData->getName());

            //スポーン地点をセット
            TeamGameSystem::setSpawnPoint($player);

            //テレポート
            $player->teleport($player->getSpawn());

            //通知
            $player->sendTitle("チームデスマッチ スタート");
            $team = TeamGameSystem::getTeam($gameId, $playerData->getTeamId());
            $player->sendMessage("あなたは" . $team->getTeamColorFormat() . $team->getName() . TextFormat::RESET . "です");

            //Scoreboardのセット
            TeamDeathMatchScoreBoard::send($player, $game);
            //ボスバーをセット
            $bossBar = new BossBar($player, BossBarTypeList::TeamDeathMatch(), "TeamDeathMatch", 0);
            $bossBar->send();

            //アイテムをセット
            $player->getInventory()->setContents([
                ItemFactory::get(ItemIds::WOODEN_SWORD, 0, 1),
                ItemFactory::get(ItemIds::APPLE, 0, 10),
            ]);
        }
    }

相手を倒したときにスコアが入るように

src\tdm\Main.phpを編集します

src\tdm\Main.php
    public function onPlayerKilledPlayer(PlayerKilledPlayerEvent $event): void {
        $attacker = $event->getAttacker();
        $attackerData = TeamGameSystem::getPlayerData($attacker);

        //チームデスマッチ以外のゲームに関するものだったら処理を行わない
        $game = TeamGameSystem::getGame($attackerData->getGameId());
        if (!$game->getType()->equals(GameTypeList::TeamDeathMatch())) return;

        TeamGameSystem::addScore($attackerData->getGameId(), $attackerData->getTeamId(), new Score(1));
    }

スコア追加時にスコアボードを更新するように

src\tdm\Main.phpを編集します

src\tdm\Main.php
    public function onAddedScore(AddedScoreEvent $event): void {
        $gameId = $event->getGameId();
        $game = TeamGameSystem::getGame($gameId);

        //チームデスマッチ以外のゲームに関するものだったら処理を行わない
        if (!$game->getType()->equals(GameTypeList::TeamDeathMatch())) return;

        $playersData = TeamGameSystem::getGamePlayersData($gameId);

        foreach ($playersData as $playerData) {
            $player = $this->getServer()->getPlayer($playerData->getName());
            TeamDeathMatchScoreBoard::update($player, $game);
        }
    }

リスポーン時にアイテムをセットする

src\tdm\Main.phpを編集します

src\tdm\Main.php
    public function onRespawn(PlayerRespawnEvent $event) {
        $player = $event->getPlayer();
        $playerData = TeamGameSystem::getPlayerData($player);

        if ($playerData->getGameId() === null) return;

        //チームデスマッチ以外のゲームに関するものだったら処理を行わない
        $game = TeamGameSystem::getGame($playerData->getGameId());
        if (!$game->getType()->equals(GameTypeList::TeamDeathMatch())) return;

        $player->getInventory()->setContents([
            ItemFactory::get(ItemIds::WOODEN_SWORD, 0, 1),
            ItemFactory::get(ItemIds::APPLE, 0, 10),
        ]);
    }

死亡時のアイテムドロップを消す

src\tdm\Main.phpを編集します

src\tdm\Main.php
    public function onPlayerDeath(PlayerDeathEvent $event) {
        $player = $event->getPlayer();
        $playerData = TeamGameSystem::getPlayerData($player);

        if ($playerData->getGameId() === null) return;

        //チームデスマッチ以外のゲームに関するものだったら処理を行わない
        $game = TeamGameSystem::getGame($playerData->getGameId());
        if (!$game->getType()->equals(GameTypeList::TeamDeathMatch())) return;

        $event->setDrops([]);
    }

試合時間経過時にボスバーを更新する

src\tdm\Main.phpを編集します

src\tdm\Main.php
    public function onUpdatedGameTimer(UpdatedGameTimerEvent $event): void {
        $gameId = $event->getGameId();
        $game = TeamGameSystem::getGame($gameId);
        //チームデスマッチ以外のゲームに関するものだったら処理を行わない
        if (!$game->getType()->equals(GameTypeList::TeamDeathMatch())) return;

        $playersData = TeamGameSystem::getGamePlayersData($gameId);
        $timeLimit = $event->getTimeLimit();
        $elapsedTime = $event->getElapsedTime();

        foreach ($playersData as $playerData) {
            $player = Server::getInstance()->getPlayer($playerData->getName());
            $bossBar = BossBar::findByType($player, BossBarTypeList::TeamDeathMatch());

            //制限時間がなかったら
            if ($timeLimit === null) {
                $bossBar->updateTitle("経過時間:" . $elapsedTime);
                continue;
            }

            $bossBar->updatePercentage($elapsedTime / $timeLimit);
            $bossBar->updateTitle($elapsedTime . "/" . $timeLimit);
        }
    }

試合終了後に参加者をロビーに送る

src\tdm\Main.phpを編集します

src\tdm\Main.php
    public function onFinishedGame(FinishedGameEvent $event): void {
        $game = $event->getGame();
        if (!$game->getType()->equals(GameTypeList::TeamDeathMatch())) return;

        $wonTeam = $event->getWonTeam();
        if ($wonTeam === null) {
            $message = "引き分け";
        } else {
            $message = $wonTeam->getTeamColorFormat() . $wonTeam->getName() . TextFormat::RESET . "の勝利!";
        }


        $playersData = $event->getPlayersData();

        //lobbyに送り返す
        $server = Server::getInstance();
        $level = $server->getLevelByName("lobby");
        foreach ($playersData as $playerData) {
            $player = $server->getPlayer($playerData->getName());
            //スコアボードを消す
            TeamDeathMatchScoreboard::delete($player);
            //ボスバーを消す
            $bossBar = BossBar::findByType($player, BossBarTypeList::TeamDeathMatch());
            $bossBar->remove();

            $player->getInventory()->setContents([]);

            //テレポート
            $player->teleport($level->getSpawnLocation());

            //メッセージ送信
            $player->sendMessage($message);
        }
    }

以上でプラグインは完成です

次はマップの設定を行っていきます

Mapの設定

1,まず登録したいマップにテレポートします

2,/mapコマンドを打ちます

3,Createを選択

4,マップ名を入力し送信

5,スポーン地点グループを2つ追加(0と1ができる)

6,0を選択しいくつかスポーン地点を登録 1も同様に行う

7,終わり

src\tdm\CreateTeamDeathMatchForm.phpの54行目のmapnameを登録したマップ名に変更

src\tdm\CreateTeamDeathMatchForm.php
$map = TeamGameSystem::selectMap("city", $teams);

以上

お疲れさまでした

ばいばい

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

LaravelのEloquentModelクラスを自動生成する

始まったばかりのLaravelのプロジェクトでは、移行元のある案件なので既存のデータベースをそのまま使います。
というときに、既存のテーブルから Laravel の Eloquent Model クラスを自動生成したときの作業記録です

こちらのライブラリを追加いました(読み方・呼び方不明です)
https://github.com/reliese/laravel

composer でライブラリを追加

composer require --dev reliese/laravel

このとき、 killed と表示されたらメモリ不足を疑ってみてください。
Dockerで開発環境を作っているならばこちらに書いた方法で対処できました。

Docker内でcomposer require したらkilledになったときの対応
https://qiita.com/makies/items/3e1064f17a18df750cad

パッケージを追加

AppServiceProvider に追記します

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Reliese\Coders\CodersServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        // 追記ここから
        if ($this->app->environment() === 'local') {
            $this->app->register(CodersServiceProvider::class);
        }
        // ここまで
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }
}

設定ファイルを作成

設定ファイルが config/models.php に生成されます。
configは一旦削除しておいたほうが良さそうです

php artisan vendor:publish --tag=reliese-models
php artisan config:clear

モデルファイル生成

php artisan code:models

DBにあるテーブルに対するModelファイルが生成されます。
デフォルトで app/Models 以下に生成されます。

config/models.php で、出力先や親クラスなどを指定することができます。

生成されない場合

生成されない場合 DBのテーブルが存在しているかを確認しましょう
DBデータの永続化していない場合、DBコンテナを再起動するとデータベースが消えてしまいます。
migrationを流してから 再実行してください。

生成されるコード

  • クラスプロパティ(=テーブルのカラム)
    • @property でクラスのdocコメントに記述
  • $table テーブル名
  • $primaryKey プライマリキー
  • $incrementing プライマリキーをインクリメントするかどうか
  • timestamps タイムスタンプを自動更新するカラム
  • $casts 型を自動キャストするカラム
  • $dates Carbonに自動変換するカラム
  • $fillable 書き込み可能なカラム
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Docker内でcomposer require したらkilledになったときの対応

Mac上で Docker (docker-compose) で開発環境を作り、開発中の Laravel のアプリケーションに composer require をしたら、中断されてしまいました

composer require ****/*****
(中略)
killed

composer が途中で終わってしまうといえば、メモリ不足が定石かなと思います。
会社の先輩からこのリンクを送ってもらい、疑惑が確信に変わりました。

[PHP] composer install が killed で失敗するときの原因と対処
https://webbibouroku.com/Blog/Article/composer-killed

PHP memory_limit の引き上げ

メモリ不足とわかったところで、まずDocker内で使っている php.ini の memory_limit を増やしました。
どのくらい必要なのかわからなかったけど、とりあえず2GB(2048MB)にしました。

memory_limit = 2048M

設定変更後、 docker build して再起動

Docker for Mac のメモリ割り当てを増やす

image.png

Docker for Mac で指定しているDockerが利用できるリソースが、当初 2GBとなっていました。使っている MacBook Pro には 64GBものメモリを積んでいるので、ドドーンと 8GB まで増やしました。ここはマシンスペックとの相談かなと思います。

変更後、Dockerホストの再起動がかかりました。

解決!!

上記2点でメモリの設定を変更して、 composer require が無事実行できました。
composer update でも同様に大量のメモリが必要なので、同じような対応が必要になるかもしれません。

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

PHP掲示板に検索機能の実装

PHPで作成した掲示板に、検索機能を実装したので振り返りつつ。

参考にした記事はこちら。
https://www.sejuku.net/blog/104455

具体的な機能は、
名前を検索すると、それに紐づいたIDと発言内容を表示する
という形。

トップページに検索内容の入力フォーム→別ファイル検索機能(search.php)の作成
で行った。

検索内容の入力フォームの作成

TOPページにこんな感じで作成した。
実際に機能が動くのはsearch.php。
ここでは入力フォームで文字を受け取り、"search_name"という名前で受け取る。

index.php
<h2>検索</h2>
<form action="search.php" method="post">
    <!-- 任意の<input>要素入力欄などを用意する -->
    <input type="text" name="search_name">
    <!-- 送信ボタンを用意する -->
    <input type="submit" name="submit" value="名前を検索する">
</form>

検索機能の作成

検索機能に関して、DB接続の前半部と、検索されてきた文字を表示する後半部に分けて説明。

DBから任意のデータを検索する前半部。

search.php
<?php
try{
    //DBに接続
    $dsn = 'mysql:dbname=test_bbs2; host=localhost';
    $username= 'root';
    $password= 'root';
    $pdo = new PDO($dsn, $username, $password);

    //SQL文を実行して、結果を$stmtに代入する。
    $stmt = $pdo->prepare(" SELECT * FROM contacts WHERE  submit_name LIKE '%" . $_POST["search_name"] . "%' "); 

    //実行する
    $stmt->execute();
    echo "OK";
    echo "<br>";
} catch(PDOException $e){
    echo "失敗:" . $e->getMessage() . "\n";
    exit();
}
?>

DB接続に関しては割愛。
大事なのは

search.php
    $stmt = $pdo->prepare(" SELECT * FROM contacts WHERE  submit_name LIKE '%" . $_POST["search_name"] . "%' "); 

ここの部分。

" SELECT * FROM contacts WHERE  submit_name LIKE '%" . $_POST["search_name"] . "%' "

ここのSQL文で
・contactsテーブルから(FROM contacts)
・submit_nameの条件で(WHERE submit_name)
・$_POST["search_name"]を含む値を取得する

$_POST["search_name"]の前後の%や、LIKEがないと、
田中という名前があった時に、「田」一文字では田中をヒットできない。
「田中」という全部一致しないと検索されてこない、という状況が起こる。

%(ワイルドカード)の使い方の詳細については
https://qiita.com/ika_katsuo/items/40e7784284344c7d8697
こちらの記事を参考に。

LIKEによる検索はあいまい検索という。

DBから取得したデータを画面に表示する後半部。

DBから取り出したデータを、HTMLとPHPで記述して画面に表示する。
コードはこんな感じになっている。

search.php
<html>
    <body>
        <table>
            <tr><th>ID</th><th>Name</th><th>remark</th></tr>
            <!-- ここでPHPのforeachを使って結果をループさせる -->
            <?php foreach ($stmt as $row): ?>
            <tr>
                <td><?php echo $row[0]?></td>
                <td><?php echo $row[1]?></td>
                <td><?php echo $row[2]?></td>
            </tr>

                <?php endforeach; ?>
        </table>
    </body>
</html>

ここではhtmlで記述し、foreachでstmtのオブジェクトから値を取り出しています。

・$row[0]でIDを、

・$row[1]で名前を、

・$row[2]で投稿内容を

それぞれ表示します。

あとはテーブルの形になるようにtableタグが使われています。

以上です。

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

Laravel6 ユーザと管理者の認証を分けて作る

目的

  • ユーザと管理者とで認証を分ける方法をまとめる。

実施環境

  • ハードウェア環境
項目 情報
OS macOS Catalina(10.15.5)
PC MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports)
プロセッサ 2 GHz クアッドコアIntel Core i5
メモリ 32 GB 3733 MHz LPDDR4
グラフィックス Intel Iris Plus Graphics 1536 MB
  • ソフトウェア環境
項目 情報 備考
PHP バージョン 7.4.3 Homwbrewを用いて導入
Laravel バージョン 6.18.35 commposerを用いてこちらの方法で導入→Mac Laravelの環境構築を行う
MySQLバージョン 8.0.19 for osx10.13 on x86_64 Homwbrewを用いてこちらの方法で導入→Mac HomebrewでMySQLをインストールする

前提条件

  • 先の実施環境に近い環境が整っていること。
  • Laravelは最新バージョン用に構築しているがアプリ作成コマンド実行時に5.6を指定して作成する。

前提情報

  • DockerやAWSなどは使用せずにMacのローカル環境に直接Laravelアプリを作成する。
  • 若干難易度が高い作業であるためなるべくわかりやすい様に丁寧に記載する。
  • 本記事の内容を最初から実施していけば誰でもユーザと管理者別々での認証を行えることを目指す。
  • 管理者認証情報は管理者名、パスワードとする。
  • 作業複雑化を避けるため管理者用のパスワードはアプリ側から変更できる様にしない。
  • 既存のAuth認証コントローラを追記編集して管理者の認証機能を作成する。
  • 下記のドキュメントを参考に必要名部分のみを抜粋して実装する。また、説明をより分かりやすくするために若干実装の順番を変更する。
  • 下記記事の内容を応用して実施するが、分かりやすさを踏まえてアプリ作成部分から本記事に記載する。

読後感

  • Laravel6のでユーザ認証と管理者認証機能のついたアプリを作成することができ、管理者認証情報は管理者名とパスワードとする。

概要

  1. データベースの作成
  2. Laravelアプリの作成と初期設定
  3. ユーザ認証機能作成
  4. 管理者情報用テーブルの準備
  5. ガードとプロバイダの追加
  6. コントローラの修正
  7. 認証用ページの作成
  8. 認証後遷移ページの作成
  9. ルーティングの記載と認証後遷移ページの設定と例外時の処理の記載
  10. 確認

詳細

  1. データベースの作成

    1. 下記コマンドを実行してMySQLにターミナルからログインする。(MySQLのrootユーザのパスワードを忘れてしまった方はこちら→Mac ローカル環境の MySQL 8.x のrootパスワードを忘れた時のリセット方法)

      $ mysql -u root -p
      
    2. 下記SQLを実行して「multi_auth」データベースを作成する。

      create database multi_auth_info_limited_laravel_6;
      
    3. 下記SQLを実行してデータベース一覧を出力して「multi_auth_info_limited_laravel_6」が含まれていることを確認する。確認後、MySQLをログアウトする。

      show databases;
      
  2. Laravelアプリの作成と初期設定

    1. Laravelアプリを作成する任意のディレクトリに移動する。
    2. 下記コマンドを実行して「multi_auth_info_limited_laravel_6」というLaravel5.6のアプリを作成する。(完了まで時間がかかる可能性があるので少し待機する。)

      $ composer create-project laravel/laravel multi_auth_info_limited_laravel_6 "5.6.*"
      
    3. 下記コマンドを実行して作成されたアプリ名ディレクトリに移動する。以後のコマンドは特に記載がない場合、このmulti_authディレクトリ内部で実行する物とする。

      $ cd multi_auth_info_limited_laravel_6
      
    4. 下記コマンドを実行して.envファイルを開く。

      $ vi .env
      
    5. 下記の様に.envファイルのデータベースの記述を修正する。

      multi_auth_info_limited_laravel_6/.env
      DB_DATABASE=multi_auth_info_limited_laravel_6
      DB_USERNAME=root
      DB_PASSWORD=mysql -u root -pコマンドを実行した際に入力したパスワード
      
    6. 修正後の.envファイルの全体の内容を記載する。

      multi_auth_info_limited_laravel_6/.env
      APP_NAME=Laravel
      APP_ENV=local
      APP_KEY=アプリキーの記載は各個人で異なります。
      APP_DEBUG=true
      APP_URL=http://localhost
      
      LOG_CHANNEL=stack
      
      DB_CONNECTION=mysql
      DB_HOST=127.0.0.1
      DB_PORT=3306
      DB_DATABASE=multi_auth_info_limited_laravel_6
      DB_USERNAME=root
      DB_PASSWORD=皆さんの環境のMySQLのrootユーザのパスワード
      
      BROADCAST_DRIVER=log
      CACHE_DRIVER=file
      QUEUE_CONNECTION=sync
      SESSION_DRIVER=file
      SESSION_LIFETIME=120
      
      REDIS_HOST=127.0.0.1
      REDIS_PASSWORD=null
      REDIS_PORT=6379
      
      MAIL_MAILER=smtp
      MAIL_HOST=smtp.mailtrap.io
      MAIL_PORT=2525
      MAIL_USERNAME=null
      MAIL_PASSWORD=null
      MAIL_ENCRYPTION=null
      MAIL_FROM_ADDRESS=null
      MAIL_FROM_NAME="${APP_NAME}"
      
      AWS_ACCESS_KEY_ID=
      AWS_SECRET_ACCESS_KEY=
      AWS_DEFAULT_REGION=us-east-1
      AWS_BUCKET=
      
      PUSHER_APP_ID=
      PUSHER_APP_KEY=
      PUSHER_APP_SECRET=
      PUSHER_APP_CLUSTER=mt1
      
      MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
      MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
      
    7. 下記コマンドを実行して初期マイグレーションファイルをマイグレートする。

      $ php artisan migrate
      
    8. 下記コマンドを実行してローカルサーバを起動する。

      $ php artisan serve
      
    9. 下記にアクセスしてLaravelの初期画面が表示されることを確認する。

  3. ユーザ認証機能作成

    1. 下記コマンドを実行してユーザ認証機能を作成する。

      $ composer require laravel/ui "^1.0" --dev
      $ php artisan ui vue --auth
      $ npm install && npm run dev
      
    2. 下記コマンドを実行してローカルサーバを起動する。

      $ php artisan serve
      
    3. 下記にアクセスしてLaravelの初期画面を開く。

    4. 右上の「REGISTER」をクリックする。

      Laravel.png

    5. 各種情報を入力して「Register」をクリックする。

      Laravel-2.png

    6. 「Register」をクリック後角の画面に遷移することを確認する。

      Laravel-3.pngLaravel.png

  4. 管理者情報用テーブルの準備

    1. 下記コマンドを実行してAdminモデルファイルとadminsテーブル作成用マイグレーションファイルを作成する。

      $ php artisan make:model Admin -m
      
    2. 下記コマンドを実行して先に作成したマイグレーションファイルを開く。(YYYY_MM_DD_XXXXXXの部分はマイグレーションファイル作成日により異なる。)

      $ vi database/migrations/YYYY_MM_DD_XXXXXX_create_admins_table.php
      
    3. 開いたマイグレーションファイルを下記の様に追記する。

      multi_auth_info_limited_laravel_6/database/migrations/YYYY_MM_DD_XXXXXX_create_admins_table.php
      <?php
      
      use Illuminate\Support\Facades\Schema;
      use Illuminate\Database\Schema\Blueprint;
      use Illuminate\Database\Migrations\Migration;
      
      class CreateAdminsTable extends Migration
      {
          /**
           * Run the migrations.
           *
           * @return void
           */
          public function up()
          {
              Schema::create('admins', function (Blueprint $table) {
                  $table->increments('id');
                  //下記から追加
                  $table->string('name');
                  $table->string('password');
                  $table->boolean('is_super')->default(false);
                  $table->rememberToken();
                  //上記までを追加
                  $table->timestamps();
              });
          }
      
          /**
           * Reverse the migrations.
           *
           * @return void
           */
          public function down()
          {
              Schema::dropIfExists('admins');
          }
      }
      
      
    4. 下記コマンドを実行して今記載したマイグレーションファイルをマイグレートする。

      $ php artisan migrate
      
    5. 下記コマンドを実行して先に作成したモデルファイルを開く。

      $ vi app/Admin.php
      
    6. 開いたモデルファイルの内容を全て削除し、下記の内容をコピーアンドペーストで記載する

      multi_auth_info_limited_laravel_6/app/Admin.php
      <?php
      
      namespace App;
      
      use Illuminate\Notifications\Notifiable;
      use Illuminate\Foundation\Auth\User as Authenticatable;
      
      class Admin extends Authenticatable
      {
          use Notifiable;
      
          protected $guard = 'admin';
      
          protected $fillable = [
              'name', 'password',
          ];
      
          protected $hidden = [
              'password', 'remember_token',
          ];
      }
      
  5. ガードとプロバイダの追加

    1. 下記コマンドを実行しガードとプロバイダを定義しているファイルを開く。

      $ vi  config/auth.php
      
    2. ガードの記載を下記の様に追記する。

      multi_auth_info_limited_laravel_6/config/auth.php
      /*
      |--------------------------------------------------------------------------
      | Authentication Guards
      |--------------------------------------------------------------------------
      |
      | Next, you may define every authentication guard for your application.
      | Of course, a great default configuration has been defined for you
      | here which uses session storage and the Eloquent user provider.
      |
      | All authentication drivers have a user provider. This defines how the
      | users are actually retrieved out of your database or other storage
      | mechanisms used by this application to persist your user's data.
      |
      | Supported: "session", "token"
      |
      */
      
      'guards' => [
          'web' => [
              'driver' => 'session',
              'provider' => 'users',
          ],
      
          'api' => [
              'driver' => 'token',
              'provider' => 'users',
          ],
          //下記を追記
          'admin' => [
              'driver' => 'session',
              'provider' => 'admins',
          ],
          //上記までを追記
      ],
      
    3. 同ファイル内のプロバイダの記載も追記を行う。

      multi_auth_info_limited_laravel_6/config/auth.php
      /*
      |--------------------------------------------------------------------------
      | User Providers
      |--------------------------------------------------------------------------
      |
      | All authentication drivers have a user provider. This defines how the
      | users are actually retrieved out of your database or other storage
      | mechanisms used by this application to persist your user's data.
      |
      | If you have multiple user tables or models you may configure multiple
      | sources which represent each model / table. These sources may then
      | be assigned to any extra authentication guards you have defined.
      |
      | Supported: "database", "eloquent"
      |
      */
      
      'providers' => [
          'users' => [
              'driver' => 'eloquent',
              'model' => App\User::class,
          ],
          //下記を追記する
          'admins' => [
              'driver' => 'eloquent',
              'model' => App\Admin::class,
          ],
          //上記までを追記する
      
          // 'users' => [
          //     'driver' => 'database',
          //     'table' => 'users',
          // ],
      ],
      
    4. 追記後のmulti_auth_info_limited_laravel_6/config/auth.phpのファイルの全体を下記に記載する。

      multi_auth_info_limited_laravel_6/config/auth.php
      <?php
      
      return [
      
          /*
          |--------------------------------------------------------------------------
          | Authentication Defaults
          |--------------------------------------------------------------------------
          |
          | This option controls the default authentication "guard" and password
          | reset options for your application. You may change these defaults
          | as required, but they're a perfect start for most applications.
          |
          */
      
          'defaults' => [
              'guard' => 'web',
              'passwords' => 'users',
          ],
      
          /*
          |--------------------------------------------------------------------------
          | Authentication Guards
          |--------------------------------------------------------------------------
          |
          | Next, you may define every authentication guard for your application.
          | Of course, a great default configuration has been defined for you
          | here which uses session storage and the Eloquent user provider.
          |
          | All authentication drivers have a user provider. This defines how the
          | users are actually retrieved out of your database or other storage
          | mechanisms used by this application to persist your user's data.
          |
          | Supported: "session", "token"
          |
          */
      
          'guards' => [
              'web' => [
                  'driver' => 'session',
                  'provider' => 'users',
              ],
      
              'api' => [
                  'driver' => 'token',
                  'provider' => 'users',
              ],
              //下記を追記する
              'admin' => [
                  'driver' => 'session',
                  'provider' => 'admins',
              ],
              //上記までを追記する
          ],
      
          /*
          |--------------------------------------------------------------------------
          | User Providers
          |--------------------------------------------------------------------------
          |
          | All authentication drivers have a user provider. This defines how the
          | users are actually retrieved out of your database or other storage
          | mechanisms used by this application to persist your user's data.
          |
          | If you have multiple user tables or models you may configure multiple
          | sources which represent each model / table. These sources may then
          | be assigned to any extra authentication guards you have defined.
          |
          | Supported: "database", "eloquent"
          |
          */
      
          'providers' => [
              'users' => [
                  'driver' => 'eloquent',
                  'model' => App\User::class,
              ],
              //下記を追記する
              'admins' => [
                  'driver' => 'eloquent',
                  'model' => App\Admin::class,
              ],
              //上記までを追記する
              // 'users' => [
              //     'driver' => 'database',
              //     'table' => 'users',
              // ],
          ],
      
          /*
          |--------------------------------------------------------------------------
          | Resetting Passwords
          |--------------------------------------------------------------------------
          |
          | You may specify multiple password reset configurations if you have more
          | than one user table or model in the application and you want to have
          | separate password reset settings based on the specific user types.
          |
          | The expire time is the number of minutes that the reset token should be
          | considered valid. This security feature keeps tokens short-lived so
          | they have less time to be guessed. You may change this as needed.
          |
          */
      
          'passwords' => [
              'users' => [
                  'provider' => 'users',
                  'table' => 'password_resets',
                  'expire' => 60,
              ],
          ],
      
      ];
      
  6. コントローラの修正

    1. 下記の二つのコントローラを修正する。
      • multi_auth_info_limited_laravel_6/app/Http/Controllers/Auth/LoginController.php
      • multi_auth_info_limited_laravel_6/app/Http/Controllers/Auth/RegisterController.php
    2. 下記コマンドを実行してログインを司るコントローラファイルを開く。

      $ vi app/Http/Controllers/Auth/LoginController.php
      
    3. 開いたコントローラファイルを下記の様に修正する。

      multi_auth_info_limited_laravel_6/app/Http/Controllers/Auth/LoginController.php
      <?php
      
      namespace App\Http\Controllers\Auth;
      
      use App\Http\Controllers\Controller;
      use Illuminate\Foundation\Auth\AuthenticatesUsers;
      
      // 下記を追記する
      use Illuminate\Http\Request;
      use 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 = '/home';
      
          /**
           * Create a new controller instance.
           *
           * @return void
           */
          public function __construct()
          {
              $this->middleware('guest')->except('logout');
              //下記を追記する
              $this->middleware('guest:admin')->except('logout');
          }
      
          // 下記を追記する
          public function showAdminLoginForm()
          {
              return view('auth.login', ['url' => 'admin']);
          }
      
          public function adminLogin(Request $request)
          {
              $this->validate($request, [
                  'name'   => 'required',
                  'password' => 'required|min:6'
              ]);
      
              if (Auth::guard('admin')->attempt(['name' => $request->name, 'password' => $request->password], $request->get('remember'))) {
      
                  return redirect()->intended('/admin');
              }
              return back()->withInput($request->only('name', 'remember'));
          }
          // 上記までを追記
      }
      
    4. 下記コマンドを実行してログインを司るコントローラファイルを開く。

      $ vi app/Http/Controllers/Auth/RegisterController.php
      
    5. 開いたコントローラファイルを下記の様に修正する。

      multi_auth_info_limited_laravel_6/app/Http/Controllers/Auth/RegisterController.php
      <?php
      
      namespace App\Http\Controllers\Auth;
      
      //下記を追記する
      use App\Admin;
      use Illuminate\Http\Request;
      //上記までを追記する
      use App\User;
      use App\Http\Controllers\Controller;
      use Illuminate\Support\Facades\Hash;
      use Illuminate\Support\Facades\Validator;
      use Illuminate\Foundation\Auth\RegistersUsers;
      
      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;
      
          /**
           * Where to redirect users after registration.
           *
           * @var string
           */
          protected $redirectTo = '/home';
      
          /**
           * Create a new controller instance.
           *
           * @return void
           */
          public function __construct()
          {
              $this->middleware('guest');
              //下記を追記する
              $this->middleware('guest:admin');
          }
      
          /**
           * Get a validator for an incoming registration request.
           *
           * @param  array  $data
           * @return \Illuminate\Contracts\Validation\Validator
           */
          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:6|confirmed',
              ]);
          }
      
          /**
           * Create a new user instance after a valid registration.
           *
           * @param  array  $data
           * @return \App\User
           */
          protected function create(array $data)
          {
              return User::create([
                  'name' => $data['name'],
                  'email' => $data['email'],
                  'password' => Hash::make($data['password']),
              ]);
          }
          //下記を追記する
          protected function validatorAdmin(array $data)
          {
              return Validator::make($data, [
                  'name' => 'required|string|max:255',
                  'password' => 'required|string|min:6|confirmed',
              ]);
          }
      
          public function showAdminRegisterForm()
          {
              return view('auth.register', ['url' => 'admin']);
          }
      
          protected function createAdmin(Request $request)
          {
              $this->validatorAdmin($request->all())->validate();
              $admin = Admin::create([
                  'name' => $request['name'],
                  'password' => Hash::make($request['password']),
              ]);
              return redirect()->intended('login/admin');
          }
          //上記までを追記する
      }
      
  7. 管理者ログインページのビューファイルの修正

    1. 下記コマンドを実行してログインページのビューファイルを開く。

      $ vi resources/views/auth/login.blade.php
      
    2. 開いたビューファイルを下記の様に修正する。(修正内容が複雑で分かりにくい時は下記をまるまるコピーして貼り付けてもOKである)

      multi_auth_info_limited_laravel_6/resources/views/auth/login.blade.php
      @extends('layouts.app')
      
      @section('content')
      <div class="container">
          <div class="row justify-content-center">
              <div class="col-md-8">
                  <div class="card">
                      <!-- 下記を修正する -->
                      <div class="card-header"> {{ isset($url) ? ucwords($url) : ""}} {{ __('Login') }}</div>
      
                      <div class="card-body">
                          @isset($url)
                          <form method="POST" action='{{ url("login/$url") }}' aria-label="{{ __('Login') }}">
                              @csrf
      
                              <div class="form-group row">
                                  <label for="name" class="col-sm-4 col-form-label text-md-right">{{ __('Name') }}</label>
      
                                  <div class="col-md-6">
                                      <input id="name" type="name" class="form-control{{ $errors->has('name') ? ' is-invalid' : '' }}" name="name" value="{{ old('name') }}" required autofocus>
      
                                      @if ($errors->has('name'))
                                          <span class="invalid-feedback" role="alert">
                                              <strong>{{ $errors->first('name') }}</strong>
                                          </span>
                                      @endif
                                  </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{{ $errors->has('password') ? ' is-invalid' : '' }}" name="password" required>
      
                                      @if ($errors->has('password'))
                                          <span class="invalid-feedback" role="alert">
                                              <strong>{{ $errors->first('password') }}</strong>
                                          </span>
                                      @endif
                                  </div>
                              </div>
                          @else
                          <form method="POST" action="{{ route('login') }}" aria-label="{{ __('Login') }}">
                              @csrf
      
                              <div class="form-group row">
                                  <label for="email" class="col-sm-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>
      
                                  <div class="col-md-6">
                                      <input id="email" type="email" class="form-control{{ $errors->has('email') ? ' is-invalid' : '' }}" name="email" value="{{ old('email') }}" required autofocus>
      
                                      @if ($errors->has('email'))
                                          <span class="invalid-feedback" role="alert">
                                              <strong>{{ $errors->first('email') }}</strong>
                                          </span>
                                      @endif
                                  </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{{ $errors->has('password') ? ' is-invalid' : '' }}" name="password" required>
      
                                      @if ($errors->has('password'))
                                          <span class="invalid-feedback" role="alert">
                                              <strong>{{ $errors->first('password') }}</strong>
                                          </span>
                                      @endif
                                  </div>
                              </div>
                          @endisset
                              <!-- 上記までを修正する -->
                              <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>
      
                                      <a class="btn btn-link" href="{{ route('password.request') }}">
                                          {{ __('Forgot Your Password?') }}
                                      </a>
                                  </div>
                              </div>
                          </form>
                      </div>
                  </div>
              </div>
          </div>
      </div>
      @endsection
      
  8. 管理者登録ページのビューファイルの修正

    1. 下記コマンドを実行してログインページのビューファイルを開く。

      $ vi resources/views/auth/register.blade.php
      
    2. 開いたビューファイルを下記の様に修正する。(修正内容が複雑で分かりにくい時は下記をまるまるコピーして貼り付けてもOKである)

      multi_auth_info_limited_laravel_6/resources/views/auth/register.blade.php
      @extends('layouts.app')
      
      @section('content')
      <div class="container">
          <div class="row justify-content-center">
              <div class="col-md-8">
                  <div class="card">
                      <!-- 下記を修正する -->
                      <div class="card-header"> {{ isset($url) ? ucwords($url) : ""}} {{ __('Register') }}</div>
      
                      <div class="card-body">
                          @isset($url)
                          <form method="POST" action='{{ url("register/$url") }}' aria-label="{{ __('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{{ $errors->has('name') ? ' is-invalid' : '' }}" name="name" value="{{ old('name') }}" required autofocus>
      
                                      @if ($errors->has('name'))
                                          <span class="invalid-feedback" role="alert">
                                              <strong>{{ $errors->first('name') }}</strong>
                                          </span>
                                      @endif
                                  </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{{ $errors->has('password') ? ' is-invalid' : '' }}" name="password" required>
      
                                      @if ($errors->has('password'))
                                          <span class="invalid-feedback" role="alert">
                                              <strong>{{ $errors->first('password') }}</strong>
                                          </span>
                                      @endif
                                  </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>
                                  </div>
                              </div>
                          @else
                          <form method="POST" action="{{ route('register') }}" aria-label="{{ __('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{{ $errors->has('name') ? ' is-invalid' : '' }}" name="name" value="{{ old('name') }}" required autofocus>
      
                                      @if ($errors->has('name'))
                                          <span class="invalid-feedback" role="alert">
                                              <strong>{{ $errors->first('name') }}</strong>
                                          </span>
                                      @endif
                                  </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{{ $errors->has('email') ? ' is-invalid' : '' }}" name="email" value="{{ old('email') }}" required>
      
                                      @if ($errors->has('email'))
                                          <span class="invalid-feedback" role="alert">
                                              <strong>{{ $errors->first('email') }}</strong>
                                          </span>
                                      @endif
                                  </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{{ $errors->has('password') ? ' is-invalid' : '' }}" name="password" required>
      
                                      @if ($errors->has('password'))
                                          <span class="invalid-feedback" role="alert">
                                              <strong>{{ $errors->first('password') }}</strong>
                                          </span>
                                      @endif
                                  </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>
                                  </div>
                              </div>
      
                          @endisset
                          <!-- 上記までを修正する -->        
                              <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
      
  9. 認証後遷移ページの作成

    1. 下記コマンドを実行してビューファイルを作成する。

      $ touch resources/views/layouts/auth.blade.php
      $ touch resources/views/admin.blade.php
      
    2. 下記コマンドを実行して先に作成したビューファイルを開く。

      $ vi resources/views/layouts/auth.blade.php
      
    3. 下記の内容をコピーアンドペーストで貼り付ける。

      multi_auth_info_limited_laravel_6/resources/views/layouts/auth.blade.php
      <!DOCTYPE html>
      <html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
      <head>
          <meta charset="utf-8">
          <meta http-equiv="X-UA-Compatible" content="IE=edge">
          <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="https://fonts.gstatic.com">
          <link href="https://fonts.googleapis.com/css?family=Raleway:300,400,600" rel="stylesheet" type="text/css">
      
          <!-- Styles -->
          <link href="{{ asset('css/app.css') }}" rel="stylesheet">
      </head>
      <body>
          <div id="app">
              <nav class="navbar navbar-expand-md navbar-light navbar-laravel">
                  <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 -->
                             <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>
                                      Hi There <span class="caret"></span>
                                  </a>
      
                                  <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
                                      <a class="dropdown-item" href="{{ route('logout') }}"
                                         onclick="event.preventDefault();
                                                       document.getElementById('logout-form').submit();">
                                          {{ __('Logout') }}
                                      </a>
      
                                      <form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
                                          @csrf
                                      </form>
                                  </div>
                              </li>
                          </ul>
                      </div>
                  </div>
              </nav>
      
              <main class="py-4">
                  @yield('content')
              </main>
          </div>
      </body>
      </html>
      
    4. 下記コマンドを実行して先に作成したビューファイルを開く。

      $ vi resources/views/admin.blade.php
      
    5. 下記の内容をコピーアンドペーストで貼り付ける。

      multi_auth_info_limited_laravel_6/resources/views/admin.blade.php
      @extends('layouts.auth')
      
      @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">
                          Hi boss!
                      </div>
                  </div>
              </div>
          </div>
      </div>
      @endsection
      
    6. 下記コマンドを実行して先に作成したビューファイルを開く。

      $ vi resources/views/home.blade.php
      
    7. すでに記載されている内容を削除し下記の内容をコピーアンドペーストで貼り付ける。

      multi_auth_info_limited_laravel_6/resources/views/home.blade.php
      @extends('layouts.auth')
      
      @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">
                           Hi there, regular user
                      </div>
                  </div>
              </div>
          </div>
      </div>
      @endsection
      
  10. ルーティングの記載と認証後遷移ページの設定と例外時の処理の記載

    1. 下記コマンドを実行してルーティングファイルを開く。

      $ vi routes/web.php
      
    2. 下記の様に追記する。

      multi_auth_info_limited_laravel_6/routes/web.php
      <?php
      
      /*
      |--------------------------------------------------------------------------
      | Web Routes
      |--------------------------------------------------------------------------
      |
      | Here is where you can register web routes for your application. These
      | routes are loaded by the RouteServiceProvider within a group which
      | contains the "web" middleware group. Now create something great!
      |
      */
      
      Route::get('/', function () {
          return view('welcome');
      });
      
      Auth::routes();
      
      Route::get('/home', 'HomeController@index')->name('home');
      
      //下記を追記する
      Route::get('/login/admin', 'Auth\LoginController@showAdminLoginForm');
      Route::get('/register/admin', 'Auth\RegisterController@showAdminRegisterForm');
      
      Route::post('/login/admin', 'Auth\LoginController@adminLogin');
      Route::post('/register/admin', 'Auth\RegisterController@createAdmin');
      
      Route::view('/home', 'home')->middleware('auth');
      Route::view('/admin', 'admin');
      //上記までを追記する
      
  11. リダイレクトの設定

    1. 下記コマンドを実行してリダイレクトを司るミドルウェアファイルを開く

      $ vi app/Http/Middleware/RedirectIfAuthenticated.php
      
    2. 下記のように追記を行う。

      multi_auth_info_limited_laravel_6/app/Http/Middleware/RedirectIfAuthenticated.php
      <?php
      
      namespace App\Http\Middleware;
      
      use Closure;
      use Illuminate\Support\Facades\Auth;
      
      class RedirectIfAuthenticated
      {
          public function handle($request, Closure $next, $guard = null)
          {
              // 下記を追記する
              if ($guard == "admin" && Auth::guard($guard)->check()) {
                  return redirect('/admin');
              }
              // 上記までを追記する
              if (Auth::guard($guard)->check()) {
                  return redirect('/home');
              }
      
              return $next($request);
          }
      }
      
  12. 例外時の設定

    1. 下記コマンドを実行してハンドラーファイルを開く。

      $ vi app/Exceptions/Handler.php
      
    2. 下記のようにハンドラーファイルを修正する。

      multi_auth_info_limited_laravel_6/app/Exceptions/Handler.php
      <?php
      
      namespace App\Exceptions;
      
      use Exception;
      use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
      
      //下記を追記する
      use Illuminate\Auth\AuthenticationException;
      use Auth; 
      //上記までを追記する
      
      class Handler extends ExceptionHandler
      {
          /**
           * A list of the exception types that are not reported.
           *
           * @var array
           */
          protected $dontReport = [
              //
          ];
      
          /**
           * A list of the inputs that are never flashed for validation exceptions.
           *
           * @var array
           */
          protected $dontFlash = [
              'password',
              'password_confirmation',
          ];
      
          /**
           * Report or log an exception.
           *
           * @param  \Exception  $exception
           * @return void
           */
          public function report(Exception $exception)
          {
              parent::report($exception);
          }
      
          /**
           * Render an exception into an HTTP response.
           *
           * @param  \Illuminate\Http\Request  $request
           * @param  \Exception  $exception
           * @return \Illuminate\Http\Response
           */
          public function render($request, Exception $exception)
          {
              return parent::render($request, $exception);
          }
          //下記を追記する
          protected function unauthenticated($request, AuthenticationException $exception)
          {
              if ($request->expectsJson()) {
                  return response()->json(['error' => 'Unauthenticated.'], 401);
              }
              if ($request->is('admin') || $request->is('admin/*')) {
                  return redirect()->guest('/login/admin');
              }
              return redirect()->guest(route('login'));
          }
          //上記までを追記する
      
      }
      
  13. 確認

    1. 下記コマンドを実行してローカルサーバを起動する。

      $ php artisan serve
      
    2. 下記にアクセスしてLaravelの初期画面が表示されることを確認する。

    3. 下記にアクセスし必要情報を入力後「Registar」をクリックする。

    4. 下記の画面にリダイレクトすることを確認する。一つ前の手順で入力した管理者登録時の情報を入力し「Login」をクリックする。

      Laravel-10.png

    5. 下記のページ「Hi boss!」が表示されれば管理者としてのログインは完了である。

      Laravel.png

参考文献

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